如何使用 .NET 6 的新最小托管模型在集成测试中使 Serilog 静音
How to silence Serilog in integration tests with the new minimal hosting model of .NET 6
我有一个 .NET 6 web API 项目,其中包含针对某些 API 端点的现有集成测试。该项目使用 Serilog 进行日志记录,到目前为止一切正常。
我将代码迁移到新的最小托管模型,删除了流程中的 Startup class。我修复了集成测试以与新模型一起使用,到目前为止一切都是 运行ning。我遇到的唯一问题是,集成测试现在会发送垃圾日志语句。
对于 Serilog,我有两个阶段设置,这就是 Program.cs 的样子:
public partial class Program
{
public static string ApplicationVersion => typeof(Program).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
.InformationalVersion;
/// <summary>
/// Hack to prevent duplicate logger initialization when integration tests run in parallel.
/// </summary>
public static bool IsIntegrationTestRun = false;
public static int Main(string[] args)
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
if (!IsIntegrationTestRun)
{
// extra logger only for app startup
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger();
}
try
{
Log.Information("Starting <my application> v{version} in env {env}.", ApplicationVersion, env);
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.Local.json", true, true);
// Actual logger for dependency injection
builder.Host.UseSerilog((ctx, lc) =>
{
lc.ReadFrom.Configuration(ctx.Configuration);
});
// ...
var app = builder.Build();
// ...
using (IServiceScope scope = app.Services.CreateScope())
{
var dataContext = scope.ServiceProvider.GetRequiredService<DataContext>();
dataContext.Database.Migrate();
}
app.UseSerilogRequestLogging(c =>
{
c.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("Host", httpContext.Request.Host.ToString());
diagnosticContext.Set("UserAgent", httpContext.Request.Headers["User-Agent"]);
};
c.GetLevel = LogLevelHelper.GetRequestLevel;
});
// ...
app.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly.");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
}
这是我的 WebApplicationFactory:
[CollectionDefinition("WebApplicationFactory")]
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// Somewhat hacky but prevents duplicate logger initialization when integration tests run in parallel.
Program.IsIntegrationTestRun = true;
builder.ConfigureAppConfiguration((context, builder) =>
{
// Load custom appsettings for Test
builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.Test.json"));
// optional load personal settings included in gitignore
builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.LocalTest.json"), true);
builder.AddEnvironmentVariables();
});
// builder.ConfigureLogging(lb => lb.ClearProviders());
Log.Logger = new LoggerConfiguration().MinimumLevel.Fatal().CreateLogger();
// ...
}
}
这样使用的:
[Collection("WebApplicationFactory")]
public class SomeTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly CustomWebApplicationFactory<Program> _factory;
public SomeTests(CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public async Task Get_Some_ReturnsSomething()
{
// setup ...
HttpClient client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("Authorization", RequestHelper.GetBearerAuthenticationHeaderValue(user));
RequestHelper.AddStrangeHeader(client, user.StrangeKey);
HttpResponseMessage response = await client.GetAsync("/api/some");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var res = await RequestHelper.DeserializeResponse<List<SomeModel>>(response);
Assert.Equal(SomeCount, res.Count);
}
}
如您所见,我已经扩展了 appsettings.json 模式以使用本地 gitignored 文件进行本地开发(将秘密排除在 repostiory 之外)和一个额外的 appsettings.Test.json
(还有另一个 git 忽略了 appsettings.LocalTest.json
,带有额外的测试设置,例如不同的数据库连接)。
当我 运行 时,集成测试控制台充斥着日志语句。奇怪的是,似乎并没有记录所有内容,例如我看不到任何请求日志。但是我可以多次看到数据库迁移的日志,如下所示:
[09:57:38 INF Microsoft.EntityFrameworkCore.Migrations] Applying migration '20210224073743_InitialSchema'
或者这个
[09:57:40 DBG lJty8ESu24x-MY6n4EYr Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] Successfully validated the token.
.
我尝试了很多方法,比如将最低日志级别设置为 Fatal
或直接用新记录器替换 Log.Logger。
应用程序本身正在使用注入的 ILogger 而不是静态 Log.Logger。谁能指导我如何解决这个问题或下一步我可以尝试什么?
日志记录似乎符合我的 appsettings.Test.json
文件中的设置,当我降低调试的最低级别时,我可以看到更多日志打印在测试 运行 上。但是,为什么即使我将最低级别设置为致命,也会记录迁移消息?
我想我已经做到了。在您的 CustomWebApplicationFactory
中,将其放入您的 ConfigureWebHost
:
[CollectionDefinition("WebApplicationFactory")]
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
#pragma warning disable CS0618
builder.UseSerilog((_, _) => { });
#pragma warning restore CS0618
// ... other customizations
base.ConfigureWebHost(builder);
}
}
它会抱怨该方法已过时,但这对我有用,它会停止调用我原来的 Serilog 配置,并且只会停止记录任何内容。
如果你愿意,我相信你也可以使用它来更改配置。
我有一个 .NET 6 web API 项目,其中包含针对某些 API 端点的现有集成测试。该项目使用 Serilog 进行日志记录,到目前为止一切正常。
我将代码迁移到新的最小托管模型,删除了流程中的 Startup class。我修复了集成测试以与新模型一起使用,到目前为止一切都是 运行ning。我遇到的唯一问题是,集成测试现在会发送垃圾日志语句。
对于 Serilog,我有两个阶段设置,这就是 Program.cs 的样子:
public partial class Program
{
public static string ApplicationVersion => typeof(Program).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
.InformationalVersion;
/// <summary>
/// Hack to prevent duplicate logger initialization when integration tests run in parallel.
/// </summary>
public static bool IsIntegrationTestRun = false;
public static int Main(string[] args)
{
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production";
if (!IsIntegrationTestRun)
{
// extra logger only for app startup
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateBootstrapLogger();
}
try
{
Log.Information("Starting <my application> v{version} in env {env}.", ApplicationVersion, env);
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("appsettings.Local.json", true, true);
// Actual logger for dependency injection
builder.Host.UseSerilog((ctx, lc) =>
{
lc.ReadFrom.Configuration(ctx.Configuration);
});
// ...
var app = builder.Build();
// ...
using (IServiceScope scope = app.Services.CreateScope())
{
var dataContext = scope.ServiceProvider.GetRequiredService<DataContext>();
dataContext.Database.Migrate();
}
app.UseSerilogRequestLogging(c =>
{
c.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("Host", httpContext.Request.Host.ToString());
diagnosticContext.Set("UserAgent", httpContext.Request.Headers["User-Agent"]);
};
c.GetLevel = LogLevelHelper.GetRequestLevel;
});
// ...
app.Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly.");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
}
这是我的 WebApplicationFactory:
[CollectionDefinition("WebApplicationFactory")]
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
// Somewhat hacky but prevents duplicate logger initialization when integration tests run in parallel.
Program.IsIntegrationTestRun = true;
builder.ConfigureAppConfiguration((context, builder) =>
{
// Load custom appsettings for Test
builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.Test.json"));
// optional load personal settings included in gitignore
builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "appsettings.LocalTest.json"), true);
builder.AddEnvironmentVariables();
});
// builder.ConfigureLogging(lb => lb.ClearProviders());
Log.Logger = new LoggerConfiguration().MinimumLevel.Fatal().CreateLogger();
// ...
}
}
这样使用的:
[Collection("WebApplicationFactory")]
public class SomeTests : IClassFixture<CustomWebApplicationFactory<Program>>
{
private readonly CustomWebApplicationFactory<Program> _factory;
public SomeTests(CustomWebApplicationFactory<Program> factory)
{
_factory = factory;
}
[Fact]
public async Task Get_Some_ReturnsSomething()
{
// setup ...
HttpClient client = _factory.CreateClient();
client.DefaultRequestHeaders.Add("Authorization", RequestHelper.GetBearerAuthenticationHeaderValue(user));
RequestHelper.AddStrangeHeader(client, user.StrangeKey);
HttpResponseMessage response = await client.GetAsync("/api/some");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var res = await RequestHelper.DeserializeResponse<List<SomeModel>>(response);
Assert.Equal(SomeCount, res.Count);
}
}
如您所见,我已经扩展了 appsettings.json 模式以使用本地 gitignored 文件进行本地开发(将秘密排除在 repostiory 之外)和一个额外的 appsettings.Test.json
(还有另一个 git 忽略了 appsettings.LocalTest.json
,带有额外的测试设置,例如不同的数据库连接)。
当我 运行 时,集成测试控制台充斥着日志语句。奇怪的是,似乎并没有记录所有内容,例如我看不到任何请求日志。但是我可以多次看到数据库迁移的日志,如下所示:
[09:57:38 INF Microsoft.EntityFrameworkCore.Migrations] Applying migration '20210224073743_InitialSchema'
或者这个
[09:57:40 DBG lJty8ESu24x-MY6n4EYr Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler] Successfully validated the token.
.
我尝试了很多方法,比如将最低日志级别设置为 Fatal
或直接用新记录器替换 Log.Logger。
应用程序本身正在使用注入的 ILogger 而不是静态 Log.Logger。谁能指导我如何解决这个问题或下一步我可以尝试什么?
日志记录似乎符合我的 appsettings.Test.json
文件中的设置,当我降低调试的最低级别时,我可以看到更多日志打印在测试 运行 上。但是,为什么即使我将最低级别设置为致命,也会记录迁移消息?
我想我已经做到了。在您的 CustomWebApplicationFactory
中,将其放入您的 ConfigureWebHost
:
[CollectionDefinition("WebApplicationFactory")]
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup>
where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
#pragma warning disable CS0618
builder.UseSerilog((_, _) => { });
#pragma warning restore CS0618
// ... other customizations
base.ConfigureWebHost(builder);
}
}
它会抱怨该方法已过时,但这对我有用,它会停止调用我原来的 Serilog 配置,并且只会停止记录任何内容。 如果你愿意,我相信你也可以使用它来更改配置。