logging - .NET Core - 如何根据类别进行过滤?
问题描述
所以我有一个基本的自定义记录器,并设置如下:
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 需要以某种方式检查传入的日志类别是否正确,但我只是不知道如何做到这一点。
我会很感激任何指示。
解决方案
所以这就是我最终做的事情:
public static IWebHost CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseIISIntegration()
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConsole();
logging.AddCustomLogger().AddFilter<CustomLoggerProvider>((category, logLevel) => {
if (category.ToUpper().Contains("Custom.Namespace") && logLevel != LogLevel.None)
{
return true;
}
else
{
return false;
}
}); // My custom logger
logging.SetMinimumLevel(LogLevel.Trace);
}).Build();
这是基于我在这里找到的信息:MSDN AddFilter Extensions,特别是这个签名:AddFilter(ILoggingBuilder, Func<String,LogLevel,Boolean>)
这正确地完成了我想要做的事情 - 与我的自定义命名空间相对应的任何类别现在都返回 True,它运行了我想要执行的正确 CustomLoggerProvider。
推荐阅读
- google-analytics - 在没有 Google Optimize 的情况下填写 ExperimentID 和 ExperimentVariant
- ios - Xcode:当用户选择语言时,如何更改应用程序的语言?
- javascript - 切换按钮 angularjs 指令不会改变状态
- sql - 如何在 PostgreSQL 中夹紧浮点数
- symfony - Symfony 3.4 远程认证
- reactjs - 如何更新共享同一个类的所有组件的状态?
- android - 如何启动 com.android.soundrecorder 并从 ADB 开始录音?
- asp.net - ASP.NET Core React-with-Redux 模板调度问题
- java - FindBy 结合 'between' 和 'or'
- c# - 如何释放多个进程之间共享的套接字描述符?