Skip to content

ASP.NET Core 中使用 NLog 记录日志

🏷️ .NET Core NLog

1. Install-Package

powershell
Install-Package NLog.Web.AspNetCore -Version 4.8.2

2. 创建 nlog.config 配置文件

tartgets 中配置输出,每个 target 为一个日志输出;
rules 中配置输出规则(哪些情况输出到哪个 target),每个 rule 为一个输出规则;
具体说明见 => TargetsRules

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 ,使用 LogInformationLogError 等方法记录日志。

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. 输出到 RabbitMQLogstashinput(输入)

安装 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 文档中添加的 extensionsassembly 值是错的。通过示例代码(Nlog.config)才发现正确的值是 Nlog.RabbitMQ.Target (而不是 NLog.Targets.RabbitMQ,看了修改历史 diff-a758e974e4c58dfcdeeb4cc1ae6b1301 发现确实是修改过了)。

二是如果 RabbitMQExchange 设置为 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 索引

参考

  1. ASP.NET Core 中的日志记录
  2. NLog : Wiki
  3. NLog : Getting started with ASP.NET Core 2
  4. NLog : Tutorial
  5. NLog : File locations
  6. NLog : Configuration options
  7. Nlog.RabbitMQ.Target
  8. Message Templates