在 .NET 项目中使用 NLog 记录日志的好处
NLog 是一个功能强大、灵活且高性能的日志记录框架,广泛应用于 .NET(包括 .NET Framework、.NET Core 和 .NET 5/6/7/8+)应用程序中。在 .NET 项目中集成 NLog 可显著提升系统的可观测性、可维护性和调试效率。以下是使用 NLog 的主要优势说明。
一、核心优势
1. 高性能与低开销
- NLog 经过高度优化,采用异步写入、缓冲和批处理机制,对应用程序性能影响极小。
- 支持异步日志(
async="true"),避免 I/O 阻塞主线程。
2. 灵活的配置方式
- 支持通过 XML 配置文件(如
nlog.config)进行外部化配置,无需重新编译代码即可调整日志行为。 - 也支持 代码方式配置,适用于动态场景或云原生环境。
3. 丰富的输出目标(Targets)
NLog 支持将日志同时写入多种目标,包括:
- 文件(按日期/大小自动滚动)
- 控制台
- 数据库(SQL Server、PostgreSQL 等)
- Windows 事件日志
- Elasticsearch、Graylog、Seq 等日志聚合平台
- 电子邮件、HTTP 端点、消息队列等
4. 强大的日志过滤与路由
- 可基于日志级别(Trace、Debug、Info、Warn、Error、Fatal)、日志来源(Logger 名称)、自定义条件进行精细路由。
- 支持“规则链”(Rules),实现复杂的日志分发逻辑。
5. 结构化日志支持
- 原生支持结构化日志(Structured Logging),便于与现代日志分析工具(如 Serilog + Seq、ELK)集成。
- 使用
${message}、${event-properties:item=...}等布局渲染器提取结构化字段。
6. 自动日志文件管理
- 内置日志文件归档策略:按时间(每日/每小时)、按大小(如 10MB)自动分割。
- 可配置最大保留文件数或总大小,防止磁盘爆满。
7. 跨平台兼容性
- 完全支持 .NET Standard,可在 Windows、Linux、macOS 上运行。
- 适用于 ASP.NET Core、WPF、WinForms、控制台应用、Azure Functions、MAUI 等各类 .NET 项目。
8. 活跃的社区与持续维护
- 开源(MIT 许可)、文档完善、更新频繁,拥有庞大的用户社区和丰富的插件生态。
二、典型应用场景
| 场景 | NLog 优势体现 |
|---|---|
| 生产环境监控 | 异步写入 + 文件滚动 + 错误日志高亮,保障系统稳定 |
| 微服务调试 | 结构化日志 + TraceID 关联,便于分布式追踪 |
| 合规审计 | 日志内容加密、防篡改配置、长期归档支持 |
| 开发阶段快速定位问题 | 控制台实时输出 + 详细 Debug 信息 |
三、简单示例
1. 安装 NuGet 包
dotnet add package NLog
dotnet add package NLog.Web.AspNetCore # ASP.NET Core 项目推荐
2. 创建配置文件 nlog.config
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
internalLogLevel="Info"
internalLogFile=".\internal-nlog.txt">
<!-- 定义日志输出的 target -->
<targets>
<!-- 输出到文件,按大小滚动 -->
<target name="logfile"
xsi:type="File"
fileName="logs/logfile.log"
layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}"
maxArchiveFiles="10"
archiveAboveSize="5242880"
archiveFileName="logs/archived/logfile.{#}.log"
archiveNumbering="Sequence"
keepFileOpen="false"
concurrentWrites="true"
enableFileDelete="true" />
<!-- 输出到文件,按大小滚动 -->
<target name="errorFile"
xsi:type="File"
fileName="logs/error.log"
layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}"
maxArchiveFiles="10"
archiveAboveSize="5242880"
archiveFileName="logs/archived/logfile.{#}.log"
archiveNumbering="Sequence"
keepFileOpen="false"
concurrentWrites="true"
enableFileDelete="true" />
<target name="console"
xsi:type="ColoredConsole"
layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}">
<!-- 为不同日志级别配置不同颜色 -->
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" />
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" />
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" />
</target>
</targets>
<rules>
<!-- 规则:所有日志都输出到 logfile -->
<logger name="*" minlevel="Info" writeTo="logfile" />
<logger name="*" minlevel="Debug" writeTo="console" />
<logger name="*" minlevel="Error" writeTo="errorFile" />
</rules>
</nlog>
3. 使用NLog
using FellowOakDicom;
using Microsoft.Extensions.Configuration;
using NLog;
namespace change_ts;
internal class Program
{
// 获取当前类日志记录器
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
static async Task Main()
{
// 在 DicomSetupBuilder 之前添加
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
// 创建 CancellationTokenSource 来处理 Ctrl+C 信号
var cancellationTokenSource = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true; // 阻止程序立即退出
Logger.Info("收到 Ctrl+C 信号,正在优雅关闭...");
cancellationTokenSource.Cancel();
};
try
{
new DicomSetupBuilder()
.RegisterServices(s =>
s.AddFellowOakDicom()
.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>())
.SkipValidation()
.Build();
// 确保日志目录存在
var logDir = Path.Combine(AppContext.BaseDirectory, "logs");
var archivedDir = Path.Combine(logDir, "archived");
if (!Directory.Exists(logDir))
Directory.CreateDirectory(logDir);
if (!Directory.Exists(archivedDir))
Directory.CreateDirectory(archivedDir);
Logger.Info("🚀 应用程序启动,NLog 日志配置成功!");
// 创建配置构建器来读取 appsettings.json
var configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
Console.WriteLine("XXXX启动中...");
Logger.Info("XXXX已启动,服务正在运行中... 按 Ctrl+C 退出");
// 等待取消信号
await Task.Delay(Timeout.Infinite, cancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
// 正常的取消操作,不需要记录错误
Logger.Info("应用程序正在关闭...");
}
catch (Exception ex)
{
// 记录内部异常到 NLog(如果配置了 internalLogFile 也会记录 NLog 自己的错误)
Logger.Error(ex, "程序因异常停止");
throw;
}
finally
{
// 确保所有日志都写入磁盘并关闭资源
LogManager.Shutdown();
}
}
}
运行程序时,NLog 会自动创建日志目录和文件,并记录应用程序的启动和运行信息。当程序接收到 Ctrl+C 信号时,NLog 会记录应用程序正在关闭的信息。如果程序因为异常而停止,NLog 会记录异常信息。