Created
November 2, 2018 12:33
-
-
Save Andreas-Hjortland/75e78e1c121bc1a35b1fab7cbdd6b773 to your computer and use it in GitHub Desktop.
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 Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Logging; | |
using Microsoft.Extensions.Logging.Console; | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text.RegularExpressions; | |
using System.Threading; | |
namespace DecoratingLogger | |
{ | |
public delegate TScope OnLog<TScope>(ILogger logger, LogLevel logLevel, EventId eventId, dynamic state, Exception exception); | |
public delegate TScope OnSimpleLog<TScope>(); | |
public static class DecoratingLoggerProviderExtensions | |
{ | |
public static OnLog<TScope> ConvertOnLog<TScope>(this OnSimpleLog<TScope> decorator) | |
{ | |
return (logger, logLevel, eventId, state, exception) => decorator(); | |
} | |
#region LoggerProvider | |
/// <summary> | |
/// Decorate a LoggerProvider with a callback which is run on log | |
/// statements | |
/// </summary> | |
/// <param name="provider">The provider to decorate</param> | |
/// <param name="decorator">The decorator</param> | |
/// <returns>A new LoggerProvider which will wrap and decorate the input</returns> | |
public static DecoratingLoggerProvider<TScope> AddDecorator<TScope>(this ILoggerProvider provider, OnLog<TScope> decorator) => new DecoratingLoggerProvider<TScope>(provider, decorator); | |
public static DecoratingLoggerProvider<TScope> AddDecorator<TScope>(this ILoggerProvider provider, OnSimpleLog<TScope> decorator) => new DecoratingLoggerProvider<TScope>(provider, decorator.ConvertOnLog()); | |
#endregion | |
#region LoggingBuilder | |
private static void ReplaceService<TScope>(ILoggingBuilder builder, ServiceDescriptor descriptor, IEnumerable<OnLog<TScope>> decorators) | |
{ | |
if (descriptor.ImplementationFactory == null && descriptor.ImplementationInstance == null) | |
{ | |
builder.Services.Add(new ServiceDescriptor(descriptor.ImplementationType, descriptor.ImplementationType, descriptor.Lifetime)); | |
} | |
builder.Services.Add( | |
new ServiceDescriptor(typeof(ILoggerProvider), | |
serviceProvider => | |
{ | |
var impl = (ILoggerProvider)( | |
descriptor.ImplementationInstance ?? | |
descriptor.ImplementationFactory?.Invoke(serviceProvider) ?? | |
serviceProvider.GetRequiredService(descriptor.ImplementationType)); | |
return decorators.Aggregate( | |
impl, | |
(provider, decorator) => provider.AddDecorator(decorator)); | |
}, | |
descriptor.Lifetime)); | |
builder.Services.Remove(descriptor); | |
} | |
/// <summary> | |
/// Replaces all logger providers with a provider which decorates them | |
/// with a callback which is run on a log statement. | |
/// </summary> | |
/// <param name="builder"></param> | |
/// <param name="decorators">Callbacks to run on on a log statement</param> | |
/// <returns>The builder after we have replaced all the other ILoggerProviders with a decorated version</returns> | |
/// <remarks> | |
/// If you need to decorate some, but not all loggers, you can achieve | |
/// that by adding all loggers you want to decorate before | |
/// `AddDecorator`, followed by `AddDecorator` and the rest of the | |
/// undecorated loggers. | |
/// </remarks> | |
public static ILoggingBuilder AddDecorator<TScope>(this ILoggingBuilder builder, IEnumerable<OnLog<TScope>> decorators) | |
{ | |
var loggerProviders = builder.Services | |
.Where(s => s.ServiceType.Equals(typeof(ILoggerProvider))) | |
.ToArray(); | |
foreach(var descriptor in loggerProviders) | |
{ | |
ReplaceService(builder, descriptor, decorators); | |
} | |
return builder; | |
} | |
public static ILoggingBuilder AddDecorator<TScope>(this ILoggingBuilder builder, params OnLog<TScope>[] decorators) | |
=> builder.AddDecorator((IEnumerable<OnLog<TScope>>)decorators); | |
public static ILoggingBuilder AddDecorator<TScope>(this ILoggingBuilder builder, params OnSimpleLog<TScope>[] decorators) | |
=> builder.AddDecorator(decorators.Select(ConvertOnLog)); | |
#endregion | |
#region LoggerFactory | |
/// <summary> | |
/// Add a decorated loggerProvider to the loggerfactory. | |
/// </summary> | |
/// <param name="loggerFactory"></param> | |
/// <param name="baseProvider">The provider to decorate</param> | |
/// <param name="decorators">Callbacks to run on a log statement</param> | |
/// <returns>The logger factory in the input with the new decorated provider</returns> | |
public static ILoggerFactory AddDecorator<TScope>(this ILoggerFactory loggerFactory, ILoggerProvider baseProvider, IEnumerable<OnLog<TScope>> decorators) | |
{ | |
loggerFactory.AddProvider( | |
decorators.Aggregate( | |
baseProvider, | |
(provider, decorator) => provider.AddDecorator(decorator) | |
) | |
); | |
return loggerFactory; | |
} | |
public static ILoggerFactory AddDecorator<TScope>(this ILoggerFactory loggerFactory, ILoggerProvider baseProvider, params OnLog<TScope>[] decorators) | |
=> loggerFactory.AddDecorator(baseProvider, (IEnumerable<OnLog<TScope>>)decorators); | |
public static ILoggerFactory AddDecorator<TScope>(this ILoggerFactory loggerFactory, ILoggerProvider baseProvider, params OnSimpleLog<TScope>[] decorators) | |
=> loggerFactory.AddDecorator(baseProvider, decorators.Select(ConvertOnLog)); | |
#endregion | |
} | |
public class DecoratingLoggerProvider<TScope> : ILoggerProvider | |
{ | |
private class DecoratingLogger : ILogger | |
{ | |
private readonly ILogger _baseLogger; | |
private readonly OnLog<TScope> _decorator; | |
public DecoratingLogger(ILogger baseLogger, OnLog<TScope> decorator) | |
{ | |
_baseLogger = baseLogger; | |
_decorator = decorator; | |
} | |
public IDisposable BeginScope<TState>(TState state) => _baseLogger.BeginScope(state); | |
public bool IsEnabled(LogLevel logLevel) => _baseLogger.IsEnabled(logLevel); | |
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) | |
{ | |
using (BeginScope(_decorator(this, logLevel, eventId, state, exception))) | |
{ | |
_baseLogger.Log(logLevel, eventId, state, exception, formatter); | |
} | |
} | |
} | |
private readonly ILoggerProvider _parentLoggerProvider; | |
private readonly OnLog<TScope> _decorator; | |
public DecoratingLoggerProvider(ILoggerProvider parentLoggerProvider, OnLog<TScope> decorator) | |
{ | |
_parentLoggerProvider = parentLoggerProvider; | |
_decorator = decorator; | |
} | |
public ILogger CreateLogger(string categoryName) | |
{ | |
var logger = _parentLoggerProvider.CreateLogger(categoryName); | |
return new DecoratingLogger(logger, _decorator); | |
} | |
public void Dispose() => _parentLoggerProvider.Dispose(); | |
} | |
} |
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 Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Logging; | |
using Microsoft.Extensions.Logging.Console; | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text.RegularExpressions; | |
using System.Threading; | |
namespace DecoratingLogger | |
{ | |
static class Program | |
{ | |
static void Main(string[] args) | |
{ | |
int count = 0; | |
var c = new ServiceCollection() | |
.AddLogging(loggingProvider => | |
{ | |
loggingProvider | |
.AddConsole(config => | |
{ | |
config.DisableColors = false; | |
config.IncludeScopes = true; | |
}) | |
.AddDecorator((l, logLevel, eventId, state, exception) => { | |
switch(logLevel) | |
{ | |
case LogLevel.Critical: | |
return ("Level", "Critical"); | |
case LogLevel.Error: | |
return ("Level", "Error"); | |
case LogLevel.Warning: | |
return ("Level", "Warning"); | |
case LogLevel.Information: | |
return ("Level", "Info"); | |
case LogLevel.Debug: | |
return ("Level", "Debug"); | |
case LogLevel.Trace: | |
return ("Level", "Trace"); | |
case LogLevel.None: | |
return ("Level", "None"); | |
default: | |
return ("Level", "Unknown"); | |
} | |
}) | |
.AddDecorator(() => ("count", Interlocked.Increment(ref count))); | |
}) | |
.BuildServiceProvider(); | |
var logger = c.GetService<ILoggerFactory>().CreateLogger(nameof(Program)); | |
using (logger.BeginScope("scope")) | |
{ | |
logger.LogInformation("Foo"); | |
} | |
logger.LogInformation("Bar"); | |
Console.WriteLine("Press enter to terminate the application"); | |
Console.ReadLine(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output from the application: