我需要能够在某些范围内临时禁用日志记录。在我的情况下,有一个后台任务,该任务会定期尝试为系统中的每个可用COM端口实例化一些设备API,并查看其是否失败。该API会写入大量信息以备万一发生故障(异常,内部组件Dispose调用等)时进行记录。结果,每秒都有如此多的失败尝试错误泛滥成日志。
我想出了使用LogContext.PushProperty
来识别被禁止的日志事件的解决方案。但是,以下代码不会记录任何内容:
internal static class Program
{
public static void Main(String[] args)
{
void StartListeningSomething()
{
Task.Factory.StartNew(() =>
{
while (true)
{
Log.Information("Listening");
Thread.Sleep(500);
}
}, TaskCreationOptions.LongRunning);
}
Log.Logger = new LoggerConfiguration()
.Enrich.WithThreadId()
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("SuppressLogging"))
.Enrich.FromLogContext()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
using (LogContext.PushProperty("SuppressLogging", true))
{
StartListeningSomething();
Console.ReadKey(); // Will ignore background thread log messages until key enter
}
// We want to start logging events after exiting using block
// But they won't be logged for listener thread at all
Console.ReadKey();
}
}
即使将其从范围中弹出,侦听器任务内的所有日志事件也将充实“ SupressLogging”属性。
答案 0 :(得分:1)
我发现的唯一解决方法(整个API中繁琐的传递自定义ILogger
除外)包括以下步骤:
"SupressLogging"
属性分配一些唯一的值Filter
部分中,检查是否附加了属性并且其值有效(包含在存储中)。以下代码使用自定义IDisposable令牌使它看起来像通常的PushProperty
internal static class Program
{
public static void Main(String[] args)
{
void StartListeningSomething()
{
Task.Factory.StartNew(() =>
{
while (true)
{
Log.Information("Listening");
Thread.Sleep(500);
}
}, TaskCreationOptions.LongRunning);
}
Log.Logger = new LoggerConfiguration()
.Enrich.WithThreadId()
.Filter.ByExcluding(logEvent => logEvent.IsSuppressed()) // Check if log event marked with supression property
.Enrich.FromLogContext()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();
using (SerilogExtensions.SuppressLogging())
{
StartListeningSomething();
Console.ReadKey(); // Will ignore background thread log messages until some key is entered
}
// Will start logging events after exiting the using block
Console.ReadKey();
}
}
以及实际的SerilogExtensions:
/// <summary>
/// Provides helper extensions to Serilog logging.
/// </summary>
public static class SerilogExtensions
{
private const String SuppressLoggingProperty = "SuppressLogging";
private static readonly HashSet<Guid> ActiveSuppressions = new HashSet<Guid>();
/// <summary>
/// Get disposable token to supress logging for context.
/// </summary>
/// <remarks>
/// Pushes "SuppressLogging" property with unique value to SerilogContext.
/// When disposed, disposes Serilog property push token and invalidates stored value so new log messages are no longer
/// supressed.
/// </remarks>
public static IDisposable SuppressLogging()
{
return new SuppressLoggingDisposableToken();
}
/// <summary>
/// Determines whether the given log event suppressed.
/// </summary>
/// <remarks>
/// Also removes "SuppressLogging" property if present.
/// </remarks>
public static Boolean IsSuppressed(this LogEvent logEvent)
{
Boolean containsProperty = logEvent.Properties.TryGetValue(SuppressLoggingProperty, out var val);
if (!containsProperty)
return false;
logEvent.RemovePropertyIfPresent(SuppressLoggingProperty); //No need for that in logs
if (val is ScalarValue scalar && scalar.Value is Guid id)
return ActiveSuppressions.Contains(id);
return false;
}
/// <summary>
/// Disposable wrapper around logging supression property push/pop and value generation/invalidation.
/// </summary>
private class SuppressLoggingDisposableToken : IDisposable
{
private readonly IDisposable _pushPropertyDisposable;
private readonly Guid _guid;
public SuppressLoggingDisposableToken()
{
_guid = Guid.NewGuid();
_pushPropertyDisposable = LogContext.PushProperty(SuppressLoggingProperty, _guid);
ActiveSuppressions.Add(_guid);
}
public void Dispose()
{
ActiveSuppressions.Remove(_guid);
_pushPropertyDisposable.Dispose();
}
}
}
可以在github上找到完整的示例项目。
我想在这里留下这个自我解答的问题,并想问更多有经验的Serilog用户对这个问题的看法。可能是我没有找到抑制日志记录的一些常用方法吗?
答案 1 :(得分:1)
我想添加到 ArXen42 答案中。
用于跟踪 activesuppression 键的提议 Hashset 不是线程安全的,并且在使用多线程时会产生问题。
一种解决方案是使用 ConcurrentDictionary<T,T2>
而不是 HashSet<T>
或如下所述的解决方案,而不跟踪 GUID 以抑制日志。
/// Provides helper extensions to Serilog logging.
/// </summary>
public static class SerilogExtensions
{
private const string SuppressLoggingProperty
= "SuppressLogging";
/// <summary>
/// Get disposable token to supress logging for context.
/// </summary>
public static IDisposable SuppressLogging()
{
return LogContext.PushProperty(SuppressLoggingProperty, true);
}
/// <summary>
/// Determines whether the given log event suppressed.
/// </summary>
/// <remarks>
/// Also removes "SuppressLogging" property if present.
/// </remarks>
public static bool IsSuppressed(this LogEvent logEvent)
{
var containsProperty = logEvent.Properties
.TryGetValue(SuppressLoggingProperty, out var val);
if (!containsProperty)
return false;
// remove suppression property from logs
logEvent.RemovePropertyIfPresent(SuppressLoggingProperty);
if (val is ScalarValue scalar && scalar.Value is bool isSuppressed)
return isSuppressed;
return false;
}
}