如何在Serilog.AspNetCore中高效缩短ActionName与SourceContext日志格式?
在ASP.NET Core中用Serilog.AspNetCore高效记录缩短的ActionName和SourceContext
一、简化SourceContext(仅保留类名)
现有方案的性能说明
你当前使用的Serilog.Expressions模板:
{Substring(SourceContext, LastIndexOf(SourceContext, '.') + 1)}
性能其实已经足够——Serilog.Expressions会将表达式编译为表达式树,而非每次日志调用都重复解析字符串,开销非常低。
更高效的替代方案:自定义Enricher
如果追求极致性能,推荐在日志事件生成阶段就提前处理SourceContext,将短类名存入新的日志属性,避免模板渲染时重复计算:
- 实现自定义Enricher:
public class ShortSourceContextEnricher : ILogEventEnricher { public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { if (logEvent.Properties.TryGetValue("SourceContext", out var sourceContextValue) && sourceContextValue is ScalarValue scalar && scalar.Value is string sourceContext) { var shortClassName = sourceContext.Substring(sourceContext.LastIndexOf('.') + 1); logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ShortSourceContext", shortClassName)); } } }
- 在Serilog配置中注册Enricher:
Log.Logger = new LoggerConfiguration() .Enrich.With<ShortSourceContextEnricher>() // 其他配置 .CreateLogger();
- 模板中直接使用新属性:
{ShortSourceContext}
这种方式仅在日志事件创建时处理一次SourceContext,高并发场景下比模板表达式更高效。
二、简化ActionName(保留Controller.Method格式)
方案1:自定义Enricher(性能最优)
和SourceContext的优化思路一致,提前处理ActionName并存为新属性:
- 实现Enricher:
public class ShortActionNameEnricher : ILogEventEnricher { public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { if (logEvent.Properties.TryGetValue("ActionName", out var actionNameValue) && actionNameValue is ScalarValue scalar && scalar.Value is string actionName) { // 移除括号中的程序集信息 var withoutAssembly = actionName.Split('(')[0].Trim(); var parts = withoutAssembly.Split('.'); var shortActionName = parts.Length >= 2 ? $"{parts[^2]}.{parts[^1]}" : actionName; // 兼容异常格式 logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ShortActionName", shortActionName)); } } }
- 注册后模板中使用
{ShortActionName}即可。
方案2:Serilog.Expressions自定义函数(模板内直接调用)
如果希望直接在模板中通过表达式实现,可注册自定义函数:
- 注册自定义函数:
using Serilog.Expressions; // 在Serilog配置前注册 ExpressionTemplate.RegisterFunction("ShortenActionName", (string actionName) => { if (string.IsNullOrEmpty(actionName)) return actionName; var withoutAssembly = actionName.Split('(')[0].Trim(); var parts = withoutAssembly.Split('.'); return parts.Length >= 2 ? $"{parts[^2]}.{parts[^1]}" : actionName; });
- 模板中调用函数:
{ShortenActionName(ActionName)}
这种方式无需新增日志属性,逻辑直接嵌入模板,但性能略低于Enricher方案(每次渲染模板时执行计算)。
内容的提问来源于stack exchange,提问作者Alek Davis
相关产品推荐
相关产品推荐

