Last active
November 22, 2017 08:55
-
-
Save dsarfati/f2fd46a4e1dfaf216ece739f0fb143d2 to your computer and use it in GitHub Desktop.
Orleans Application Insights Logger
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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