Created
June 5, 2017 22:39
-
-
Save nblumhardt/e9477bf4c3e2acf13ae4227323cd0407 to your computer and use it in GitHub Desktop.
A sketch implementation of a mapped sink wrapper for Serilog
This file contains hidden or 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 Serilog.Configuration; | |
using Serilog.Core; | |
using Serilog.Events; | |
namespace Serilog.Sinks.Map | |
{ | |
class Program | |
{ | |
static void Main() | |
{ | |
Log.Logger = new LoggerConfiguration() | |
.WriteTo.Map("SourceContext", (source, wt) => wt.RollingFile($"./logs/log-{source}-{{Date}}.txt")) | |
.CreateLogger(); | |
Log.ForContext<string>().Information("Hello, from string!"); | |
Log.ForContext<int>().Information("Hello, from int!"); | |
Log.CloseAndFlush(); | |
} | |
} | |
public static class MapLoggerConfigurationExtensions | |
{ | |
public static LoggerConfiguration Map( | |
this LoggerSinkConfiguration loggerSinkConfiguration, | |
string keyPropertyName, | |
Action<string, LoggerSinkConfiguration> configure, | |
string defaultKey = null, | |
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, | |
LoggingLevelSwitch levelSwitch = null) | |
{ | |
return Map<string>(loggerSinkConfiguration, keyPropertyName, configure, defaultKey, restrictedToMinimumLevel, levelSwitch); | |
} | |
public static LoggerConfiguration Map<TKey>( | |
this LoggerSinkConfiguration loggerSinkConfiguration, | |
string keyPropertyName, | |
Action<TKey, LoggerSinkConfiguration> configure, | |
TKey defaultKey = default(TKey), | |
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, | |
LoggingLevelSwitch levelSwitch = null) | |
{ | |
if (loggerSinkConfiguration == null) throw new ArgumentNullException(nameof(loggerSinkConfiguration)); | |
if (keyPropertyName == null) throw new ArgumentNullException(nameof(keyPropertyName)); | |
if (configure == null) throw new ArgumentNullException(nameof(configure)); | |
return Map(loggerSinkConfiguration, le => | |
{ | |
if (le.Properties.TryGetValue(keyPropertyName, out var v) && | |
v is ScalarValue sv && | |
sv.Value is TKey key) | |
{ | |
return key; | |
} | |
return defaultKey; | |
}, configure, restrictedToMinimumLevel, levelSwitch); | |
} | |
public static LoggerConfiguration Map<TKey>( | |
this LoggerSinkConfiguration loggerSinkConfiguration, | |
Func<LogEvent, TKey> keySelector, | |
Action<TKey, LoggerSinkConfiguration> configure, | |
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, | |
LoggingLevelSwitch levelSwitch = null) | |
{ | |
if (loggerSinkConfiguration == null) throw new ArgumentNullException(nameof(loggerSinkConfiguration)); | |
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); | |
if (configure == null) throw new ArgumentNullException(nameof(configure)); | |
return loggerSinkConfiguration.Sink(new MappedSink<TKey>(keySelector, configure), restrictedToMinimumLevel, levelSwitch); | |
} | |
} | |
class MappedSink<TKey> : ILogEventSink, IDisposable | |
{ | |
readonly Func<LogEvent, TKey> _keySelector; | |
readonly Action<TKey, LoggerSinkConfiguration> _configure; | |
readonly object _sync = new object(); | |
readonly Dictionary<TKey, Logger> _sinkMap = new Dictionary<TKey, Logger>(); | |
public MappedSink(Func<LogEvent, TKey> keySelector, Action<TKey, LoggerSinkConfiguration> configure) | |
{ | |
_keySelector = keySelector; | |
_configure = configure; | |
} | |
public void Emit(LogEvent logEvent) | |
{ | |
var key = _keySelector(logEvent); | |
Logger sink; | |
lock (_sync) | |
{ | |
if (!_sinkMap.TryGetValue(key, out sink)) | |
{ | |
var config = new LoggerConfiguration() | |
.MinimumLevel.Is(LevelAlias.Minimum); | |
_configure(key, config.WriteTo); | |
sink = _sinkMap[key] = config.CreateLogger(); | |
} | |
} | |
// Outside the lock to improve concurrency; this means the sink | |
// may throw ObjectDisposedException, which is fine. | |
sink.Write(logEvent); | |
} | |
public void Dispose() | |
{ | |
lock (_sync) | |
{ | |
var values = _sinkMap.Values; | |
_sinkMap.Clear(); | |
foreach (var sink in values) | |
{ | |
sink.Dispose(); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment