NET Logging Serilog Tutorial

Posted by Andy Feng on July 18, 2025

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)抽象,不绑定具体实现| |**Serilog**|实现 ILogger 接口,提供结构化日志、模板化输出、文件/数据库/云 Sink 支持等| 组合结构图

应用代码(依赖 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接口 中等,需额外适配接口注入
🔄 生态 配合 SeqGrafana 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 InsightsElasticSearchSeq 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,支持中间件、过滤器等| |支持作用域|可记录请求上下文,日志中附带 TraceId、RequestId 等信息| 局限性

局限 / 特点 说明
不直接支持结构化日志输出 需要 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

    • 控制台/文件:ConsoleFileRollingFile
    • 数据库:SQLServerPostgreSQLMongoDB
    • 云服务:AzureTableStorage, `AWSCloudWatch
    • 日志系统:ElasticsearchSeqGraylog
    • 消息队列:RabbitMQKafka

| 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.cstry-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-finallyOnExit 方法|

Reference