Serilog是.NET生态中最受欢迎的结构化日志记录框架之一,以其强大的日志处理能力和丰富的输出目标(Sinks)支持而著称。本文将全面介绍如何在C#项目中使用Serilog实现高效的日志记录。
一、Serilog核心概念
1.1 结构化日志 vs 传统日志
传统日志:
2023-08-15 14:30:45 [INFO] 用户12345登录成功
结构化日志(Serilog):
{
"Timestamp": "2023-08-15T14:30:45.123456Z",
"Level": "Information",
"Message": "用户 {UserId} 登录成功",
"Properties": {
"UserId": "12345",
"SourceContext": "AuthService",
"IPAddress": "192.168.1.100"
}
}
1.2 核心组件
组件 | 作用 |
---|---|
Logger | 日志记录器,用于创建和写入日志事件 |
Sink | 日志输出目标(控制台、文件、数据库等) |
Enricher | 日志丰富器,用于添加额外属性(线程ID、机器名等) |
Filter | 日志过滤器,控制哪些日志会被记录 |
Formatting | 控制日志输出的格式 |
二、基础安装与配置
2.1 安装NuGet包
# 基础包
dotnet add package Serilog
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
# ASP.NET Core集成
dotnet add package Serilog.AspNetCore
2.2 最小化配置示例
using Serilog;
// 创建Logger配置
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/myapp.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
try
{
Log.Information("应用程序启动");
// 记录结构化日志
var user = new { Id = 123, Name = "张三" };
Log.Information("用户 {@User} 登录成功", user);
}
catch (Exception ex)
{
Log.Fatal(ex, "应用程序启动失败");
}
finally
{
Log.CloseAndFlush(); // 确保所有日志都被写入
}
三、ASP.NET Core集成
3.1 完整集成示例
// Program.cs
using Serilog;
// 初始化Serilog配置
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.Enrich.FromLogContext()
.Enrich.WithMachineName()
.Enrich.WithProcessId()
.Enrich.WithThreadId()
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} {Properties}{NewLine}{Exception}")
.WriteTo.File("logs/webapp.log", rollingInterval: RollingInterval.Day)
.CreateLogger();
try
{
var builder = WebApplication.CreateBuilder(args);
// 使用Serilog替代默认日志系统
builder.Host.UseSerilog();
var app = builder.Build();
// 示例中间件,演示日志上下文
app.Use(async (context, next) =>
{
using (LogContext.PushProperty("RequestId", context.TraceIdentifier))
{
await next();
}
});
app.MapGet("/", () =>
{
Log.Information("首页被访问");
return "Hello World!";
});
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "应用程序终止");
}
finally
{
Log.CloseAndFlush();
}
3.2 控制器中使用示例
[ApiController]
[Route("[controller]")]
public class WeatherController : ControllerBase
{
private readonly ILogger<WeatherController> _logger;
public WeatherController(ILogger<WeatherController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
// 结构化日志记录
_logger.LogInformation("获取天气数据,城市: {City}", "北京");
return Ok(new { Temperature = 25, Summary = "Sunny" });
}
}
四、常用Sinks配置
4.1 控制台输出
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message}{NewLine}{Exception}{NewLine}")
4.2 文件输出
.WriteTo.File(
path: "logs/log-.txt",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 7,
shared: true,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
4.3 Seq日志服务器
.WriteTo.Seq("http://localhost:5341",
apiKey: "your-api-key",
controlLevelSwitch: new LoggingLevelSwitch(LogEventLevel.Verbose))
4.4 Elasticsearch
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200"))
{
IndexFormat = "myapp-logs-{0:yyyy.MM.dd}",
AutoRegisterTemplate = true,
NumberOfShards = 2,
NumberOfReplicas = 1
})
4.5 SQL数据库
.WriteTo.MSSqlServer(
connectionString: "Server=.;Database=Logs;Integrated Security=SSPI;",
tableName: "LogEvents",
autoCreateSqlTable: true)
五、日志丰富(Enrichers)
5.1 常用Enrichers
.Enrich.WithProperty("Application", "MyApp")
.Enrich.WithMachineName()
.Enrich.WithProcessId()
.Enrich.WithThreadId()
.Enrich.WithEnvironmentUserName()
.Enrich.WithAssemblyName()
.Enrich.WithAssemblyVersion()
.Enrich.WithMemoryUsage()
5.2 自定义Enricher
public class UserEnricher : ILogEventEnricher
{
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
{
var user = HttpContext.Current?.User?.Identity?.Name ?? "anonymous";
var property = propertyFactory.CreateProperty("CurrentUser", user);
logEvent.AddPropertyIfAbsent(property);
}
}
// 使用
.Enrich.With<UserEnricher>()
六、日志过滤
6.1 基本过滤
.Filter.ByIncludingOnly(Matching.FromSource("MyApp.Controllers"))
.Filter.ByExcluding(Matching.WithProperty<int>("StatusCode", code => code >= 200 && code < 300))
6.2 条件过滤
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(e => e.Level == LogEventLevel.Error)
.WriteTo.File("logs/errors.txt"))
七、高级配置技巧
7.1 动态日志级别
var levelSwitch = new LoggingLevelSwitch(LogEventLevel.Information);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.ControlledBy(levelSwitch)
.WriteTo.Console()
.CreateLogger();
// 运行时修改日志级别
levelSwitch.MinimumLevel = LogEventLevel.Warning;
7.2 子日志记录器
var diagnosticLogger = Log.ForContext("LoggerType", "Diagnostic");
diagnosticLogger.Information("系统诊断信息 {@Metrics}", metrics);
7.3 日志上下文
using (LogContext.PushProperty("TransactionId", Guid.NewGuid()))
using (LogContext.PushProperty("UserId", user.Id))
{
Log.Information("处理用户请求");
}
八、性能优化
8.1 异步日志
.WriteTo.Async(a => a.Console())
.WriteTo.Async(a => a.File("logs/async.txt"))
8.2 批量写入
.WriteTo.Sink(new PeriodicBatchingSink(
new FileSink("logs/batched.txt", new JsonFormatter(), null),
new PeriodicBatchingSinkOptions
{
BatchSizeLimit = 100,
Period = TimeSpan.FromSeconds(5)
}))
8.3 性能敏感场景
// 使用LoggerMessage模式
public static class LogEvents
{
public static readonly Action<ILogger, int, Exception?> ProcessingOrder =
LoggerMessage.Define<int>(
LogLevel.Information,
new EventId(1001, "ProcessingOrder"),
"处理订单 {OrderId}");
}
// 使用
LogEvents.ProcessingOrder(_logger, order.Id, null);
九、最佳实践
- 结构化日志优先:始终使用
{Property}
格式而非字符串拼接 - 合理选择Sinks:生产环境避免仅使用控制台输出
- 日志级别控制:生产环境通常设为Information级别
- 敏感信息保护:避免记录密码、密钥等
- 上下文丰富:添加请求ID、用户信息等
- 日志轮转策略:配置合理的日志保留策略
十、常见问题解决
10.1 日志丢失
- 确保程序退出时调用
Log.CloseAndFlush()
- 检查Sink配置是否有权限问题
- 使用同步Sink或增加缓冲时间
10.2 性能问题
- 启用异步写入
- 减少不必要的Enricher
- 提高最小日志级别
10.3 Seq连接问题
// 添加重试策略
.WriteTo.Seq("http://localhost:5341",
period: TimeSpan.FromSeconds(5),
batchPostingLimit: 100,
bufferBaseFilename: "logs/seq-buffer")
十一、扩展阅读
- Serilog官方文档
- Serilog.Settings.Configuration – 从appsettings.json配置
- Serilog.Expressions – 高级过滤和格式设置
- Serilog Analyzer – Roslyn分析器
通过本指南,您应该已经掌握了Serilog的核心用法和高级配置技巧。Serilog的强大之处在于其灵活性和可扩展性,建议根据项目需求选择合适的Sinks和Enrichers组合,构建最适合您应用的日志系统。