You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Serilog时LoggerMessage模式性能下降?求原因与优化方案

问题

按照微软的建议,我尝试将其高性能日志方案(即LoggerMessage模式)与Serilog日志记录器结合使用,但未获得任何性能提升,反而导致性能下降。以下是基准测试代码:

[MemoryDiagnoser]
public class SerilogBenchmark
{
    private static readonly ILogger<SerilogBenchmark> Logger;
    private static readonly string LogStringTemplate = "User {Username} {Email} has logged in.";
    private static Action<Microsoft.Extensions.Logging.ILogger, string, string, Exception?> LoggerAction =
        LoggerMessage.Define<string, string>(
            LogLevel.Information,
            new EventId(1, "Login"),
            LogStringTemplate);


    static SerilogBenchmark()
    {
        var services = new ServiceCollection();

        Log.Logger = new LoggerConfiguration()
            .WriteTo.TextWriter(TextWriter.Null)
            .CreateLogger();

        services.AddLogging(builder =>
        {
            builder.ClearProviders();
            builder.AddSerilog(dispose: true);
        });

        var serviceProvider = services.BuildServiceProvider();
        Logger = serviceProvider.GetRequiredService<ILogger<SerilogBenchmark>>();
    }

    [Benchmark]
    public void TestLogExtension()
    {
        Logger.LogInformation(LogStringTemplate, "User", "foo@bar.com");
    }

    [Benchmark]
    public void TestLogAction()
    {
        LoggerAction(Logger, "User", "foo@bar.com", null);
    }
}

测试结果显示,该"高性能日志"方案使内存分配增加50%,性能降低25%。恳请帮忙分析问题原因,并指导如何结合Serilog实现日志性能提升?

分析与解决方案

问题原因

  • 适配器层额外开销:Serilog通过Serilog.Extensions.Logging适配微软ILogger接口时,LoggerMessage生成的委托会触发模板格式的二次转换——微软原生模板需要映射到Serilog的模板逻辑,这中间产生了冗余的计算和内存分配。
  • 无效参数传递:LoggerMessage.Define生成的委托强制要求传递Exception?参数(即使传入null),而直接调用LogInformation扩展方法时,Serilog适配层会跳过无效参数的处理逻辑,减少不必要的开销。
  • 测试场景的特殊性:当前测试输出到TextWriter.Null,Serilog对空输出有专门的短路优化,但LoggerMessage的委托调用路径绕开了这部分优化,反而引入了冗余操作。

优化方案

1. 直接使用Serilog原生API

放弃微软LoggerMessage,使用Serilog原生强类型日志接口,彻底避免适配器开销:

// 跳过微软ILogger适配器,直接使用Serilog日志器
private static readonly ILogger SerilogLogger = Log.ForContext<SerilogBenchmark>();

[Benchmark]
public void TestSerilogNative()
{
    SerilogLogger.Information("User {Username} {Email} has logged in.", "User", "foo@bar.com");
}

这种方式完全利用Serilog的原生优化,能显著降低内存分配并提升性能。

2. 优化LoggerMessage的使用(保留微软ILogger)

如果必须依赖微软ILogger接口,可通过以下方式减少开销:

  • 移除无效Exception参数:使用.NET 6+支持的LoggerMessage.DefineWithoutException生成委托,跳过Exception参数传递:
private static Action<ILogger, string, string> LoggerAction =
    LoggerMessage.DefineWithoutException<string, string>(
        LogLevel.Information,
        new EventId(1, "Login"),
        LogStringTemplate);

// 调用时无需传入null
LoggerAction(Logger, "User", "foo@bar.com");
  • 开启Serilog适配优化:配置Serilog时启用微软日志优化开关,减少模板转换开销:
services.AddLogging(builder =>
{
    builder.ClearProviders();
    builder.AddSerilog(dispose: true, configure: l => l.EnableMicrosoftLoggingOptimizations());
});

3. 调整基准测试场景

  • 改用真实日志输出(如文件、控制台)测试,TextWriter.Null的特殊优化会导致基准结果失真;
  • 增加日志级别过滤测试(如设置为Warning),此时LoggerMessage在日志被过滤时的性能优势会更明显。

内容的提问来源于stack exchange,提问作者the_V

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:53:09