Итак, у меня есть базовый пользовательский регистратор, и я настроил его так:
public static IWebHost CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseIISIntegration()
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConsole();
logging.AddCustomLogger().AddFilter<CustomLoggerProvider>(typeof(CustomLogEntry).ToString(), LogLevel.None); // My custom logger
logging.SetMinimumLevel(LogLevel.Trace);
}).Build();
}
Насколько я понимаю, .AddFilter означает, что только регистраторы с
typeof(CustomLogEntry)
должнына самом деле выйти на мой пользовательский регистратор. Однако это не так!
Расширение для добавления фактического регистратора довольно простое:
public static ILoggingBuilder AddCustomLogger(this ILoggingBuilder builder)
{
builder.AddConfiguration();
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, CustomLoggerProvider>());
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IConfigureOptions<CustomLoggerOptions>, CustomLoggerOptionsSetup>());
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptionsChangeTokenSource<LoggerOptions>, CustomLoggerProviderOptionsChangeTokenSource<CustomLoggerOptions, CustomLoggerProvider>>());
return builder;
}
Мой собственный регистратор также довольно прост:
public class CustomLogger : ILogger
{
public CustomLoggerProvider Provider { get; private set; }
public string Category { get; private set; }
public Logger(CustomLoggerProvider provider, string category)
{
Provider = provider;
Category = category;
}
public IDisposable BeginScope<T>(T item)
{
return Provider.ScopeProvider.Push(item);
}
public bool IsEnabled(LogLevel logLevel)
{
return Provider.IsEnabled(logLevel);
}
public void Log<T>(LogLevel logLevel, EventId eventId, T item, Exception exception, Func<T, Exception, string> formatter)
{
if (IsEnabled(logLevel))
{
// Generate the log entry
CustomLogEntry logEntry = new CustomLogEntry();
// removed for brevity...
// Write the log out to the provider!
Provider.WriteLog(logEntry);
}
}
}
И провайдер:
[ProviderAlias("Custom")]
public class CustomLoggerProvider : ILoggerProvider, IDisposable, ISupportExternalScope
{
private ConcurrentQueue<CustomLogEntry> _logQueue = new ConcurrentQueue<CustomLogEntry>();
private readonly IDisposable _settingsChangeToken;
private readonly CustomLogger _customLogger;
private CustomLoggerOptions _settings;
private bool _terminated = false;
public IExternalScopeProvider ScopeProvider { get; set; }
public CustomLoggerProvider(IOptionsMonitor<LoggerOptions> settings, IConfiguration configuration) : this(settings.CurrentValue, configuration)
{
// https://docs.microsoft.com/en-us/aspnet/core/fundamentals/change-tokens
_settingsChangeToken = settings.OnChange(options =>
{
_settings = options;
});
}
public CustomLoggerProvider(CustomLoggerOptions settings, IConfiguration configuration)
{
_settings = settings;
_customLogger = new CustomLogger(this, typeof(CustomLogEntry).ToString());
ProcessQueue();
}
public ILogger CreateLogger(string categoryName)
{
return _customLogger;
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel == LogLevel.Information;
}
public void WriteLog(LogEntry logEntry)
{
_logQueue.Enqueue(logEntry);
}
private void ProcessQueue()
{
Task.Run(async () =>
{
while (!_terminated)
{
try
{
// chew up the queue
if (_logQueue.TryDequeue(out CustomLogEntry logEntry))
{
// Do work, removed for brevity
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
});
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
_terminated = true;
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// This code added to correctly implement the disposable pattern.
void IDisposable.Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
#endregion
#region ISupportExternalScope Support
public void SetScopeProvider(IExternalScopeProvider scopeProvider)
{
ScopeProvider = scopeProvider;
}
#endregion
}
Итак, после того, как я собрал это и протестировал, я вижу, что каждый журнал LogLevel.Trace или выше регистрируется в CustomLogger.
То, что я пытаюсь сделать, - это иметь только журналы определенной категории, фактически выходящие из системы для специального провайдера. Или, другими словами, отфильтруйте, к какому провайдеру журналов подключиться, в зависимости от категории.
Что я не могу сделать? Я дико неправильно понимаю, как .AddFilter на самом деле работает? (вероятно, смеется)
Я подозреваю, что мой CustomLoggerProvider.IsEnabled каким-то образом должен проверить, что категория входящего журнала верна, но я просто не вижу, как это сделать.
Буду признателен за любые указатели.