Created
November 19, 2021 08:22
-
-
Save Erwinvandervalk/89fda5ba57f10ac3a5d22f26a5c42ca8 to your computer and use it in GitHub Desktop.
Serilog TestLoggerProvider
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
public class ElapsedEnricher : ILogEventEnricher | |
{ | |
public const string PropertyName = "Elapsed"; | |
private readonly Stopwatch _stopwatch; | |
public ElapsedEnricher() | |
{ | |
_stopwatch = Stopwatch.StartNew(); | |
} | |
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) | |
{ | |
var value = (int)Math.Floor(_stopwatch.Elapsed.TotalSeconds) + "." + _stopwatch.Elapsed.Milliseconds; | |
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(PropertyName, | |
value)); | |
} | |
} |
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
/// <summary> | |
/// Adds a formatted version of an exception as a property, which then can easily be pretty-printed. | |
/// </summary> | |
/// <seealso cref="Serilog.Core.ILogEventEnricher" /> | |
public class ExceptionFormattingEnricher : ILogEventEnricher | |
{ | |
public const string EnrichedExceptionPropertyName = "_detailedException"; | |
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) | |
{ | |
if (logEvent.Exception != null) | |
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty(EnrichedExceptionPropertyName, | |
FormatException(logEvent.Exception))); | |
} | |
private static string FormatData(IDictionary dict, string indent) | |
{ | |
if (dict == null) return string.Empty; | |
indent = indent + " "; | |
var retVal = ""; | |
foreach (var val in dict.Keys) retVal += $"{Environment.NewLine}{indent}'{val}' : '{dict[val]}'"; | |
return retVal; | |
} | |
public static string FormatException(Exception arg) | |
{ | |
var builder = new StringBuilder(); | |
FormatException(arg, builder); | |
return builder.ToString(); | |
} | |
private static void FormatException(Exception exception, StringBuilder logs, int tabs = 4) | |
{ | |
if (exception == null) return; | |
var indent = new string(' ', tabs); | |
logs.AppendLine().AppendLine($"{indent}{exception.GetType()?.Name}:") | |
.AppendLine($"{indent}Message : '{exception.Message}"); | |
if (exception.Data.Count > 0) logs.AppendLine($"{indent}Data : {FormatData(exception.Data, indent)}"); | |
logs.AppendLine( | |
$"{indent}StackTrace : '{Environment.NewLine}{indent} {exception.StackTrace?.Replace(Environment.NewLine, Environment.NewLine + indent + " ")}") | |
.AppendLine($"{indent}TargetSite : '{exception.TargetSite}"); | |
if (exception.InnerException != null) logs.AppendLine($"{indent}InnerEx : "); | |
FormatException(exception.InnerException, logs, tabs + 4); | |
} | |
} |
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
/// <summary> | |
/// Writes the log messages through the serilog extensions | |
/// </summary> | |
public class TestLoggerProvider : IDisposable, ILoggerProvider | |
{ | |
public static readonly string ApplicationNameProperty = "ApplicationName"; | |
/// <summary> | |
/// This message template is denser and contains the enriched exceptions | |
/// </summary> | |
public static readonly string ImprovedLoggingTemplate = | |
"{" + ElapsedEnricher.PropertyName + "} [{Level:u3}]{" + ApplicationNameProperty + "} " | |
+ "{Message} {SpanId} {ParentId} {TraceId} - {sub} {NewLine}{" + | |
ExceptionFormattingEnricher.EnrichedExceptionPropertyName + "}"; | |
/// <summary> | |
/// Use the default serilog message template if you are not happy with the improved one. | |
/// </summary> | |
public static readonly string DefaultSerilogMessageTemplate = | |
"{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"; | |
private readonly Logger _serilog; | |
public readonly LoggerFactory LoggerFactory; | |
private readonly TextWriter logs = new StringWriter(); | |
public readonly Serilog.ILogger Serilog; | |
public TestLoggerProvider(ITestOutputHelper output, | |
string applicationName = null, | |
string outputTemplate = null, | |
LogEventLevel minimumLogLevel = LogEventLevel.Verbose, | |
Func<LoggerConfiguration, LoggerConfiguration> configure = null | |
) | |
{ | |
var loggerConfiguration = new LoggerConfiguration() | |
.Enrich.With(new ElapsedEnricher()) | |
.Enrich.With(new ExceptionFormattingEnricher()) | |
.Enrich.FromLogContext().MinimumLevel.Is(minimumLogLevel) | |
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) | |
; | |
if (configure != null) | |
{ | |
loggerConfiguration = configure.Invoke(loggerConfiguration); | |
} | |
if (applicationName != null) | |
{ | |
loggerConfiguration.Enrich.WithProperty(ApplicationNameProperty, | |
" " + applicationName + " -"); | |
} | |
_serilog = loggerConfiguration.WriteTo.TextWriter(logs, minimumLogLevel, ImprovedLoggingTemplate).WriteTo | |
.TestOutput(output, minimumLogLevel, ImprovedLoggingTemplate).CreateLogger(); | |
Serilog = _serilog; | |
LoggerFactory = new LoggerFactory(); | |
LoggerFactory.AddProvider(new SerilogLoggerProvider(Serilog)); | |
} | |
public string Logs => logs.ToString(); | |
public void Dispose() | |
{ | |
_serilog.Dispose(); | |
LoggerFactory?.Dispose(); | |
logs?.Dispose(); | |
} | |
public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) | |
{ | |
return LoggerFactory.CreateLogger(categoryName); | |
} | |
public ILogger<T> BuildLogger<T>() | |
{ | |
return LoggerFactory.CreateLogger<T>(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment