如何基于LogEventLevel为Serilog实现条件式上下文增强
为Serilog实现按日志级别条件添加上下文属性的流畅API
需求背景
希望在类中集中配置Serilog的上下文增强逻辑,通过流畅API实现按日志级别条件动态添加不同属性——比如不同级别日志自动携带对应专属属性,而非手动维护多个Logger实例或在每个日志调用处做判断。
现有方案的不足
- 手动编写
GetLogger方法切换不同Logger的方式,需要在每个日志调用前指定级别,代码冗余且容易出错; - 官方的
Enrich.AtLevel仅支持“>=指定级别”的判断,且需要重新创建Logger实例,无法满足更灵活的级别条件(如<=、==),也不符合集中配置的需求。
最优实现方案
通过自定义条件属性增强器和ILogger扩展方法,完全实现你期望的流畅API风格:
1. 实现条件属性增强器
创建一个实现ILogEventEnricher的类,负责在日志事件生成时判断级别并添加属性:
public class ConditionalPropertyEnricher : ILogEventEnricher { private readonly Func<LogEventLevel, bool> _levelCondition; private readonly string _propertyName; private readonly object _propertyValue; public ConditionalPropertyEnricher(Func<LogEventLevel, bool> levelCondition, string propertyName, object propertyValue) { _levelCondition = levelCondition ?? throw new ArgumentNullException(nameof(levelCondition)); _propertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); _propertyValue = propertyValue; } public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { // 当日志级别满足条件时,添加属性 if (_levelCondition(logEvent.Level)) { var property = propertyFactory.CreateProperty(_propertyName, _propertyValue); logEvent.AddPropertyIfAbsent(property); } } }
2. 扩展ILogger的流畅API
写一个静态扩展方法,让你可以直接在ILogger实例上调用ForConditionalContext:
public static class LoggerConditionalContextExtensions { public static ILogger ForConditionalContext(this ILogger logger, Func<LogEventLevel, bool> levelCondition, string propertyName, object propertyValue) { return logger.ForContext(new ConditionalPropertyEnricher(levelCondition, propertyName, propertyValue)); } }
3. 按期望方式使用
现在你可以像最初设想的那样,在类中集中配置条件属性,调用时无需额外判断:
public class MyClass { ILogger Log => Serilog.Log.Logger .ForConditionalContext(lvl => lvl <= LogEventLevel.Information, "prop 1", "仅添加到Information及以下级别") .ForConditionalContext(lvl => lvl <= LogEventLevel.Debug, "prop 2", "仅添加到Debug及以下级别") .ForConditionalContext(lvl => lvl == LogEventLevel.Verbose, "prop 3", "仅添加到Verbose级别"); void MyMethod() { // 这条Verbose日志会包含prop1、prop2、prop3 Log.ForContext("method-specific ctx", "value") .Verbose("这条日志会包含所有符合条件的属性"); // 这条Information日志仅包含prop1 Log.ForContext("method-specific ctx", "value") .Information("这条日志仅包含Information级别对应的属性"); } }
方案优势
- 完全贴合流畅API需求:集中配置类级别的条件属性,调用时无需手动切换Logger;
- 灵活的级别判断:支持任意自定义级别条件(<=、==、范围判断等),比官方
Enrich.AtLevel更强大; - 轻量无侵入:无需创建新的Logger实例,仅通过添加增强器实现逻辑,与Serilog原有上下文机制完全兼容;
- 代码更整洁:避免了冗余的switch判断和重复的Logger初始化逻辑。
内容的提问来源于stack exchange,提问作者Andrea Bardelli
相关产品推荐
相关产品推荐

