ASP.NET Core 中使用 NLog 记录日志
1. Install-Package
powershell
Install-Package NLog.Web.AspNetCore -Version 4.8.2
2. 创建 nlog.config 配置文件
tartgets 中配置输出,每个 target 为一个日志输出;
rules 中配置输出规则(哪些情况输出到哪个 target),每个 rule 为一个输出规则;
具体说明见 => Targets 和 Rules 。
xml
<?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="c:\temp\internal-nlog.txt">
<!-- enable asp.net core layout renderers -->
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
<!-- the targets to write to -->
<targets>
<!-- write logs to file -->
<target xsi:type="File" name="allfile" fileName="c:\temp\nlog-all-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />
<!-- another file log, only own logs. Uses some ASP.NET core renderers -->
<target xsi:type="File" name="ownFile-web" fileName="c:\temp\nlog-own-${shortdate}.log"
layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
</targets>
<!-- rules to map from logger name to target -->
<rules>
<!--All logs, including from Microsoft-->
<logger name="*" minlevel="Trace" writeTo="allfile" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*" maxlevel="Info" final="true" /> <!-- BlackHole without writeTo -->
<logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
.NET Core Web 项目中默认会以如下顺序查找配置文件(=> File locations):
- standard web application file web.config
- web.nlog located in the same directory as web.config
- NLog.config in application’s directory
- NLog.dll.nlog in a directory where NLog.dll is located (only if NLog isn't installed in the GAC)
3. Program.cs 中启用 NLog
csharp
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureLogging(logging => {
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.UseNLog();
4. SampleController.cs
使用构造函数注入方式注入 ILogger
,使用 LogInformation
、LogError
等方法记录日志。
csharp
[Route("api/[controller]")]
[ApiController]
public class SampleController : ControllerBase
{
private readonly ILogger _logger;
public SampleController(ILogger<SampleController> logger)
{
_logger = logger;
}
[HttpGet("info/{id}")]
public void Info(string id)
{
_logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
}
[HttpGet("error/{id}")]
public void Error(string id)
{
_logger.LogError(LoggingEvents.GenerateItems, "Generate items {ID}", id);
}
}
public class LoggingEvents
{
public const int GenerateItems = 1000;
public const int ListItems = 1001;
public const int GetItem = 1002;
public const int InsertItem = 1003;
public const int UpdateItem = 1004;
public const int DeleteItem = 1005;
public const int GetItemNotFound = 4000;
public const int UpdateItemNotFound = 4001;
}
5. 输出到 RabbitMQ (Logstash 的 input(输入))
安装 Nlog.RabbitMQ.Target 包
bash
Install-Package Nlog.RabbitMQ.Target Nlog.RabbitMQ.Target -Version 2.6.22
nlog.config 中的配置:
xml
<?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">
<extensions>
<add assembly="Nlog.RabbitMQ.Target" />
</extensions>
<targets>
<!-- these are the defaults (except 'topic' and 'appid'): -->
<target name="RabbitMQTarget"
xsi:type="RabbitMQ"
appid="NLog.RabbitMQ.DemoApp"
topic="DemoApp.Logging.{0}"
username="guest"
password="guest"
hostname="localhost"
exchange="app-logging"
port="5672"
vhost="/"
maxBuffer="10240"
heartBeatSeconds="3"
Timeout="3000"
layout="${longdate}|${level:uppercase=true}|${logger}|${message}"
UseJSON="false"
UseLayoutAsMessage="false"
UseSsl="false"
SslCertPath=""
SslCertPassphrase=""
Compression="None"
DeliveryMode="NonPersistent">
<field key="threadid" layout="${threadid}" />
<field key="machinename" layout="${machinename}" />
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="RabbitMQTarget"/>
</rules>
</nlog>
在这里遇到了两个坑。
一是 Nlog.RabbitMQ.Target 文档中添加的 extensions 的 assembly 值是错的。通过示例代码(Nlog.config)才发现正确的值是 Nlog.RabbitMQ.Target (而不是 NLog.Targets.RabbitMQ,看了修改历史 diff-a758e974e4c58dfcdeeb4cc1ae6b1301 发现确实是修改过了)。
二是如果 RabbitMQ 的 Exchange 设置为 Transient (也就是非持久化的)启动时会报如下错误,应该设置为 Durable 。
txt
2019-05-14 17:29:11.6760 Error RabbitMQTarget(Name=logstash_wrapped): Could not declare exchange: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - inequivalent arg 'durable' for exchange 'loggingsample' in vhost '/': received 'true' but current is 'false'", classId=40, methodId=10, cause= Exception: RabbitMQ.Client.Exceptions.Operation
InterruptedException: The AMQP operation was interrupted: AMQP close-reason, initiated by Peer, code=406, text="PRECONDITION_FAILED - nequivalent arg 'durable' for exchange 'loggingsample' in vhost '/': received 'true' but current is 'false'", classId=40, methodId=10, cause=
at RabbitMQ.Client.Impl.SimpleBlockingRpcContinuation.GetReply(TimeSpan timeout)
at RabbitMQ.Client.Impl.ModelBase.ModelRpc(MethodBase method, ContentHeaderBase header, Byte[] body)
at RabbitMQ.Client.Framing.Impl.Model._Private_ExchangeDeclare(String exchange, String type, Boolean passive, Boolean durable, Boolean autoDelete, Boolean internal, Boolean nowait, IDictionary`2 arguments)
at RabbitMQ.Client.Impl.AutorecoveringModel.ExchangeDeclare(String exchange, String type, Boolean durable, Boolean autoDelete, IDictionary`2 arguments)
at Nlog.RabbitMQ.Target.RabbitMQTarget.<>c__DisplayClass107_0.<StartConnection>b__0()
关于 Logstash 的使用和配置可以参考 SpringBoot ELK(ElasticSearch + Logstash + Kibana)日志收集 或 Logstash 保存不同类型的日志到各自的 ElasticSearch 索引。