.NET Core - Как фильтровать по категориям? - PullRequest
0 голосов
/ 28 октября 2019

Итак, у меня есть базовый пользовательский регистратор, и я настроил его так:

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 каким-то образом должен проверить, что категория входящего журнала верна, но я просто не вижу, как это сделать.

Буду признателен за любые указатели.

Ответы [ 2 ]

0 голосов
/ 28 октября 2019

Итак, вот что я в итоге сделал:

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 , в частности, эта подпись: AddFilter (ILoggingBuilder, Func)

Это правильно сделало то, что я пытался сделать - любая категория, которая соответствовала моему настраиваемому пространству имен, теперь возвращает True, которая выполняла правильный CustomLoggerProvider, который я хотел выполнить.

0 голосов
/ 28 октября 2019

Первый параметр AddFilter - это пространство имен, к которому он применяется. В настоящее время вы передаете nameof(CustomLogEntry), что не сработает. По сути, это означает, что only применяет это к сообщениям, зарегистрированным от CustomLogEntry (то есть внутри класса). Хотя даже это не сработает, потому что у него не будет полного пространства имен до CustomLogEntry. Что вам нужно сделать, это что-то вроде:

.AddFilter<CustomLoggerProvider>("Microsoft", LogLevel.None)

Это тогда применило бы фильтр ко всем журналам, происходящим из любого места в пространстве имен, начинающемся с Microsoft, то есть Microsoft.AspNetCore, Microsoft.Extensions.Configuration и т. Д. Очевидно, вы бы добавили больше пространства имен, чтобы еще больше его ограничить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...