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

调整Serilog异步接收器,移除CI/CD集成测试中的EventLog

问题

在Linux代理的CI/CD环境中执行ASP.NET Core 7应用的集成测试时,因Serilog.Sinks.EventLog不支持Linux,初始化Serilog配置时触发PlatformNotSupported异常。

应用生产环境需部署为Windows服务并写入EventLog,但测试环境(Linux CI/CD)不需要EventLog接收器,希望尽可能少修改应用源码,仅调整测试项目代码解决问题。

背景信息

  • 使用Microsoft.AspNetCore.Mvc.Testing的WebApplicationFactory编写集成测试
  • 生产环境Serilog配置包含Console和EventLog接收器,测试环境仅需Console
  • 已尝试:设置UseEnvironment("Testing")无效;注入appsettings.Testing.json但EventLog配置仍被加载

现有代码片段

测试工厂配置

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    var connBuilder = new SqlConnectionStringBuilder(_dbContainer.GetConnectionString())
    {
        InitialCatalog = "start-db",
    };
    _ = builder.ConfigureTestServices(services => services
        .AddDefaultTestAssets()
        .InjectTestConnectionStringProvider(connBuilder.ConnectionString)
        .InjectTestingDb(connBuilder.ConnectionString)
        .AddTestSecurity())
        .ConfigureAppConfiguration((c, b) =>
        {
            _ = b.AddJsonFile("appsettings.Testing.json"); // 尝试的解决方法之一
            c.HostingEnvironment.EnvironmentName = "Testing";
        });
}

应用Program.cs核心代码

var builder = WebApplication.CreateBuilder();

builder.AddServiceOptions();
builder.Host.UseSerilog();
builder.Configuration.AddEnvironmentVariables("SERVICE__");

// 服务注册...

var app = builder.Build();

Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(app.Configuration).CreateLogger();

// 中间件配置...

try
{
    await app.RunAsync();
}
catch (Exception ex)
{
    Log.Logger.Critical(ex, "Application terminated unexpectedly");
}
finally
{
    Log.Logger.Information("[{LogEvent:l}]The service is closing", LogEvent.AppClosing);
    Log.CloseAndFlush();
}

生产环境Serilog配置(appsettings.json)

"Serilog": {
  "Using": [
    "Serilog.Sinks.Console",
    "Serilog.Sinks.EventLog"
  ],
  "MinimumLevel": "Information",
  "WriteTo": [
    {
      "Name": "Async",
      "Args": {
        "configure": [
          {
            "Name": "Console",
            "Args": {
              "outputTemplate": "[{Timestamp:u}][{Application}][{MachineName}][{ProcessName}:{ProcessId}][{ThreadId}][{SourceContext}][{Level:u3}]{Message}{NewLine}{Exception}"
            }
          },
          {
            "Name": "EventLog",
            "Args": {
              "source": "{Application}",
              "restrictedToMinimumLevel": "Error",
              "logName": "Application",
              "manageEventSource": true,
              "outputTemplate": "[{Timestamp:u}][{Application}][{MachineName}][{ProcessName}:{ProcessId}][{ThreadId}][{SourceContext}][{Level:u3}]{Message}{NewLine}{Exception}"
            }
          }
        ]
      }
    }
  ],
  "Enrich": [
    "FromLogContext",
    "WithMachineName",
    "WithThreadId",
    "WithProcessId",
    "WithProcessName"
  ],
  "Properties": {
    "Application": "SEM.NG.SqlScriptExecutor"
  }
}

解决方案

方法1:通过环境变量覆盖Serilog配置(仅修改测试项目)

在测试工厂中添加内存配置项,直接覆盖Serilog的EventLog相关节点,避免其被加载:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    var connBuilder = new SqlConnectionStringBuilder(_dbContainer.GetConnectionString())
    {
        InitialCatalog = "start-db",
    };
    
    _ = builder.ConfigureTestServices(services => services
        .AddDefaultTestAssets()
        .InjectTestConnectionStringProvider(connBuilder.ConnectionString)
        .InjectTestingDb(connBuilder.ConnectionString)
        .AddTestSecurity())
        .ConfigureAppConfiguration((ctx, configBuilder) =>
        {
            // 加载测试环境配置文件
            configBuilder.AddJsonFile("appsettings.Testing.json", optional: false, reloadOnChange: true);
            // 覆盖Serilog配置,移除EventLog相关项
            configBuilder.AddInMemoryCollection(new Dictionary<string, string>
            {
                ["Serilog:Using:1"] = "",
                ["Serilog:WriteTo:0:Args:configure:1"] = ""
            });
            ctx.HostingEnvironment.EnvironmentName = "Testing";
        });
}

确保appsettings.Testing.json中仅包含Console接收器配置(如你提供的示例)。

方法2:平台判断动态过滤接收器(仅修改应用1处代码)

在Program.cs初始化Serilog时,根据操作系统平台自动跳过EventLog配置:

var loggerConfig = new LoggerConfiguration().ReadFrom.Configuration(app.Configuration);

// Linux环境下移除EventLog接收器
if (!OperatingSystem.IsWindows())
{
    loggerConfig = loggerConfig.WriteTo.Remove("EventLog");
}

Log.Logger = loggerConfig.CreateLogger();

这个修改极小,不影响生产环境(Windows)的EventLog功能,同时自动适配Linux测试环境。

方法3:利用ASP.NET Core环境分层配置(无代码修改)

将EventLog配置拆分到生产环境专属配置文件中:

  • appsettings.json(公共基础配置):
"Serilog": {
  "Using": [
    "Serilog.Sinks.Console"
  ],
  "MinimumLevel": "Information",
  "WriteTo": [
    {
      "Name": "Async",
      "Args": {
        "configure": [
          {
            "Name": "Console",
            "Args": {
              "outputTemplate": "[{Timestamp:u}][{Application}][{MachineName}][{ProcessName}:{ProcessId}][{ThreadId}][{SourceContext}][{Level:u3}]{Message}{NewLine}{Exception}"
            }
          }
        ]
      }
    }
  ],
  "Enrich": [
    "FromLogContext",
    "WithMachineName",
    "WithThreadId",
    "WithProcessId",
    "WithProcessName"
  ],
  "Properties": {
    "Application": "SEM.NG.SqlScriptExecutor"
  }
}
  • appsettings.Production.json(生产环境追加配置):
"Serilog": {
  "Using": [
    "Serilog.Sinks.EventLog"
  ],
  "WriteTo": [
    {
      "Name": "Async",
      "Args": {
        "configure": [
          {
            "Name": "EventLog",
            "Args": {
              "source": "{Application}",
              "restrictedToMinimumLevel": "Error",
              "logName": "Application",
              "manageEventSource": true,
              "outputTemplate": "[{Timestamp:u}][{Application}][{MachineName}][{ProcessName}:{ProcessId}][{ThreadId}][{SourceContext}][{Level:u3}]{Message}{NewLine}{Exception}"
            }
          }
        ]
      }
    }
  ]
}

然后在测试工厂中直接设置测试环境:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    var connBuilder = new SqlConnectionStringBuilder(_dbContainer.GetConnectionString())
    {
        InitialCatalog = "start-db",
    };
    
    _ = builder.ConfigureTestServices(services => services
        .AddDefaultTestAssets()
        .InjectTestConnectionStringProvider(connBuilder.ConnectionString)
        .InjectTestingDb(connBuilder.ConnectionString)
        .AddTestSecurity())
        .UseEnvironment("Testing");
}

ASP.NET Core会自动加载appsettings.Testing.json,不会加载生产环境的EventLog配置。

方法4:测试中直接替换Serilog配置(完全隔离测试环境)

在测试工厂中直接配置测试专属的Serilog实例,完全绕过应用的配置读取:

protected override void ConfigureWebHost(IWebHostBuilder builder)
{
    var connBuilder = new SqlConnectionStringBuilder(_dbContainer.GetConnectionString())
    {
        InitialCatalog = "start-db",
    };
    
    _ = builder.ConfigureTestServices(services => services
        .AddDefaultTestAssets()
        .InjectTestConnectionStringProvider(connBuilder.ConnectionString)
        .InjectTestingDb(connBuilder.ConnectionString)
        .AddTestSecurity())
        .UseSerilog((ctx, config) =>
        {
            config
                .MinimumLevel.Information()
                .WriteTo.Async(wt => wt.Console(outputTemplate: "[{Timestamp:u}][{Application}][{MachineName}][{ProcessName}:{ProcessId}][{ThreadId}][{SourceContext}][{Level:u3}]{Message}{NewLine}{Exception}"))
                .Enrich.FromLogContext()
                .Enrich.WithMachineName()
                .Enrich.WithThreadId()
                .Enrich.WithProcessId()
                .Enrich.WithProcessName()
                .Enrich.WithProperty("Application", "SEM.NG.SqlScriptExecutor");
        });
}

此方法完全隔离测试环境的日志配置,无需依赖任何appsettings文件。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 09:34:57