Introduction
.NET 生态中有多个流行的免费日志系统。下面是一份常见日志库的对比表,涵盖功能、性能、易用性、维护活跃度、生态支持等维度: 主流 .NET 日志库对比表(免费开源)
|名称|优点|缺点|GitHub Stars / 维护情况|市场使用情况 / 案例| |—|—|—|—|—| |Serilog|- 基于结构化日志(Structured Logging)- 强大的 Sink 支持(输出目标)- 文档全、生态好- 与 ASP.NET Core 无缝集成|- 默认配置略复杂- 日志格式较为庞大(对性能略影响)|⭐️ 6.4k+活跃维护中|微软官方文档推荐,主流框架支持| |NLog|- 配置简单灵活(支持 XML 和代码)- Sink 支持丰富- 性能好|- 结构化日志支持不如 Serilog- 配置系统略显老旧|⭐️ 5.5k+活跃维护中|大量传统企业、桌面系统使用| |log4net|- 历史悠久,Apache 基金会项目- 成熟稳定|- 项目过于老旧- 很少更新- 不支持 .NET Core 的新特性|⭐️ 1.6k+基本停止维护|传统 .NET Framework 项目| |Microsoft.Extensions.Logging|- 官方内置支持- 与 ASP.NET Core 紧密整合- 可作为日志抽象层|- 需要配合第三方 Sink(如 Serilog)使用才强大|.NET 官方包持续更新|ASP.NET Core 默认日志框架| |ElmahCore|- Web 异常捕获友好,自动页面记录- 支持 MVC / Core- 配置简单|- 更适合做 Web 异常审计- 非通用日志记录器|⭐️ 0.5k+活跃维护中|适合做补充日志或错误追踪系统| |Seq(配合 Serilog)|- 图形化结构化日志平台(Web UI)- Serilog 最佳拍档|- Web UI 有免费版和付费版- 本质是日志平台,不是库|⭐️ 5.5k+(Seq 官方)|DevOps 团队常用,可本地部署| 推荐选择建议
|场景|推荐日志库|理由| |—|—|—| |一般 ASP.NET Core 项目|Serilog + 官方 Logging|Serilog 写日志,官方 Logging 做抽象层,配置灵活,功能强| |桌面应用(WinForms/WPF)|NLog|简单配置,输出文件、Windows 日志都很好用| |只需要一个异常监控后台|ElmahCore|开箱即用,支持页面查看,适合 MVC 项目| |老项目,基于 .NET Framework 4.x|log4net|稳定兼容,虽然老旧但够用| |云原生 / DevOps / 日志平台整合|Serilog + Seq / Grafana|Serilog 输出结构化日志,配合 UI 平台方便调试和运维|
推荐组合
Microsoft.Extensions.Logging + Serilog
|组成|作用说明|
|—|—|
|Microsoft.Extensions.Logging|微软官方的日志接口(ILogger
应用代码(依赖 ILogger<T>)
│
▼
Microsoft.Extensions.Logging(抽象)
│
▼
Serilog(实现 + 配置)
│
└── Sink(文件、控制台、数据库、Seq、ELK、ApplicationInsights...)
或者 Microsoft.Extensions.Logging + NLog
Serilog vs NLog 全面对比
对比项 | Serilog | NLog |
---|---|---|
🌟 核心特点 | 结构化日志,支持语义化日志字段 | 灵活配置,支持传统 XML 配置 |
🧱 配置方式 | 代码配置为主(也支持 JSON) | XML 配置为主(也支持代码配置) |
🎯 Sink 支持 | 丰富(控制台、文件、Seq、Elasticsearch) | 丰富(文件、数据库、邮件、网络等) |
📦 包大小 | 相对小,依赖较少 | 稍大一些,内置较多功能 |
📈 性能 | 高性能,支持异步 Sink | 高性能,写入文件速度极快 |
🛠️ 易用性 | 初学者稍微需要理解结构化概念 | 更偏传统日志格式,简单上手 |
🧩 与 ASP.NET Core | 完美支持(UseSerilog) | 也支持(AddNLog),但需更多配置 |
🪵 输出格式 | 支持 JSON / Key-Value / 自定义模板 | 文本为主,也支持 JSON |
📋 文档 | 非常完善,样例丰富 | 也很完善,但偏向老派写法 |
🧪 单元测试友好 | 高,日志可注入ILogger接口 | 中等,需额外适配接口注入 |
🔄 生态 | 配合 Seq 、Grafana Loki 等更活跃 |
配合老项目迁移,适合桌面老项目 |
👴 历史 | 新一代日志库(自 2013 起) | 经典老牌库(自 2009 起) |
🧑💻 社区维护 | 非常活跃(GitHub Star 6k+) | 活跃(Star 5.5k+) |
Microsoft.Extensions.Logging
Microsoft.Extensions.Logging 是微软为 .NET 提供的一个日志抽象框架,是.NET Core/5+
及ASP.NET Core
的默认日志系统。
它定义了一个统一的日志 API(接口),让你可以在不依赖具体日志库(如 Serilog、NLog、log4net 等)的情况下使用日志功能。
核心理念:日志抽象层
你在代码中只依赖:
ILogger<T>
而真正的日志实现(写文件、写控制台、写数据库)可以通过注册不同的“Provider”来替换。面向接口编程,便于替换/测试/跨平台复用。
使用推荐
-
推荐用途:做为日志接口层,统一日志调用方式
-
推荐搭配:
- 控制台测试项目:
AddConsole()
即可 - Web 项目或正式生产环境:配合
Serilog
使用 - 云原生:配合
Application Insights
、ElasticSearch
、Seq
Sink包结构
- 控制台测试项目:
|包名|说明|
|—|—|
|Microsoft.Extensions.Logging.Abstractions
|提供 ILogger
接口和基础结构|
|Microsoft.Extensions.Logging
|提供简单的控制台 Provider(不依赖 ASP.NET)|
|Microsoft.Extensions.Logging.Console
|控制台日志 Provider|
|Microsoft.Extensions.Logging.Debug
|输出到 Visual Studio 的输出窗口|
|Microsoft.Extensions.Logging.EventLog
|写入 Windows EventLog|
|Microsoft.Extensions.Logging.AzureAppServices
|Azure 云环境日志输出支持|
|第三方 Provider(如 Serilog、NLog)|实现了 ILoggerProvider
接口|
核心接口介绍
| 类型 | 说明 |
| —————– | ——————————————————- |
| ILogger<T>
| 泛型日志接口,T 为上下文(如类名) |
| ILogger
| 非泛型版本,适合工厂模式动态使用 |
| ILoggerFactory
| 创建日志对象,添加 Provider |
| ILoggingBuilder
| 用于注册各种 Provider |
| ILoggerProvider
| 提供具体日志实现(Serilog、NLog) |
| LogLevel
| 日志等级枚举(Trace, Debug, Info, Warn, Error, Critical, None) |
Console example
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var serviceProvider = new ServiceCollection()
.AddLogging(builder =>
{
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
})
.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Hello from Microsoft.Extensions.Logging!");
日志等级说明(LogLevel
)
| 等级 | 含义 | 是否生产环境推荐 |
| ————- | —————– | ——– |
| Trace
| 最详细的日志,调试级调用栈 | ❌ |
| Debug
| 调试信息,适用于开发调试 | ❌ |
| Information
| 一般性业务信息,系统状态、流程日志 | ✅ |
| Warning
| 潜在错误、异常,但不影响业务 | ✅ |
| Error
| 业务失败、系统异常(可恢复) | ✅ |
| Critical
| 严重故障、系统崩溃前兆 | ✅ 强烈建议记录 |
| None
| 关闭所有日志记录 | 一般不使用 |
与 ASP.NET Core 的集成方式
ASP.NET Core 自动注册了 ILogger<T>
,你只需要构造函数注入即可:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("访问 Index 页面");
return View();
}
}
日志输出通过配置文件 appsettings.json
控制:
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
日志 Provider 替换机制
你可以使用任何日志系统作为 Provider,只要实现了 ILoggerProvider
接口,比如:
|第三方日志库|使用方式|
|—|—|
|Serilog|.UseSerilog()
|
|NLog|.AddNLog()
(需要 Microsoft.Extensions.Logging.NLog
)|
|log4net|.AddLog4Net()
|
优缺点
优点总结
|优点|说明|
|—|—|
|抽象层设计|解耦日志逻辑与日志实现,便于替换和单元测试|
|内置多种 Provider|控制台、调试窗口、EventLog、Azure AppService 等|
|配置灵活|支持代码配置和 JSON 配置|
|与 ASP.NET Core 深度集成|自动注入 ILogger
局限 / 特点 | 说明 |
---|---|
不直接支持结构化日志输出 | 需要 Serilog/NLog 等第三方库补充 |
自带 Provider 功能较简单 | 控制台输出有限,缺少异步写入、日志压缩等功能 |
无图形化界面 / 查询功能 | 不包含查看系统(需配合第三方如 Seq/ELK) |
Serilog
Serilog 是 一款专注于结构化日志记录的.NET日志库,以简洁、灵活和高性能著称。
核心思想:将日志视为事件流(Event Stream),每条日志包含属性集(键值对),而非纯文本。
关键包:
Serilog
(核心库)Serilog.Sinks.Console
/File
/Elasticsearch
等(输出目标)Serilog.Enrichers
(上下文增强)
ASP.NET Core WebAPI项目,原生支持。
WPF / WinForms 项目。可复用 Serilog 配置,但需要手动接管 ILogger
注册。
核心组件
|组件|作用|
|—|—|
|Logger|记录日志的主接口,通过Log.Logger
全局访问|
|Sink|日志输出目标(如文件、数据库、云服务)|
|Enricher|为日志添加上下文属性(如机器名、线程ID)|
|Formatter|控制日志格式(JSON、纯文本等)|
|Filter|动态过滤日志事件|
丰富的Sinks生态系统
Sink 是 Serilog 日志系统的 输出目标,决定日志事件最终被写入到哪里。
-
官方/社区支持的Sinks:
- 控制台/文件:
Console
,File
,RollingFile
- 数据库:
SQLServer
,PostgreSQL
,MongoDB
- 云服务:
AzureTableStorage
, `AWSCloudWatch - 日志系统:
Elasticsearch
,Seq
,Graylog
- 消息队列:
RabbitMQ
,Kafka
- 控制台/文件:
| Sink 类型 | 包名 | 示例用途 |
| ——————- | ———————————– | —————— |
| 控制台 | Serilog.Sinks.Console
| Debug、控制台开发 |
| 文件(滚动) | Serilog.Sinks.File
| 写入日志文件,支持按天滚动 |
| Seq | Serilog.Sinks.Seq
| 配合日志查看平台,支持 Web UI |
| Elasticsearch | Serilog.Sinks.Elasticsearch
| 日志对接 ELK |
| MSSQL / SQLite | Serilog.Sinks.MSSqlServer
| 写入关系型数据库 |
| ApplicationInsights | Serilog.Sinks.ApplicationInsights
| Azure 云平台 |
| Telegram | Serilog.Sinks.Telegram
| 错误消息推送 |
自定义 Sink 开发
public class CustomSink : ILogEventSink
{
public void Emit(LogEvent logEvent)
{
var message = logEvent.RenderMessage();
MyStorageSystem.Save(message); // 自定义存储逻辑
}
}
// 注册
.WriteTo.Sink(new CustomSink())
包装现有sink,装饰器模式
public class BufferedSink : ILogEventSink
{
private readonly ILogEventSink _innerSink;
private readonly List<LogEvent> _buffer = new();
public BufferedSink(ILogEventSink innerSink) => _innerSink = innerSink;
public void Emit(LogEvent logEvent)
{
_buffer.Add(logEvent);
if (_buffer.Count >= 10)
{
foreach (var evt in _buffer) _innerSink.Emit(evt);
_buffer.Clear();
}
}
}
上下文增强(Enrichers)
内置Enrichers:
.Enrich.WithMachineName() // 添加机器名
.Enrich.WithThreadId() // 添加线程ID
.Enrich.WithCorrelationId() // 添加请求关联ID
自定义属性:
using (LogContext.PushProperty("TransactionId", Guid.NewGuid()))
{
Log.Information("Processing payment");
}
例子,Enricher自动添加上下文字段
.WriteTo.Console()
.Enrich.WithProperty("Application", "MyWpfApp")
.Enrich.FromLogContext()
会在每条日志中自动添加:
{ "Application": "MyWpfApp" }
使用方式
可以用 DI 注入 ILogger<T>
,配合 Microsoft.Extensions.DependencyInjection
。
public class MyService : IMyService
{
private readonly ILogger<MyService> _logger;
public MyService(ILogger<MyService> logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.Information("窗体启动:{Time}", DateTime.Now);
_logger.Error(ex, "发生错误:{Message}", ex.Message);
}
}
直接使用静态类Log
。
Log.Information("窗体启动:{Time}", DateTime.Now);
Log.Error(ex, "发生错误:{Message}", ex.Message);
Tips
始终使用LogContext
:为请求链添加关联ID。
避免字符串拼接:利用消息模板传递属性。
生产环境配置:
.WriteTo.Console(new JsonFormatter())
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(...))
.AuditTo.File("logs/audit.log") // 审计日志
异常记录
try { ... }
catch (Exception ex) {
Log.Error(ex, "Operation failed for {UserId}", userId);
}
Serilog vs NLog vs log4net
|特性|Serilog|NLog|log4net| |—|—|—|—| |结构化日志|原生支持|需手动配置|不支持| |性能|高(异步优化)|极高|中等| |配置方式|代码/JSON|XML/代码|XML| |扩展生态|200+ Sinks|100+ Targets|较少| |ASP.NET Core集成|官方推荐|需适配|不推荐|
最小可运行 Serilog 日志代码例子
配置:
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
使用
Log.Information("应用启动成功于 {Time}", DateTime.Now);
Log.Error(new Exception("示例异常"), "出错啦");
关闭
Log.CloseAndFlush();
推荐使用方式总结
| 场景 | 推荐使用方式 |
| ———— | ————————————————– |
| 控制台/桌面项目 | .WriteTo.File(...)
+ .WriteTo.Console()
|
| ASP.NET Core | UseSerilog()
+ JSON 配置或代码配置 |
| 生产环境 | .MinimumLevel.Information()
+ 文件 / Seq / DB Sink |
| 异常日志 | Log.Error(ex, "出错啦:{Message}", ex.Message)
|
注意事项
|注意点|建议|
|—|—|
|关闭日志|程序退出前调用 Log.CloseAndFlush()
,避免日志未写完丢失|
|多线程性能|使用异步 Sink(如 Async Sink 或 DurableFile Sink)|
|日志格式臃肿|合理设置字段数量,避免嵌套深的结构性数据|
|日志过大|配置日志清理策略(如每天滚动、保留 N 天)|
Web项目 (.NET Core)
安装nuget
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
Program.cs 配置
using Serilog;
var builder = WebApplication.CreateBuilder(args);
// 设置 Serilog
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
// 替换默认 LoggerFactory
builder.Host.UseSerilog();
// or
builder.Host.UseSerilog((ctx, lc) =>
{
lc.ReadFrom.Configuration(ctx.Configuration); // 从 appsettings.json 读取配置
});
// or
builder.Host.UseSerilog((ctx, config) =>
{
config.ReadFrom.Configuration(ctx.Configuration)
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}");
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthorization();
app.MapControllers();
app.Run();
控制器或服务中使用:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Index page visited at {Time}", DateTime.Now);
return View();
}
}
高级配置(过滤器、按类别记录)
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) // 降低系统日志级别
.MinimumLevel.Override("YourApp.Services", LogEventLevel.Debug) // 自定义命名空间级别
.WriteTo.Console()
.CreateLogger();
高级配置(JSON配置)
appsettings.json
:
{
"Serilog": {
"Using": ["Serilog.Sinks.Console"],
"MinimumLevel": "Information",
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": { "path": "logs/log.txt" }
}
],
"Enrich": ["FromLogContext", "WithMachineName"]
}
}```
推荐日志输出结构(文件)
```plaintext
/logs/
log-20250719.txt
log-20250720.txt
使用滚动策略 RollingInterval.Day
自动每天生成新文件。
Desktop项目
📁 项目结构预览
MyWpfApp/
├── App.xaml
├── App.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── ViewModels/
│ └── MainViewModel.cs
├── Services/
│ └── ServiceRegistration.cs
├── logs/
│ └── log-*.txt
安装 NuGet 包
desktop项目
dotnet add package Serilog
dotnet add package Serilog.Extensions.Logging //让 Serilog 适配微软的 `ILogger`
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Sinks.Console
Serilog.Settings.Configuration
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Logging
Service项目
配置并初始化(在 App.xaml.cs 或 Program.cs)
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using System;
using System.Windows;
using MyWpfApp.ViewModels;
namespace MyWpfApp
{
public partial class App : Application
{
public static IServiceProvider Services { get; private set; }
protected override void OnStartup(StartupEventArgs e)
{
var serviceCollection = new ServiceCollection();
// 初始化 Serilog
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
//.WriteTo.Console()
.WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
// 注册日志
serviceCollection.AddLogging(builder =>
{
builder.ClearProviders(); // 清除默认
builder.AddSerilog(); // 使用 Serilog
});
// 注册其他服务(ViewModel、服务等)
ServiceRegistration.ConfigureServices(serviceCollection);
Services = serviceCollection.BuildServiceProvider();
// 启动主窗口
var mainWindow = new MainWindow
{
DataContext = Services.GetRequiredService<MainViewModel>()
};
mainWindow.Show();
base.OnStartup(e);
}
}
}
ServiceRegistration.cs:注册服务和 ViewModel
using Microsoft.Extensions.DependencyInjection;
using MyWpfApp.ViewModels;
namespace MyWpfApp.Services
{
public static class ServiceRegistration
{
public static void ConfigureServices(IServiceCollection services)
{
// 注册 ViewModel
services.AddSingleton<MainViewModel>();
}
}
}
进阶配置
添加config/logging.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"Enrich": [ "FromLogContext" ],
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/log-.txt",
"rollingInterval": "Day",
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
]
}
}
LoggingConfiguration.cs加载并初始化
public static class LoggingConfiguration
{
public static void ConfigureLogging(IServiceCollection services)
{
// 1. 加载 logging.json 配置
var loggingConfigPath = Path.Combine(AppContext.BaseDirectory, "config", "logging.json");
if (!File.Exists(loggingConfigPath))
throw new FileNotFoundException("未找到日志配置文件: logging.json");
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("config/logging.json", optional: false, reloadOnChange: true)
.Build();
// 2. 创建 Serilog 全局 logger
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(configuration)
.CreateLogger();
// 3. 注册 Microsoft.Extensions.Logging 使用 Serilog
services.AddLogging(loggingBuilder =>
{
loggingBuilder.ClearProviders(); // 清除默认提供者
loggingBuilder.AddSerilog(); // 注入 Serilog
});
// 可选:调试输出
Log.Information("Serilog 日志系统已初始化。");
}
}
在 ServiceConfiguration.cs
开头调用注册
public static void Configure(IServiceCollection services)
{
// ✅ 注册日志系统(最早执行)
LoggingConfiguration.ConfigureLogging(services);
// ✅ 注册配置系统
AppConfigLoader.LoadAndRegisterAll(services);
// 其他服务注册逻辑...
....
services.AddSingleton<MainViewModel>();
....
App.xaml.cs
退出前释放日志资源
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
// ✅ 强制刷新并关闭 Serilog
Serilog.Log.CloseAndFlush();
}
使用logger
MainViewModel.cs:使用 ILogger<T>
using Microsoft.Extensions.Logging;
using System;
namespace MyWpfApp.ViewModels
{
public class MainViewModel
{
private readonly ILogger<MainViewModel> _logger;
public MainViewModel(ILogger<MainViewModel> logger)
{
_logger = logger;
_logger.LogInformation("MainViewModel 初始化于 {Time}", DateTime.Now);
}
public void DoSomething()
{
_logger.LogDebug("执行 DoSomething 方法...");
try
{
// 模拟错误
throw new Exception("模拟异常");
}
catch (Exception ex)
{
_logger.LogError(ex, "发生异常:{Message}", ex.Message);
}
}
}
}
MainWindow.xaml.cs:调用 ViewModel 的日志方法
using System.Windows;
using MyWpfApp.ViewModels;
namespace MyWpfApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (DataContext is MainViewModel vm)
{
vm.DoSomething();
}
}
}
}
MainWindow.xaml:添加一个按钮测试日志
<Window x:Class="MyWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="MainWindow" Height="200" Width="300">
<Grid>
<Button Content="测试日志" Click="Button_Click" Width="100" Height="30" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Window>
日志输出(logs/log-20250719.txt)
[10:12:23 INF] MainViewModel 初始化于 2025-07-19 10:12:23
[10:12:27 DBG] 执行 DoSomething 方法...
[10:12:27 ERR] 发生异常:模拟异常
System.Exception: 模拟异常
at MyWpfApp.ViewModels.MainViewModel.DoSomething() in ...
使用日志注意:Serilog 中 Log
是静态类(除非你用依赖注入),在桌面程序里可以直接用。
Log.Information("窗体启动:{Time}", DateTime.Now);
Log.Error(ex, "发生错误:{Message}", ex.Message);
注意:Serilog 中 Log
是静态类(除非你用依赖注入),在桌面程序里可以直接用。
FAQ
Log.CloseAndFlush() 放在哪里?
Log.CloseAndFlush()
是 Serilog 的重要清理方法,用于在程序退出前刷新缓冲区并安全关闭日志资源,防止日志丢失或文件未关闭。
为什么需要调用它?
Serilog 中某些 Sink(如 FileSink
, AsyncSink
, DurableSink
)是缓冲写入,如果不调用 CloseAndFlush()
,程序异常退出时:
- 日志可能未写入磁盘
- 文件句柄未释放
- 控制台输出可能不完整
不同项目类型中 Log.CloseAndFlush()
的推荐位置:
| 项目类型 | 推荐调用位置 |
| ————————- | —————————— |
| 控制台应用 | Main
方法末尾,try-finally
中 |
| WPF / WinForms | App.xaml.cs
中的 OnExit()
方法 |
| ASP.NET Core | Program.cs
的 try-finally
中 |
| 后台服务 / Worker Service | Main
方法或 IHost.Run()
外部 |
示例:控制台应用中使用
class Program
{
static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.File("logs/log.txt")
.CreateLogger();
try
{
Log.Information("程序启动");
// 执行业务逻辑...
}
catch (Exception ex)
{
Log.Fatal(ex, "程序异常退出");
}
finally
{
Log.CloseAndFlush(); // 必须放在最后
}
}
}
示例:WPF 项目中使用
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.File("logs/log.txt")
.CreateLogger();
Log.Information("WPF 应用启动");
base.OnStartup(e);
}
protected override void OnExit(ExitEventArgs e)
{
Log.Information("WPF 应用关闭");
Log.CloseAndFlush(); // 放在这里
base.OnExit(e);
}
}
.NET 5及早期版本示例(使用CreateHostBuilder)
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();
try
{
Log.Information("启动 WebHost");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host 启动失败");
return 1;
}
finally
{
Log.CloseAndFlush(); // ✅ 最后一定调用
}
}
.NET 6+(使用Program.cs)
using Serilog;
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/web-log-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
try
{
Log.Information("应用启动中...");
var builder = WebApplication.CreateBuilder(args);
// 替换默认 Logger
builder.Host.UseSerilog();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 中间件注册
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
Log.Information("应用正常运行中");
}
catch (Exception ex)
{
Log.Fatal(ex, "应用启动失败!");
}
finally
{
Log.CloseAndFlush(); // 🔥 必须确保在所有日志调用后释放资源
}
解释
|部分|说明|
|—|—|
|Log.Logger = ...
|初始化 Serilog 的全局静态实例|
|builder.Host.UseSerilog()
|注册 Serilog 为 .NET 日志系统的实现|
|Log.CloseAndFlush()
|确保所有日志刷新写入磁盘、资源释放,避免遗漏最后的日志|
|try-catch-finally
包裹整个程序启动流程|推荐做法,可捕捉全局初始化异常并记录|
说明
ASP.NET Core 项目中 Log.CloseAndFlush 最佳实践
|操作|推荐做法|
|—|—|
|初始化 Log.Logger
|在 Program.cs
的最前面|
|注册 UseSerilog()
|替换内置 ILoggerFactory|
|包裹 builder.Build().Run()
|使用 try-catch-finally
|
|finally 中调用 CloseAndFlush
|确保所有日志写入、文件关闭、资源释放|
❗ 如果不调用会怎样?
- 程序最后几行日志可能写不进去
- 文件句柄可能未释放
- 某些异步 Sink(如 ApplicationInsights)可能数据丢失
✅ 总结
|操作|是否必须|推荐做法|
|—|—|—|
|Log.CloseAndFlush()
|✅ 是|程序结束前 必须调用一次|
|放置位置|✅ 是|try-finally
或 OnExit
方法|