ASP.NET Core中使用NLog記錄日誌

2019/10/28, ASP.NET Core 3.0, NLog 4.6.7, NLog.Web.AspNetCore 4.9.0html

摘要:NLog在ASP.NET Core網站中的使用,NLog日誌寫入數據庫,NLog日誌寫入文件
案例代碼mysql

編輯於 2020/02/21 :
本文的記錄日誌封裝了統一的NLogUtil方法進行調用寫日誌,其實可使用依賴注入的方式獲得logger,而且日誌按等級過濾,能夠考慮看我寫的新的關於NLog使用的方法:《ASP.NET Core搭建多層網站架構【7-使用NLog日誌記錄器】》git

需求

1.日誌自動寫入到數據庫、寫入到文件
2.appsettings.json數據庫鏈接更改後,不須要去改NLog中的鏈接地址,啓動網站或項目時自動檢測變更而後去更改,以appsettings.json爲準,保持同步。
3.寫入日誌時,除了NLog自帶的字段,新增LogType自定義字段記錄日誌類型,例如網站日誌、中間件日誌等
4.統一的寫日誌方法,不用每次get一個logger對象(或依賴注入)來記日誌github

安裝包

在nuget中安裝NLogNLog.Web.AspNetCore ,這兩個是NLog相關的包。
還須要安裝NLog寫入數據庫的數據庫適配器,我這裏寫入到MySQL數據庫,因此安裝MySql.Data
若是是寫入到SQL server數據庫,須要安裝Microsoft.Data.SqlClient
sql

NLog.config

配置文件內容

網站根目錄下新建NLog.config配置文件,記得右擊該文件「屬性」,複製到輸出目錄:「始終複製」
數據庫

NLog.config文件內容:json

<?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" 
      throwExceptions="false" 
      internalLogLevel="Off" 
      internalLogFile="NlogRecords.log">
  <!--Nlog內部日誌記錄爲Off關閉。除非糾錯,不能夠設爲Trace不然速度很慢,起碼Debug以上-->
  <extensions>
    <add assembly="NLog.Web.AspNetCore" />
  </extensions>
  <targets>
    <!--經過數據庫記錄日誌 配置
    dbProvider請選擇mysql或是sqlserver,同時注意鏈接字符串,須要安裝對應的sql數據提供程序
    MYSQL:
    dbProvider="MySql.Data.MySqlClient.MySqlConnection, MySql.Data"
    connectionString="server=localhost;database=BaseMIS;user=root;password=123456"
    MSSQL:
    dbProvider="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient"
    connectionString="Server=127.0.0.1;Database=BaseMIS;User ID=sa;Password=123456"
    -->
    <target name="log_database" xsi:type="Database" dbProvider="MySql.Data.MySqlClient.MySqlConnection, MySql.Data"
            connectionString="server=192.168.137.10;database=TestNLog;user=root;password=mysql@local">
      <commandText>
        INSERT INTO TblLogrecords 
        (LogDate,LogLevel,LogType,Logger,Message,MachineName,MachineIp,NetRequestMethod
        ,NetRequestUrl,NetUserIsauthenticated,NetUserAuthtype,NetUserIdentity,Exception)
        VALUES
        (@LogDate,@LogLevel,@LogType,@Logger,@Message,@MachineName,@MachineIp,@NetRequestMethod
        ,@NetRequestUrl,@NetUserIsauthenticated,@NetUserAuthtype,@NetUserIdentity,@Exception);
      </commandText>
      <parameter name="@LogDate" layout="${date}" />
      <parameter name="@LogLevel" layout="${level}" />
      <parameter name="@LogType" layout="${event-properties:item=LogType}" />
      <parameter name="@Logger" layout="${logger}" />
      <parameter name="@Message" layout="${message}" />
      <parameter name="@MachineName" layout="${machinename}" />
      <parameter name="@MachineIp" layout="${aspnet-request-ip}" />
      <parameter name="@NetRequestMethod" layout="${aspnet-request-method}" />
      <parameter name="@NetRequestUrl" layout="${aspnet-request-url}" />
      <parameter name="@NetUserIsauthenticated" layout="${aspnet-user-isauthenticated}" />
      <parameter name="@NetUserAuthtype" layout="${aspnet-user-authtype}" />
      <parameter name="@NetUserIdentity" layout="${aspnet-user-identity}" />
      <parameter name="@Exception" layout="${exception:tostring}" />
    </target>
    <target name="log_file" xsi:type="File" fileName="${basedir}/logs/${shortdate}.log" 
            layout="${longdate} | ${level:uppercase=false} | ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}" />
  </targets>
  <rules>
    <!--跳過全部級別的Microsoft組件的日誌記錄-->
    <logger name="Microsoft.*" final="true" />
    <!-- BlackHole without writeTo -->
    <!--只經過數據庫記錄日誌,若是給了name名字,cs裏用日誌記錄的時候,取logger須要把name當作參數-->
    <logger name="logdb" writeTo="log_database" />
    <logger name="logfile" writeTo="log_file" />
  </rules>
</nlog>

配置文件解讀

  • nlog根節點:
    • autoReload屬性,true時,若是NLog.config文件有變更,會自動應用新配置(可是會有延遲,過幾秒纔會應用起來)
    • internalLogLevel屬性,設定後,輸出的是NLog內部本身的日誌記錄,若是遇到NLog異常/配置文件沒配好,能夠把Off改成Trace或Debug來查看NlogRecords.log裏的內容
    • internalLogFile屬性,能夠設定路徑,例如默認的c:\temp\nlog-internal.log
  • 新增了extensions節點,由於引用了NLog.Web.AspNetCore
  • targets節點中是各類記錄方式的配置
  • 第一個target節點,能夠看到name是log_database,這裏的name和下方logger中writeTo屬性對應
    • xsi:type="Database",就是寫入數據庫了
    • dbProvider屬性是數據庫適配器,MySQL是MySql.Data.MySqlClient.MySqlConnection, MySql.Data,SQL server是Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient(此處更新時間2020-01-06,NLog在新版本中對於mssql的dbProvider有所變更,原先只要Microsoft.Data.SqlClient便可),其餘數據庫適配器可在官方文檔內查看
    • connectionString即鏈接字符串了
    • commandText子節點是插入數據庫時insert語句,能夠看到我這裏是寫入到TblLogrecords表,表結構下文會展現出來
    • parameter子節點是insert語句的各個參數:
      • 有個name="@LogType"參數,layout="${event-properties:item=LogType}",表示@LogType參數的值從event-properties中的LogType中取,這個後文會寫到用法
      • 其他參數均是NLog自帶的內容,aspnet-開頭的是NLog.Web.AspNetCore包中提供的方法
      • layout render官方文檔
  • 第二個target節點,能夠看到name是log_file,這裏的name和下方logger中writeTo屬性對應
    • xsi:type="File",即寫入到文件
    • fileName屬性是文件名,這裏是寫入到當前目錄下的logs文件夾,而且按日期歸檔
    • layout屬性是寫入日誌的格式
  • rules節點是各個日誌記錄器logger的配置
    • 第一個logger配置跳過全部Microsoft組件的日誌記錄,final 標記當前規則爲最後一個規則。其後的規則即時匹配也不會被運行。
    • 第二個logger name="logdb",該日誌記錄器名爲logdb,是適配log_database規則,即寫入數據庫,若是要適配多條規則,用逗號隔開
    • 其他規則能夠參考博客

數據庫配置

數據表結構

這裏數據庫爲TestNLog:c#

CREATE DATABASE IF NOT EXISTS `TestNLog`;
USE `TestNLog`;

-- Dumping structure for table TestNLog.TblLogrecords
CREATE TABLE IF NOT EXISTS `TblLogrecords` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `LogDate` datetime(6) NOT NULL,
  `LogLevel` varchar(50) NOT NULL,
  `LogType` varchar(50) DEFAULT NULL,
  `Logger` varchar(256) NOT NULL,
  `Message` longtext,
  `MachineName` varchar(50) DEFAULT NULL,
  `MachineIp` varchar(50) DEFAULT NULL,
  `NetRequestMethod` varchar(10) DEFAULT NULL,
  `NetRequestUrl` varchar(500) DEFAULT NULL,
  `NetUserIsauthenticated` varchar(10) DEFAULT NULL,
  `NetUserAuthtype` varchar(50) DEFAULT NULL,
  `NetUserIdentity` varchar(50) DEFAULT NULL,
  `Exception` longtext,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=96 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

網站配置鏈接

appsettings.json中增長ConectionStrings節點:架構

"ConectionStrings": {
    "MySqlConnection": "server=192.168.137.10;database=TestNLog;user=root;password=mysql@local"
  }

統一日誌記錄方法

網站下新建CommonUtils文件夾,添加NLogUtil.cs文件(包含LogType定義):app

using NLog;
using NLog.Config;
using System;
using System.ComponentModel;
using System.Linq;
using System.Xml.Linq;

namespace NLogUsage.CommonUtils
{
    public enum LogType
    {
        [Description("網站")]
        Web,
        [Description("數據庫")]
        DataBase,
        [Description("Api接口")]
        ApiRequest,
        [Description("中間件")]
        Middleware
    }
    public static class NLogUtil
    {
        public static Logger dbLogger = LogManager.GetLogger("logdb");
        public static Logger fileLogger = LogManager.GetLogger("logfile");
        /// <summary>
        /// 寫日誌到數據庫
        /// </summary>
        /// <param name="logLevel">日誌等級</param>
        /// <param name="logType">日誌類型</param>
        /// <param name="message">信息</param>
        /// <param name="exception">異常</param>
        public static void WriteDBLog(LogLevel logLevel, LogType logType, string message, Exception exception = null)
        {
            LogEventInfo theEvent = new LogEventInfo(logLevel, dbLogger.Name, message);
            theEvent.Properties["LogType"] = logType.ToString();
            theEvent.Exception = exception;
            dbLogger.Log(theEvent);
        }
        /// <summary>
        /// 寫日誌到文件
        /// </summary>
        /// <param name="logLevel">日誌等級</param>
        /// <param name="logType">日誌類型</param>
        /// <param name="message">信息</param>
        /// <param name="exception">異常</param>
        public static void WriteFileLog(LogLevel logLevel, LogType logType, string message, Exception exception = null)
        {
            LogEventInfo theEvent = new LogEventInfo(logLevel, fileLogger.Name, message);
            theEvent.Properties["LogType"] = logType.ToString();
            theEvent.Exception = exception;
            fileLogger.Log(theEvent);
        }

        /// <summary>
        /// 確保NLog配置文件sql鏈接字符串正確
        /// </summary>
        /// <param name="nlogPath"></param>
        /// <param name="sqlConnectionStr"></param>
        public static void EnsureNlogConfig(string nlogPath, string sqlConnectionStr)
        {
            XDocument xd = XDocument.Load(nlogPath);
            if (xd.Root.Elements().FirstOrDefault(a => a.Name.LocalName == "targets")
                is XElement targetsNode && targetsNode != null &&
                targetsNode.Elements().FirstOrDefault(a => a.Name.LocalName == "target" && a.Attribute("name").Value == "log_database")
                is XElement targetNode && targetNode != null)
            {
                if (!targetNode.Attribute("connectionString").Value.Equals(sqlConnectionStr))//不一致則修改
                {
                    //這裏暫時沒有考慮dbProvider的變更
                    targetNode.Attribute("connectionString").Value = sqlConnectionStr;
                    xd.Save(nlogPath);
                    //編輯後從新載入配置文件(不依靠NLog本身的autoReload,有延遲)
                    LogManager.Configuration = new XmlLoggingConfiguration(nlogPath);
                }
            }
        }
    }
}

配置NLog依賴注入

網站Program.cs文件中,在CreateHostBuilder方法中添加如下內容:

//using NLog.Web;
.ConfigureLogging(logging => { 
                logging.ClearProviders(); 
                logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); 
            }).UseNLog();  // NLog: 依賴注入Nlog

完成後以下圖所示:

啓動項目同步鏈接字符串

修改網站啓動Program.cs中的邏輯:

//using NLogUsage.CommonUtils;
//using Microsoft.Extensions.DependencyInjection;
public static void Main(string[] args)
{
    //CreateHostBuilder(args).Build().Run();
    var host = CreateHostBuilder(args).Build();
    try
    {
        using (IServiceScope scope = host.Services.CreateScope())
        {
            IConfiguration configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
            //獲取到appsettings.json中的鏈接字符串
            string sqlString = configuration.GetSection("ConectionStrings:MySqlConnection").Value;
            //確保NLog.config中鏈接字符串與appsettings.json中同步
            NLogUtil.EnsureNlogConfig("NLog.config", sqlString);
        }
        //throw new Exception("測試異常");//for test

        //其餘項目啓動時須要作的事情
        //code
        NLogUtil.WriteDBLog(NLog.LogLevel.Trace, LogType.Web, "網站啓動成功");
        host.Run();
    }
    catch (Exception ex)
    {
        //使用nlog寫到本地日誌文件(萬一數據庫沒建立/鏈接成功)
        string errorMessage = "網站啓動初始化數據異常";
        NLogUtil.WriteFileLog(NLog.LogLevel.Error, LogType.Web, errorMessage, new Exception(errorMessage, ex));
        NLogUtil.WriteDBLog(NLog.LogLevel.Error, LogType.Web, errorMessage, new Exception(errorMessage, ex));
        throw;
    }
}

修改完成後,以下圖所示:

啓動驗證

啓動項目,能夠正常記錄日誌到數據庫和文件:

相關文章
相關標籤/搜索