迷恋自留地 发表于 2021-1-1 23:45

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

# ASP.NET Core中使用NLog记录日志
##一、前言
在所有的应用程序中,日志功能是不可或缺的模块,我们可以根据日志信息进行调试、查看产生的错误信息,在ASP.NET Core中我们可以使用log4net或者NLog日志组件来实现记录日志的功能,这里讲解如何在ASP.NET Core中使用NLog。

这里采用的是.NET Core 3.1创建应用程序。

那么什么是NLog呢?

NLog是一个基于.NET平台编写的类库,我们可以使用NLog在应用程序中添加即为完善的跟踪调试代码。

NLog是一个简单灵活的.NET日志记录类库。通过使用NLog,我们可以在任何一种.NET语言中输出带有上下文的调试诊断信息,根据个人的爱好配置其输出的样式,然后发送到一个或多个输出目标(target)中。

NLog的API非常类似于log4net,且配置方式非常简单。NLog使用路由表进行配置,这样就让NLog的配置文件非常容易阅读,并便于今后维护。

NLog遵循BSD license,即允许商业应用且完全开放源代码。任何人都可以免费使用并对其进行测试,然后通过邮件列表反馈问题以及建议。

NLog支持.NET、C/C++以及COM组件,因此我们的程序、组件、包括用C++/COM编写的遗留模块都可以通过同一个路由引擎将信息发送至NLog中。

简单来说,NLog就是用来记录项目日志的组件。
## 二、使用NLog
### 1. 直接在NuGet里面搜索`NLog.Web.AspNetCore`,然后进行安装即可,如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101213533721.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_0)
修改Program类,在里面配置使用NLog,代码如下所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101214027221.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_0)
### 2. 添加配置文件
右键添加新建项,然后选择Web配置文件,命名为nlog.config如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101214408666.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_0)

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"
      throwConfigExceptions="true"
      internalLogLevel="info"
      internalLogFile="D:\log\internal-nlog.txt">
<!--autoReload:修改后自动加载,可能会有延迟-->
<!--throwConfigExceptions:NLog日志系统抛出异常-->
<!--internalLogLevel:内部日志的级别-->
<!--internalLogFile:内部日志保存路径,日志的内容大概就是NLog的版本信息,配置文件的地址等等-->
<!--输出日志的配置,用于rules读取-->
<targets>
    <!--write logs to file-->
    <!--将日志写入文件中,fileName可以指定日志生成的路径-->
    <target xsi:type="File" name="allfile" fileName="D:\Log\nlog-all-${shortdate}.log"
             layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />
   <!--同样是将文件写入日志中,写入的内容有所差别,差别在layout属性中体现。写入日志的数量有差别,差别在路由逻辑中体现-->
    <target xsi:type="File" name="ownFile-web" fileName="D:\Log\nlog-my-${shortdate}.log"
             layout="${longdate}|${logger}|${uppercase:${level}}|${message} ${exception}" />
    <target xsi:type="Null" name="blackhole" />
</targets>
<rules>
    <!--路由顺序会对日志打印产生影响。路由匹配逻辑为顺序匹配。-->
    <!--All logs, including from Microsoft-->
    <logger name="*" minlevel="Trace" writeTo="allfile" />

    <!--Skip Microsoft logs and so log only own logs-->
    <!--以Microsoft打头的日志将进入此路由,由于此路由没有writeTo属性,所有会被忽略-->
    <!--且此路由设置了final,所以当此路由被匹配到时。不会再匹配此路由下面的路由。未匹配到此路由时才会继续匹配下一个路由-->
    <logger name="Microsoft.*" minlevel="Trace"final="true" />
    <!--上方已经过滤了所有Microsoft.*的日志,所以此处的日志只会打印除Microsoft.*外的日志-->
    <logger name="*" minlevel="Trace" writeTo="ownFile-web" />
</rules>
</nlog>
```
添加完配置文件以后,我们还需要修改配置文件的属性,设置为始终复制,如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101214648186.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_70)

### 3. 在控制器中使用
通过上面的步骤,我们已经完成NLog的配置,接下来我们就可以在控制器中使用了,通过构造函数注入的方式实现注入。控制器代码如下:
```
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NLogDemo.Controllers
{
    /]")]
   
    public class NLogTestController : ControllerBase
    {
      private readonly ILogger<NLogTestController> _logger;

      public NLogTestController(ILogger<NLogTestController> logger)
      {
            _logger = logger;
      }

      
      public IActionResult Get()
      {
            _logger.LogError("这是错误信息");
            _logger.LogDebug("这是调试信息");
            _logger.LogInformation("这是提示信息");

            return Ok();
      }
    }
}

```
运行程序,访问nlogtest控制器,然后查看是否有日志生成:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101215531448.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_0)

我们在nlog.config里面配置的文件路径是D:\Log,从上面的截图中看到,有日志生成了 。这里生成了两个日志文件,这是因为我们在nlog.config里面配置的日志级别不同。日志内容如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101215716643.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_70)

可以看到,启动过程中的Microsoft日志也输出了,如果不想输出Microsoft日志,修改nlog.config里rules节点下面的路径规则顺序即可。

### 4.读取指定位置的配置文件
上面的例子中,我们是直接在项目的根目录下面添加的nlog.config文件,有时候我们想把程序里面的配置文件都放到单独的文件夹里面,这样方便管理,那么该如何设置让程序读取指定位置的nlog.config文件呢?

新建一个文件夹,命名为XmlConfig,然后把nlog.config文件移到到XmlConfig文件夹下面,移到后的结构如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101220020734.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_0)
然后修改Program文件,在程序里面设置读取XmlConfig文件夹下面的nlog.config文件,代码如下:
` // 设置读取指定位置的nlog.config文件
            NLogBuilder.ConfigureNLog("XmlConfig/nlog.config");`
需要引入命令空间`using NLog.Web;`![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101215954396.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_70)
这样就可以读取XmlConfig文件夹下面的配置文件了。
### 5.封装
上面的例子中,最后输出的日志格式是根据nlog.config配置文件里面的layout样式输出的,有时候输出的内容可能不满足我们的需求,我们可以对程序中的日志功能模块进行封装,输出我们自己定义的日志格式。

在解决方案中添加一个类库,命名为Nlog.Framework,然后在类库中添加一个Log文件夹,把所有Log相关的文件都放到该文件夹下,添加后的项目结构如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101221018743.png)
添加`LogMessage`类,里面是要记录的一些信息属性字段:
```
using System;
using System.Collections.Generic;
using System.Text;

namespace Nlog.Framework.Log
{
    /// <summary>
    /// 日志消息
    /// </summary>
    public class LogMessage
    {
      /// <summary>
      /// IP
      /// </summary>
      public string IpAddress { get; set; }

      /// <summary>
      /// 操作人
      /// </summary>
      public string OperationName { get; set; }

      /// <summary>
      /// 操作时间
      /// </summary>
      public DateTime OperationTime { get; set; }

      /// <summary>
      /// 日志信息
      /// </summary>
      public string LogInfo { get; set; }

      /// <summary>
      /// 跟踪信息
      /// </summary>
      public string StackTrace { get; set; }

    }
}


```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101225522251.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_70)


添加一个`LogFormat`格式化类,用来格式化日志输出内容:
```
using System;
using System.Collections.Generic;
using System.Text;

namespace Nlog.Framework
{
    /// <summary>
    /// 格式化输出样式
    /// </summary>
    public class LogFormat
    {
      public static string ErrorFormat(LogMessage logMessage)
      {
            StringBuilder strInfo = new StringBuilder();
            strInfo.Append("1. 操作时间: " + logMessage.OperationTime + " \r\n");
            strInfo.Append("2. 操作人: " + logMessage.OperationName + " \r\n");
            strInfo.Append("3. Ip: " + logMessage.IpAddress + "\r\n");
            strInfo.Append("4. 错误内容: " + logMessage.LogInfo + "\r\n");
            strInfo.Append("5. 跟踪: " + logMessage.StackTrace + "\r\n");
            strInfo.Append("------------------------------------------------------------------\r\n");
            return strInfo.ToString();
      }
    }
}

```
![](https://img-blog.csdnimg.cn/20210101225632567.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_16,color_FFFFFF,t_70)
这里使用依赖注入的方式,所以我们首先定义一个接口,代码如下:
```
using System;

namespace Nlog.Framework.Log
{
    public interface INLogHelper
    {
      void LogError(Exception ex);
    }
}
```

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101225757898.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_16,color_FFFFFF,t_70)

然后定义接口的实现类`NLogHelper`,代码如下:
```
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;

namespace Nlog.Framework.Log
{
    public class NLogHelper : INLogHelper
    {
      //public static Logger logger { get; private set; }

      private readonly IHttpContextAccessor _httpContextAccessor;

      private readonly ILogger<NLogHelper> _logger;

      public NLogHelper(IHttpContextAccessor httpContextAccessor, ILogger<NLogHelper> logger)
      {
            _httpContextAccessor = httpContextAccessor;
            _logger = logger;
      }

      public void LogError(Exception ex)
      {
            LogMessage logMessage = new LogMessage();
            logMessage.IpAddress = _httpContextAccessor.HttpContext.Request.Host.Host;
            if (ex.InnerException != null)
                logMessage.LogInfo = ex.InnerException.Message;
            else
                logMessage.LogInfo = ex.Message;
            logMessage.StackTrace = ex.StackTrace;
            logMessage.OperationTime = DateTime.Now;
            logMessage.OperationName = "admin";
            _logger.LogError(LogFormat.ErrorFormat(logMessage));
      }
    }
}

```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101225954231.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_70)
为了演示效果,在站点创建File文件夹,创建类`CustomerGlobalExceptionFilterAsync`,我们添加一个全局异常过滤器,代码如下:
```
using Microsoft.AspNetCore.Mvc.Filters;
using Nlog.Framework.Log;
using System.Threading.Tasks;

namespace NLogDemo.Filter
{
    /// <summary>
    /// 异步版本自定义全局异常过滤器
    /// </summary>
    public class CustomerGlobalExceptionFilterAsync : IAsyncExceptionFilter
    {

      private readonly INLogHelper _logHelper;

      public CustomerGlobalExceptionFilterAsync(INLogHelper logHelper)
      {
            _logHelper = logHelper;
      }

      /// <summary>
      /// 重新OnExceptionAsync方法
      /// </summary>
      /// <param name="context">异常信息</param>
      /// <returns></returns>
      public Task OnExceptionAsync(ExceptionContext context)
      {
            // 如果异常没有被处理,则进行处理
            if (context.ExceptionHandled == false)
            {
                // 记录错误信息
                _logHelper.LogError(context.Exception);
                // 设置为true,表示异常已经被处理了,其它捕获异常的地方就不会再处理了
                context.ExceptionHandled = true;
            }

            return Task.CompletedTask;
      }
    }
}

```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101230432874.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_70)

接着添加`values`一个控制器,在控制器里面模拟发生错误的操作,代码如下:
```
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace NLogDemo.Controllers
{
    /")]
   
    public class ValuesController : ControllerBase
    {
      /// <summary>
      /// 日志
      /// </summary>
      private readonly ILogger<ValuesController> _logger;

      public ValuesController(ILogger<ValuesController> logger)
      {
            _logger = logger;
      }

      
      public IActionResult Test()
      {
            _logger.LogError("测试封装日志");
            int.Parse("asd");
            return Ok();
      }


    }
}

```
修改Program类:

```
      // 读取指定位置的配置文件
            var logger = NLogBuilder.ConfigureNLog("XmlConfig/nlog.config").GetCurrentClassLogger();

            try
            {
                logger.Info("Init Main");
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception ex)
            {
                logger.Error(ex, "Stopped program because of exception");
            }
            finally
            {
                LogManager.Shutdown();
            }
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101230053967.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_16,color_FFFFFF,t_70)
最后在Startup类里面注入:
```
    public void ConfigureServices(IServiceCollection services)
      {
            #region 添加异常处理过滤器
            services.AddControllers(options => options.Filters.Add(typeof(CustomerGlobalExceptionFilterAsync)));
            #endregion

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddSingleton<INLogHelper, NLogHelper>();

            services.AddControllers();
      }

```

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210101230904454.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQwNzMyMzM2,size_1,color_FFFFFF,t_70)
大功告成!在D://log查看日志

项目链接:https://pan.baidu.com/s/1BfSqD03oIMbj1bxGhadlhw 提取码:qts3

qq6227575 发表于 2021-1-7 19:28

现在现在从FramWork转core

好先生坏印象 发表于 2021-1-9 11:41

感谢分享

forever然哥 发表于 2021-2-8 13:27

感谢分享

caaol 发表于 2021-4-7 17:32

感谢分享,写得很好
页: [1]
查看完整版本: 【Core】ASP.NET Core中使用NLog记录日志