ASP.NET Core: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

首先我們創建一個WebAPI的項目:

1、引入NLog

直接在NuGet裏面搜索NLog.Web.AspNetCore,然後進行安裝即可,如下圖所示:

安裝完成以後在依賴項裏面就可以看到了:

修改Program類,在裏面配置使用NLog,代碼如下所示:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using NLog.Web;

namespace NLogDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                // 配置使用NLog
                .UseNLog();
    }
}

2、添加配置文件

右鍵添加新建項,然後選擇Web配置文件,命名爲nlog.config如下圖所示:

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="E:\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> 

添加完配置文件以後,我們還需要修改配置文件的屬性,設置爲始終複製,如下圖所示:

3、在控制器中使用

通過上面的步驟,我們已經完成NLog的配置,接下來我們就可以在控制器中使用了,通過構造函數注入的方式實現注入。控制器代碼如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace NLogDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class NLogTestController : ControllerBase
    {
        private readonly ILogger<NLogTestController> _logger;

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

        [HttpGet]
        public IActionResult Get()
        {
            _logger.LogError("這是錯誤信息");
            _logger.LogDebug("這是調試信息");
            _logger.LogInformation("這是提示信息");

            return Ok();
        }
    }
}

運行程序,訪問nlogtest控制器,然後查看是否有日誌生成:

我們在nlog.config裏面配置的文件路徑是D:\Log,從上面的截圖中看到,有日誌生成了 。這裏生成了兩個日誌文件,這是因爲我們在nlog.config裏面配置的日誌級別不同。日誌內容如下:

可以看到,啓動過程中的Microsoft日誌也輸出了,如果不想輸出Microsoft日誌,修改nlog.config裏rules節點下面的路徑規則順序即可。

4、讀取指定位置的配置文件

上面的例子中,我們是直接在項目的根目錄下面添加的nlog.config文件,有時候我們想把程序裏面的配置文件都放到單獨的文件夾裏面,這樣方便管理,那麼該如何設置讓程序讀取指定位置的nlog.config文件呢?看下面的例子。

新建一個文件夾,命名爲XmlConfig,然後把nlog.config文件移到到XmlConfig文件夾下面,移到後的結構如下圖所示:

然後修改Program文件,在程序裏面設置讀取XmlConfig文件夾下面的nlog.config文件,代碼如下:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using NLog.Web;

namespace NLogDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // 設置讀取指定位置的nlog.config文件
            NLogBuilder.ConfigureNLog("XmlConfig/nlog.config");
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                // 配置使用NLog
                .UseNLog();
    }
}

這樣就可以讀取XmlConfig文件夾下面的配置文件了。

5、封裝

上面的例子中,最後輸出的日誌格式是根據nlog.config配置文件裏面的layout樣式輸出的,有時候輸出的內容可能不滿足我們的需求,我們可以對程序中的日誌功能模塊進行封裝,輸出我們自己定義的日誌格式。

在解決方案中添加一個類庫,命名爲Nlog.Framework,然後在類庫中添加一個Log文件夾,把所有Log相關的文件都放到該文件夾下,添加後的項目結構如下圖所示:

添加LogMessage類,裏面是要記錄的一些信息屬性字段:

using System;

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; }

    }
}

添加一個格式化類,用來格式化日誌輸出內容:

using System.Text;

namespace Nlog.Framework.Log
{
    /// <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();
        }
    }
}

這裏使用依賴注入的方式,所以我們首先定義一個接口,代碼如下:

using System;

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

然後定義接口的實現類,代碼如下:

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));
        }
    }
}

爲了演示效果,我們添加一個全局異常過濾器,代碼如下:

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;
        }
    }
}

接着添加一個控制器,在控制器裏面模擬發生錯誤的操作,代碼如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace NLogDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        /// <summary>
        /// 日誌
        /// </summary>
        private readonly ILogger<ValuesController> _logger;

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

        [HttpGet]
        public IActionResult Test()
        {
            _logger.LogError("測試封裝日誌");
            int i = 0;
            int result = 10 / i;
            return Ok();
        }


    }
}

修改Program類:

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using NLog;
using NLog.Web;
using System;

namespace NLogDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // 讀取指定位置的配置文件
            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();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                // 配置使用NLog
                .UseNLog();
    }
}

最後在Startup類裏面注入:

public void ConfigureServices(IServiceCollection services)
{
    #region 添加異常處理過濾器
    services.AddControllers(options => options.Filters.Add(typeof(CustomerGlobalExceptionFilterAsync)));
    #endregion

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


    // NLogHelper.LoadLogger();
    services.AddControllers();
}

這樣就完成了一個簡單的封裝,運行程序,訪問value控制器測試:

上面的例子中,只是封裝了Error,如果是其他級別的日誌,可以自己封裝。

GitHub地址:[email protected]:jxl1024/NLog.git 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章