Skip to content

Instantly share code, notes, and snippets.

@dsarfati
Last active November 22, 2017 08:55
Show Gist options
  • Save dsarfati/f2fd46a4e1dfaf216ece739f0fb143d2 to your computer and use it in GitHub Desktop.
Save dsarfati/f2fd46a4e1dfaf216ece739f0fb143d2 to your computer and use it in GitHub Desktop.
Orleans Application Insights Logger
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.Extensions.Logging;
using Orleans.Runtime;
using Zapic.Service.Interfaces;
namespace Zapic.Service.Host.Logging
{
internal sealed class AppInsightsLogger : ILogger
{
private readonly TelemetryClient _client;
private readonly string _categoryName;
private readonly string _siloName;
private const string DateTimeFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK";
private static readonly string Version = typeof(ZapicLogger).Assembly.GetCustomAttribute<AssemblyFileVersionAttribute>().Version;
public AppInsightsLogger(TelemetryClient client, string categoryName, string siloName)
{
this._client = client;
this._categoryName = categoryName;
this._siloName = siloName;
}
public IDisposable BeginScope<TState>(TState state) => null;
/// <summary>
/// Filtering will occur in the Application Insights pipeline. This allows for the QuickPulse telemetry
/// to always be sent, even if logging actual records is completely disabled.
/// </summary>
/// <param name="logLevel"></param>
/// <returns></returns>
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var formattedMessage = formatter?.Invoke(state, exception);
var stateValues = state as IEnumerable<KeyValuePair<string, object>>;
// Log an exception
if (exception != null)
{
LogException(logLevel, stateValues, exception, formattedMessage);
return;
}
LogTrace(logLevel, stateValues, formattedMessage);
}
private static SeverityLevel? GetSeverityLevel(LogLevel logLevel)
{
switch (logLevel)
{
case LogLevel.Trace:
case LogLevel.Debug:
return SeverityLevel.Verbose;
case LogLevel.Information:
return SeverityLevel.Information;
case LogLevel.Warning:
return SeverityLevel.Warning;
case LogLevel.Error:
return SeverityLevel.Error;
case LogLevel.Critical:
return SeverityLevel.Critical;
case LogLevel.None:
default:
return null;
}
}
private void LogTrace(LogLevel logLevel, IEnumerable<KeyValuePair<string, object>> values, string formattedMessage)
{
var telemetry = new TraceTelemetry(formattedMessage)
{
SeverityLevel = GetSeverityLevel(logLevel),
Timestamp = DateTimeOffset.UtcNow
};
AddScopedProperties(telemetry);
ApplyProperties(telemetry, values, LogConstants.CustomPropertyPrefix);
AddScopedValues(telemetry);
_client.TrackTrace(telemetry);
}
// Inserts properties into the telemetry's properties. Properly formats dates, removes nulls, applies prefix, etc.
private static void ApplyProperties(ISupportProperties telemetry, IEnumerable<KeyValuePair<string, object>> values, string propertyPrefix = null)
{
foreach (var property in values)
{
string stringValue = null;
// drop null properties
if (property.Value == null)
{
continue;
}
// Format dates
var propertyType = property.Value.GetType();
if (propertyType == typeof(DateTime))
{
stringValue = ((DateTime)property.Value).ToUniversalTime().ToString(DateTimeFormatString);
}
else if (propertyType == typeof(DateTimeOffset))
{
stringValue = ((DateTimeOffset)property.Value).UtcDateTime.ToString(DateTimeFormatString);
}
else
{
stringValue = property.Value.ToString();
}
telemetry.Properties.Add($"{propertyPrefix}{property.Key}", stringValue);
}
}
private void LogException(LogLevel logLevel, IEnumerable<KeyValuePair<string, object>> values, Exception exception, string formattedMessage)
{
var telemetry = new ExceptionTelemetry(exception)
{
SeverityLevel = GetSeverityLevel(logLevel),
Timestamp = DateTimeOffset.UtcNow,
};
if (!string.IsNullOrEmpty(formattedMessage))
{
telemetry.Properties[LogConstants.FormattedMessageKey] = formattedMessage;
}
AddScopedProperties(telemetry);
ApplyProperties(telemetry, values, LogConstants.CustomPropertyPrefix);
AddScopedValues(telemetry);
_client.TrackException(telemetry);
}
private void AddScopedProperties(ISupportProperties telemetry)
{
telemetry.Properties[LogConstants.CategoryNameKey] = _categoryName;
telemetry.Properties[LogConstants.SiloNameKey] = _siloName;
}
private static void AddScopedValues(ITelemetry telemetry)
{
var opId = RequestContext.Get(Const.TraceId) as string;
if (!string.IsNullOrWhiteSpace(opId))
{
telemetry.Context.Operation.Id = opId;
}
telemetry.Context.Component.Version = Version;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment