简介
NLog
是 .NET
平台上最流行的开源日志框架之一,特色是 灵活的配置、丰富的输出目标(Target
),以及 高性能 的异步写入能力。
适用场景:从控制台、文件、数据库、网络 到 ElasticSearch、Seq、Azure Table Storage
等各种日志收集后端。
支持文件、数据库(SQL/NoSQL
)、控制台、邮件、Elasticsearch
等 50+ 内置目标,并可通过插件扩展
原生兼容 JSON
格式,可输出带上下文信息的结构化日志,便于 ELK
等系统分析
性能优势
- 异步日志:默认异步写入,不影响主线程
- 缓冲机制:批量写入减少 I/O 操作
- 低开销:轻量级设计,最小化性能影响
安装
安装核心包
dotnet add package NLog
安装扩展包以集成 Microsoft.Extensions.Logging
dotnet add package NLog.Extensions.Logging
dotnet add package NLog.Web.AspNetCore
NLog.config 文件
在项目根目录添加一个 NLog.config
(或 nlog.config
)XML
文件,IDE 会自动识别并在运行时加载
appsettings.json 配置方案
{
"NLog": {
"targets": {
"logfile": {
"type": "File",
"fileName": "logs/${shortdate}.log"
}
},
"rules": [
{
"logger": "*",
"minLevel": "Debug",
"writeTo": "logfile"
}
]
}
}
C# 代码配置
var config = new LoggingConfiguration();
// 文件目标(按类名分目录)
var fileTarget = new FileTarget("timeLevelClassFile") {
FileName = "NLog/${shortdate}/${logger}.log",
Layout = "${longdate} ${level:uppercase=true} ${logger} ${message}",
ArchiveEvery = FileArchivePeriod.Day,
MaxArchiveDays = 30
};
// 异步包装提升性能
var asyncFileTarget = new AsyncTargetWrapper(fileTarget);
config.AddTarget(asyncFileTarget);
config.AddRule(LogLevel.Info, LogLevel.Fatal, asyncFileTarget);
// 应用配置
LogManager.Configuration = config;
核心组件
组件 | 作用 |
---|---|
Logger | 记录日志的入口,通常按类或命名空间获取:var log = LogManager.GetCurrentClassLogger(); |
Target | 日志输出目标,如 Console , File , Database , Mail , Network , Custom 等 |
Layout | 输出格式模板,可包含 ${longdate} ,${level} , ${message} , ${exception} 等内置渲染器 |
Rule | 日志规则,匹配 Logger 名称与级别后,将日志路由到一个或多个 Target |
Filter | 规则的高级过滤器,精细控制哪些消息被忽略或处理 |
在代码中使用
using NLog;
public class MyService
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
public void DoWork()
{
Logger.Trace("Trace message");
Logger.Debug("Debug message");
Logger.Info("Application started");
try
{
// ...
throw new InvalidOperationException("Oops");
}
catch (Exception ex)
{
Logger.Error(ex, "Unhandled exception in DoWork");
}
}
}
- Log Levels
Trace < Debug < Info < Warn < Error < Fatal
- 参数化日志
Logger.Info("Processing {OrderId} for {Customer}", order.Id, customer.Name);
常用布局渲染器
${longdate}
:完整日期时间${level}
:日志级别${logger}
:记录器名称${message}
:日志消息${exception}
:异常信息${callsite}
:调用位置(类名+方法名)${stacktrace}
:堆栈跟踪${machinename}
:机器名${processid}
:进程 ID${threadid}
:线程 ID${aspnet-request-url}
:ASP.NET 请求 URL
高级用法
结构化日志
Logger.Info("订单 {OrderId} 创建,用户 {UserId}", orderId, userId);
Mapped Diagnostics Context (MDC) / Mapped Diagnostics Logical Context (MDLC)
用于给日志记录中添加每个执行上下文的额外属性,比如请求 ID、用户 ID 等。
using NLog.MappedDiagnosticsLogicalContext;
public void HandleRequest(HttpContext ctx)
{
MDLC.Set("RequestId", ctx.TraceIdentifier);
MDLC.Set("User", ctx.User?.Identity?.Name ?? "anonymous");
Logger.Info("Handling request");
// …
MDLC.Remove("User");
}
在 Layout 中引用:
layout="${longdate}|${mdlc:item=RequestId}|${mdlc:item=User}|${message}"
异步日志记录
使用缓存
异步与缓存
AsyncWrapper
:将任何目标包裹为异步写入,避免日志写入阻塞业务线程
BufferingWrapper
:按条数或时间批量写入,适合数据库、网络等慢目标。
条件过滤
文件归档策略
自定义扩展
- 创建自定义
Target
[Target("CustomTarget")]
public sealed class CustomTarget : TargetWithLayout
{
protected override void Write(LogEventInfo logEvent)
{
string logMessage = RenderLogEvent(Layout, logEvent);
// 自定义输出逻辑(如发送到第三方服务)
SendToCustomService(logMessage);
}
}
- 配置自定义
Target
多环境配置
配置变量
避免日志记录开销
// 避免不必要的字符串拼接
if (Logger.IsDebugEnabled) {
Logger.Debug("复杂计算结果: {0}", ExpensiveCalculation());
}
// 或使用结构化日志
Logger.Debug("用户 {UserName} 登录", userName);
启用内部日志调试
按级别/命名空间过滤
数据库日志写入
结构化日志(Elasticsearch 集成)
故障转移:FallbackGroup 目标(主目标失败时切备用)
不同环境下集成
配置 Program.cs
// Program.cs
using NLog.Web;
var builder = WebApplication.CreateBuilder(args);
// 配置 NLog
builder.Logging.ClearProviders();
builder.Host.UseNLog();
// 其他配置...
配置 Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder => {
loggingBuilder.ClearProviders();
loggingBuilder.AddNLog();
});
}
控制台应用集成
// 手动加载配置
LogManager.LoadConfiguration("NLog.config");
依赖注入集成
// 在 Startup.cs 中注册
services.AddSingleton(LogManager.GetCurrentClassLogger());
注入 ILogger
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController(ILogger logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("访问首页");
return View();
}
}
中间件记录请求
app.Use(async (context, next) =>
{
var logger = context.RequestServices.GetRequiredService>();
var sw = Stopwatch.StartNew();
await next();
sw.Stop();
logger.LogInformation("请求 {Method} {Path} 耗时 {Elapsed}ms",
context.Request.Method,
context.Request.Path,
sw.ElapsedMilliseconds);
});
AsyncWrapper VS async="true"
特性 | AsyncWrapper |
async="true" (简化配置) |
---|---|---|
写法 | 显式使用 包裹 |
在普通 target 上设置 async="true" |
能力 | 更强大、更灵活(支持缓冲大小、丢弃策略、包装多个 target) | 语法更简洁,适合简单异步场景 |
推荐使用 | ✅ 更推荐生产使用 | ✅ 快速开发或小型项目时可用 |
背后机制
AsyncWrapper
NLog
提供的一个强大的异步包装器,适用于任何 target
。它使用独立的后台线程池写日志,并允许配置行为细节
overflowAction
:当队列满时的行为(默认Block
, 可设为Discard
或Grow
)batchSize
:每次最多写多少条timeToSleepBetweenBatches
:线程空闲等待时间- 支持多个目标包裹(多个
)
async="true"
是 NLog
针对简单异步写日志的简化语法,本质是自动为该 target
添加一个 AsyncWrapper
包裹层
相当于 NLog
内部自动做了:
但它不支持配置 batchSize、overflowAction
等高级参数。
使用场景建议:
场景 | 推荐方式 | 原因说明 |
---|---|---|
✅ 中小型项目,日志量不大 | async="true" |
简洁、易用,性能也足够 |
✅ 生产环境、日志量大(高并发写日志) | AsyncWrapper |
提高性能、可配置批处理大小、溢出策略等,适合高负载 |
❌ 需要多个目标异步写入(如控制台+文件) | AsyncWrapper |
一个 AsyncWrapper 可包裹多个目标,async="true" 无法组合多个目标 |
❌ 需要控制日志队列行为 | AsyncWrapper |
可配置缓冲区行为:Discard / Block / Grow 等 |
✅ 快速调试开发 | async="true" |
配置文件更简洁、清晰 |
终极文件目标配置
archiveEvery="Day"
archiveAboveSize="10485760"
archiveNumbering="Rolling"
archiveFileName="logs/archives/app.${shortdate}.{#}.log"
maxArchiveFiles="7"
concurrentWrites="true"
keepFileOpen="false"
encoding="utf-8"
/>
配置说明:
fileName="logs/app.${shortdate}.log"
每天都会以 app.YYYY-MM-DD.log 的文件名写入新日志。archiveEvery="Day"
在每个日历天的开头,都会对前一天的文件进行归档(配合 archiveNumbering 控制编号方式)。archiveAboveSize="10485760"
当当天的当前日志文件大小超过 10 MB(10×1024×1024 bytes)时,会自动把它归档并新建一个同名空文件继续写日志。archiveNumbering="Rolling"
使用滚动编号方式:第一个归档为 .0.log,第二个为 .1.log,以此类推。archiveFileName="logs/archives/app.${shortdate}.{#}.log"
将归档文件集中到 logs/archives/ 目录下,便于管理。{#} 会被替换成自增的归档号。maxArchiveFiles="7"
最多保留最近 7 个归档文件,超过则自动删除最旧的。AsyncWrapper
overflowAction="Discard"
:队列满时丢弃最旧的日志,避免阻塞;batchSize="200"
:每批写入 200 条;timeToSleepBetweenBatches="50"
:批量写入后等待 50 ms。
concurrentWrites="true" & keepFileOpen="false"
在并发写入和文件锁管理上更稳健,适合多线程/多进程场景。