如何更改 Serilog 中特定日志事件的 LogLevel?

How to change the LogLevel of specific log events in Serilog?

是否有可配置的方法来更改特定日志事件的日志级别?也许用 serilog-expressions?

我的想法是,我们的配置会列出 methods/source 上下文和正则表达式以匹配暂时性错误。对于匹配的异常,记录警告而不是错误。记录错误会触发我们在 Seq 中的分类通知。如果我们得到一个不匹配的意外异常,我们确实想要记录一个错误。我们正在使用 Hangfire 重试,所以我们想抛出异常。如果不是短暂的,Hangfire 会报错。

这是我们想要的行为的硬编码示例:

void CurrentCode() {
    try {
        throw new Exception("Log a warning for this transient exception.");
    } catch (Exception e) {
        var regex = new Regex(@"^Log a warning");
        var logLevel = regex.IsMatch(e.Message) ? LogLevel.Warning : LogLevel.Error;
        logger.Log(logLevel, e, "{Method}() {ExceptionMessage}"
            , "CurrentCode", e.Message);
        throw;
    }
}

或者我们应该考虑采用完全不同的方法吗?

Serilog 的一种可能方法是创建一个接收器包装器,您可以在日志到达真正的接收器之前拦截日志,并根据符合您的条件的异常修改日志事件级别。

下面是一个根据某些逻辑修改日志事件的接收器包装器示例:

public class LogLevelModifierSink : ILogEventSink, IDisposable
{
    private readonly ILogEventSink _targetSink;

    public LogLevelModifierSink(ILogEventSink targetSink)
    {
        _targetSink = targetSink ?? throw new ArgumentNullException(nameof(targetSink));
    }

    public void Emit(LogEvent logEvent)
    {
        if (LogLevelMustChange(logEvent, out var newLogLevel))
        {
            // Clone the original log event, but with the new log level
            var newLogEvent = new LogEvent(
                logEvent.Timestamp,
                newLogLevel,
                logEvent.Exception,
                logEvent.MessageTemplate,
                logEvent.Properties
                    .Select(kv => new LogEventProperty(kv.Key, kv.Value)));

            _targetSink.Emit(newLogEvent);
        }
        else
        {
            // Pass-through the original event
            _targetSink.Emit(logEvent);
        }
    }

    private bool LogLevelMustChange(LogEvent logEvent, out LogEventLevel newLogLevel)
    {
        if (logEvent.Level == LogEventLevel.Error /*&& some other logic*/)
        {
            newLogLevel = LogEventLevel.Warning;
            return true;
        }
    
        newLogLevel = default;
        return false;
    }

    public void Dispose()
    {
        (_targetSink as IDisposable)?.Dispose();
    }
}

// Extension method to hook the wrapper into the configuration syntax
public static class LoggerSinkConfigurationLogLevelModifierExtensions
{
    public static LoggerConfiguration LogLevelModifier(
        this LoggerSinkConfiguration loggerSinkConfiguration,
        Action<LoggerSinkConfiguration> configure)
    {
        return LoggerSinkConfiguration.Wrap(loggerSinkConfiguration, sink =>
            new LogLevelModifierSink(sink), configure, LevelAlias.Minimum, null);
    }
}

接收器的用法示例:

void Main()
{
    Log.Logger = new Serilog.LoggerConfiguration()
        .MinimumLevel.Verbose()
        .WriteTo.LogLevelModifier(writeTo =>
            writeTo.Console())
        .CreateLogger();

    Log.Error("Hello, {Name}", "Augusto");

    Log.CloseAndFlush();
}