<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0-preview.2.22152.2" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0-preview.2.22152.2" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.2-dev-00890" />
</ItemGroup>
Using dependency injection to simply inject Serilog:
static IServiceProvider GetServiceProvider()
{
using var log = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.Enrich.FromLogContext()
.CreateLogger();
Serilog.Log.Logger = log;
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder.AddSerilog(dispose: true));
return serviceCollection.BuildServiceProvider();
}
Getting logs:
var loggerProvider = GetServiceProvider().GetService<ILoggerProvider>()!;
var logger = loggerProvider.CreateLogger(nameof(Program));
Creating a Scope
Considerations for log scopes:
Microsoft.Extensions.Logging provides the BeginScope API, which can be used to add any properties to log events within specific code areas. The API has two forms:
Method: IDisposable BeginScope
Extension method: IDisposable BeginScope(this ILogger logger, string messageFormat, params object[] args)
Template:
{SourceContext} {Timestamp:HH:mm} [{Level}] (ThreadId:{ThreadId}) {Message}{NewLine}{Exception} {Scope}
using (logger.BeginScope("Checking mail"))
{
// Scope is "Checking mail"
logger.LogInformation("Opening SMTP connection");
using (logger.BeginScope("Downloading messages"))
{
// Scope is "Checking mail" -> "Downloading messages"
logger.LogError("Connection interrupted");
}
}
Temporarily Adding a Property
You need to enable .Enrich.FromLogContext()
first.
using (LogContext.PushProperty("Test", 1))
{
// Process request; all logged events will carry `RequestId`
Log.Information("{Test} Adding {Item} to cart {CartId}", 1, 1);
}
A bit more complex nesting:
using (LogContext.PushProperty("A", 1))
{
log.Information("Carries property A = 1");
using (LogContext.PushProperty("A", 2))
using (LogContext.PushProperty("B", 1))
{
log.Information("Carries A = 2 and B = 1");
}
log.Information("Carries property A = 1, again");
}
ASP.NET Core Template Example
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.Elasticsearch", "Serilog.Sinks.Trace" ],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Default": "Debug",
"Microsoft": "Debug",
"System": "Debug",
"System.Net.Http.HttpClient": "Debug",
"Microsoft.AspNetCore": "Debug",
"Microsoft.Hosting.Lifetime": "Debug",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Debug"
}
},
"Enrich": [ "FromLogContext", "WithMachineName" ],
"WriteTo": [
{
"Name": "Console",
"Args": {
"outputTemplate": "{SourceContext} {Scope} {Timestamp:HH:mm} [{Level}] {Message:lj} {Properties:j} {NewLine}{Exception} "
}
},
The template is:
{SourceContext} {Scope} {Timestamp:HH:mm} [{Level}] {Message:lj} {Properties:j} {NewLine}{Exception}
Configuring log output:
var logger = _loggerfactory.CreateLogger<IFreeSql>();
fsql.Aop.CurdAfter += (s, e) =>
{
#pragma warning disable CA2017 // Parameter count does not match.
logger.LogInformation("[Freesql] operation log, operation type:{CurdType} TableName: {DbName} , Time: {ElapsedMilliseconds}ms , SQL: {Sql}", e.CurdType, e.Table.DbName, e.ElapsedMilliseconds, e.Sql, e.DbParms);
#pragma warning restore CA2017 // Parameter count does not match.
};
services.AddSingleton(fsql);
Effect:
In middleware with many nested calls, an example of injecting properties:
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var enrichers = new List<ILogEventEnricher>();
if (!string.IsNullOrEmpty(correlationId))
{
enrichers.Add(new PropertyEnricher(_options.EnricherPropertyNames.CorrelationId, correlationId));
}
using (LogContext.Push(enrichers.ToArray()))
{
await next(context);
}
}
文章评论