本文介绍如何在.NET 6 API中使用Serilog的APM。
1. 引用Serilog相关的packages
csharp
<PackageReference Include="Elastic.Apm.SerilogEnricher" Version="8.11.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="10.0.0" />
2. appsettings.json添加配置文件
csharp
"SeriLog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File", "Serilog.Sinks.Elasticsearch" ],
"MinimumLevel": {
"Default": "Information"
},
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "WithElasticApmCorrelationInfo" ]
},
"SerilogSupplementer": {
"WriteToFile": true,
"LogFilePath": "c:/temp/logs/mes-api/mes-api-.json",
"FileRestrictToMinimumLevel": "Information",
"FileSizeLimitBytes": 10485760, //10 MB
"RetainedFileTimeLimit": "10.00:00", // Retain files for a maximum of 10 days 0 Hrs 0 Mins
"rollOnFileSizeLimit": true,
"EnrichFromLogContext": true,
"MinimumLevelControlledBy": null,
"EnrichProperties": {
"Application": "mes-api",
"Environment": "Development"
},
"MinimumLevelOverrides": {
"Microsoft.NetCore": "Warning",
"Elastic.Apm": "Warning"
},
"EnrichWithExceptionDetails": true,
"AddElasticApmCorrelationInfo": true,
"WriteToConsole": true,
"ConsoleFormat": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level}] [{SourceContext}] [{EventId}] {Message}{NewLine}{Exception}"
},
"ElasticApm": {
"ServerUrls": "http://localhost:8200", // URL of your APM server
"ServiceName": "mes-api",
"Environment": "Development",
"CentralConfig": false,
"LogLevel": "Error"
},
3. 注册
3.1 创建SerilogSupplementerConfiguration.cs
csharp
using Serilog.Core;
using Serilog.Events;
namespace MES.API.Logging;
public class SerilogSupplementerConfiguration
{
public bool WriteToFile { get; set; }
public string LogFilePath { get; set; } = string.Empty;
public LogEventLevel FileRestrictedToMinimumLevel { get; set; } = LogEventLevel.Debug;
public bool EnrichFromLogContext { get; set; } = true;
public LoggingLevelSwitch? MinimumLevelControlledBy { get; set; }
public Dictionary<string,object> EnrichProperties { get; set; } = new();
public Dictionary<string,LogEventLevel> MinimumLevelOverrides { get; set; } = new();
public bool EnrichWithExceptionDetails { get; set; } = true;
public bool AddElasticApmCorrelationInfo { get; set; }
public bool WriteToConsole { get; set; }
public string ConsoleFormat { get; set; } = string.Empty;
public string RetainedFileTimeLimit { get; set; } = "10.00:00"; //Default to 10 days
public long FileSizeLimitBytes { get; set; } = 10485760; //Default to 10 MB
public bool RollOnFileSizeLimit { get; set; }
}
3.2 注册
csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using AlterDomus.a360.Core.Logging;
using Serilog;
using Serilog.Exceptions;
using Elastic.Apm.SerilogEnricher;
using Serilog.Formatting.Elasticsearch;
using Serilog.Exceptions.Core;
namespace MES.API.Extensions;
public static class MESExtensions
{
/// <summary>
/// Add Serilog ILogger using configuration. SerilogSupplementerConfiguration will be read from configuration if exists.
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="logArgs"></param>
public static IServiceCollection AddSerilog(this IServiceCollection services, IConfiguration configuration)
{
var serilogConfig = new Serilog.LoggerConfiguration()
.ReadFrom.Configuration(configuration);
var serilogSupplementer = configuration.GetSection("SerilogSupplementer").Get<SerilogSupplementerConfiguration>() ?? new();
var retainedFileTimeLimit = TimeSpan.Parse(serilogSupplementer.RetainedFileTimeLimit);
var fileSizeLimitBytes = serilogSupplementer.FileSizeLimitBytes;
var rollOnFileSizeLimit = serilogSupplementer.RollOnFileSizeLimit;
if (serilogSupplementer != null)
{
if (serilogSupplementer.MinimumLevelControlledBy != null)
{
serilogConfig.MinimumLevel.ControlledBy(serilogSupplementer.MinimumLevelControlledBy);
}
foreach (var kvp in serilogSupplementer.MinimumLevelOverrides)
{
serilogConfig.MinimumLevel.Override(kvp.Key, kvp.Value);
}
if (serilogSupplementer.EnrichFromLogContext)
{
serilogConfig.Enrich.FromLogContext();
}
if (serilogSupplementer.AddElasticApmCorrelationInfo)
{
serilogConfig.Enrich.WithElasticApmCorrelationInfo();
}
if (serilogSupplementer.WriteToConsole || !string.IsNullOrEmpty(serilogSupplementer.ConsoleFormat))
{
if (string.IsNullOrEmpty(serilogSupplementer.ConsoleFormat))
{
serilogConfig.WriteTo.Console();
}
else
{
serilogConfig.WriteTo.Console(outputTemplate: serilogSupplementer.ConsoleFormat);
}
}
if (serilogSupplementer.EnrichWithExceptionDetails)
{
serilogConfig.Enrich.WithExceptionDetails(
new DestructuringOptionsBuilder().WithDefaultDestructurers()
);
}
if (serilogSupplementer.WriteToFile)
{
serilogConfig.WriteTo.File(
new ElasticsearchJsonFormatter(renderMessage: true, renderMessageTemplate: false),
path: serilogSupplementer.LogFilePath,
restrictedToMinimumLevel: serilogSupplementer.FileRestrictedToMinimumLevel,
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: fileSizeLimitBytes,
retainedFileTimeLimit: retainedFileTimeLimit,
rollOnFileSizeLimit: rollOnFileSizeLimit,
shared: true
);
}
foreach (var kvp in serilogSupplementer.EnrichProperties)
{
serilogConfig.Enrich.WithProperty(kvp.Key, kvp.Value);
}
if (!serilogSupplementer.EnrichProperties.ContainsKey("UserName"))
{
serilogConfig.Enrich.WithProperty("UserName", Environment.UserName);
}
}
ILogger logger = serilogConfig.CreateLogger();
services.AddSingleton<Serilog.ILogger>(logger);
return services;
}
}
3.3 在program.cs使用
csharp
builder.Services.AddSerilog(builder.Configuration);
builder.Host.UseSerilog();
builder.Services.AddAllElasticApm();