如何更改 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();
}
是否有可配置的方法来更改特定日志事件的日志级别?也许用 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();
}