Skip to content

Instantly share code, notes, and snippets.

@ti24horas
Created March 29, 2018 11:16
Show Gist options
  • Save ti24horas/30a0dfc81b3f2a233546af64a7edfabe to your computer and use it in GitHub Desktop.
Save ti24horas/30a0dfc81b3f2a233546af64a7edfabe to your computer and use it in GitHub Desktop.
internal class ConfigurableLoggerFiltering : IFilterLoggerSettings
{
private CancellationTokenSource cts;
private IChangeToken token;
private List<(string category, LogLevel level)> levels = new List<(string category, LogLevel level)>();
public ConfigurableLoggerFiltering()
{
this.Reload();
}
public void Change(string key, LogLevel level)
{
var list = levels.ToList();
var exists = list.Where((s, idx) => string.Equals(s.category, key, StringComparison.OrdinalIgnoreCase)).Select((s, ix) => ix).ToArray();
if (list.Any())
{
list.RemoveAt(exists.First());
}
list.Add((key, level));
Interlocked.Exchange(ref this.levels, list);
this.Reload();
}
public bool TryGetSwitch(string name, out LogLevel level)
{
var list = this.levels.FirstOrDefault(s => s.category.StartsWith(name));
if (list.category == null)
{
list = this.levels.FirstOrDefault(s => s.category == "Default");
if (list.category == null)
{
level = LogLevel.None;
return false;
}
level = list.level;
return true;
}
level = list.level;
return true;
}
public IFilterLoggerSettings Reload()
{
var old = Interlocked.Exchange(ref this.cts, new CancellationTokenSource());
Interlocked.Exchange(ref this.token, new CancellationChangeToken(cts.Token));
old?.Cancel();
return this;
}
public IChangeToken ChangeToken => this.token;
}
public static class ServiceCollectionExtensions
{
public static KeyedServicesConfiguration<TKey, TService> AddKeyedServiceProvider<TKey, TService>(
this IServiceCollection services)
{
services.TryAddSingleton<IKeyedServiceProvider<TKey, TService>, KeyedServiceProvider<TKey, TService>>();
return new KeyedServicesConfiguration<TKey, TService>(services);
}
public class KeyedServicesConfiguration<TKey, TService>
{
private readonly IServiceCollection _services;
public KeyedServicesConfiguration(IServiceCollection services)
{
_services = services;
}
public KeyedServicesConfiguration<TKey, TService> AddSingleton<TImpl>(TKey key)
where TImpl : class, TService
{
_services.TryAddSingleton<TImpl>();
_services.TryAddSingleton(sp => new KeyedService<TKey, TService>(key, sp.GetRequiredService<TImpl>));
return this;
}
public KeyedServicesConfiguration<TKey, TService> AddTransient<TImpl>(TKey key)
where TImpl : class, TService
{
_services.TryAddTransient<TImpl>();
_services.TryAddSingleton(sp => new KeyedService<TKey, TService>(key, sp.GetRequiredService<TImpl>));
return this;
}
public KeyedServicesConfiguration<TKey, TService> AddScoped<TImpl>(TKey key)
where TImpl : class, TService
{
_services.TryAddScoped<TImpl>();
_services.TryAddSingleton(sp => new KeyedService<TKey, TService>(key, sp.GetRequiredService<TImpl>));
return this;
}
public KeyedServicesConfiguration<TKey, TService> Add<TImpl>(TKey key, TImpl instance)
where TImpl : class, TService
{
_services.TryAddSingleton(instance);
_services.TryAddSingleton(sp => new KeyedService<TKey, TService>(key, sp.GetRequiredService<TImpl>));
return this;
}
public KeyedServicesConfiguration<TKey, TService> Add<TImpl>(TKey key, Func<TKey, IServiceProvider, TImpl> constructor)
where TImpl : class, TService
{
_services.TryAddTransient(sp => constructor(key, sp));
_services.TryAddSingleton(sp => new KeyedService<TKey, TService>(key, sp.GetRequiredService<TImpl>));
return this;
}
public KeyedServicesConfiguration<TKey, TService> Add<TImpl>(TKey key, Func<IServiceProvider, TImpl> constructor)
where TImpl : class, TService
{
_services.TryAddTransient(sp => constructor(sp));
_services.TryAddSingleton(sp => new KeyedService<TKey, TService>(key, sp.GetRequiredService<TImpl>));
return this;
}
}
private class KeyedServiceProvider<TKey, TService> : IKeyedServiceProvider<TKey, TService>
{
private readonly IDictionary<TKey, KeyedService<TKey, TService>> _providedServices;
public KeyedServiceProvider(IEnumerable<KeyedService<TKey, TService>> providedServices)
{
_providedServices = providedServices.ToDictionary(s => s.Key);
}
public TService GetRequiredNamed(TKey key)
{
return this._providedServices[key].GetService();
}
}
private class KeyedService<TKey, TService>
{
private readonly Func<TService> _implFunction;
public KeyedService(TKey key, Func<TService> implFunction)
{
_implFunction = implFunction;
this.Key = key;
}
public TKey Key { get; }
public TService GetService() => this._implFunction();
}
}
public class ReloadableLoggerFactory : ILoggerFactory
{
private readonly ILoggerFactory parent;
private readonly IFilterLoggerSettings settings;
public ReloadableLoggerFactory(ILoggerFactory parent, IFilterLoggerSettings settings)
{
this.parent = parent;
this.settings = settings;
}
public void Dispose()
{
this.parent.Dispose();
}
public ILogger CreateLogger(string categoryName)
{
return this.parent.CreateLogger(categoryName);
}
public void AddProvider(ILoggerProvider provider)
{
this.parent.AddProvider(new FilteredLoggerProvider(provider, this.settings));
}
private class FilteredLoggerProvider : ILoggerProvider
{
private readonly ILoggerProvider _parent;
private readonly IFilterLoggerSettings _settings;
public FilteredLoggerProvider(ILoggerProvider parent, IFilterLoggerSettings settings)
{
_parent = parent;
_settings = settings;
}
public void Dispose()
{
this._parent.Dispose();
}
public ILogger CreateLogger(string categoryName)
{
return new FilteredLogger(categoryName, this._parent.CreateLogger(categoryName), this._settings);
}
private class FilteredLogger : ILogger
{
private readonly ILogger _parent;
private readonly IFilterLoggerSettings _settings;
private LogLevel currentLevel;
private IDisposable disposable;
~FilteredLogger()
{
this.disposable?.Dispose();
}
public FilteredLogger(string category, ILogger parent, IFilterLoggerSettings settings)
{
_parent = parent;
_settings = settings;
disposable = ChangeToken.OnChange(() => settings.ChangeToken, () =>
{
if (_settings.TryGetSwitch(category, out var result))
{
this.currentLevel = result;
}
else
{
this.currentLevel = LogLevel.Trace;
}
});
if (_settings.TryGetSwitch(category, out var v))
{
this.currentLevel = v;
}
else
{
this.currentLevel = LogLevel.Trace;
}
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception,
Func<TState, Exception, string> formatter)
{
if (this.IsEnabled(logLevel))
{
this._parent.Log(logLevel, eventId, state, exception, formatter);
}
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel >= currentLevel;
}
public IDisposable BeginScope<TState>(TState state)
{
return this._parent.BeginScope(state);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment