首页 > 解决方案 > .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 需要以某种方式检查传入的日志类别是否正确,但我只是不知道如何做到这一点。

我会很感激任何指示。

标签: logging.net-coreasp.net-core-2.1

解决方案


所以这就是我最终做的事情:

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。


推荐阅读