天天看點

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

基于ASP.NET Core 3.1 WebApi搭建後端多層網站架構【7-使用NLog日志記錄器】

NLog日志記錄器的寫入資料庫、寫入檔案、彩色控制台,按等級過濾日志等功能

2020/01/29, ASP.NET Core 3.1, VS2019, NLog.Web.AspNetCore 4.9.0

摘要:基于ASP.NET Core 3.1 WebApi搭建後端多層網站架構【7-使用NLog日志記錄器】

文章目錄

此分支項目代碼

本章節介紹了NLog日志記錄器的寫入資料庫、寫入檔案、彩色控制台,按等級過濾日志等功能,之前寫過ASP.NET Core中使用NLog記錄日志,僅僅是寫入資料庫和檔案,也沒有按等級過濾日志

添加包引用

MS.WebCore

類庫添加以下包引用:

<ItemGroup>
  <PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
</ItemGroup>
           

上一章節中已經添加過,這裡再次提示下,已經添加過的不需要重複添加

NLog.Web.AspNetCore

包中已經包含

NLog

包,是以隻需要這一個包即可

MS.WebApi

應用程式添加以下包引用:

<ItemGroup>
  <PackageReference Include="MySql.Data" Version="8.0.19" />
</ItemGroup>
           

MySql.Data

這個包是NLog寫入MySQL資料庫需要使用的資料庫提供程式。

NLogExtensions

MS.WebCore

類庫中添加Logger檔案夾,在該檔案夾中添加

NLogExtensions.cs

類:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Config;
using NLog.Web;
using System.Linq;
using System.Xml.Linq;

namespace MS.WebCore.Logger
{
    public static class NLogExtensions
    {
        //優先級:Trace>Debug>Info>Warn>Error>Fatal

        const string _mssqlDbProvider = "Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient";
        const string _mysqlDbProvider = "MySql.Data.MySqlClient.MySqlConnection, MySql.Data";

        /// <summary>
        /// 確定NLog配置檔案sql連接配接字元串正确
        /// </summary>
        /// <param name="nlogPath"></param>
        /// <param name="dbType"></param>
        /// <param name="sqlConnectionStr"></param>
        public static void EnsureNlogConfig(string nlogPath, string dbType, 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))//連接配接字元串不一緻則修改
                {
                    targetNode.Attribute("connectionString").Value = sqlConnectionStr;
                    //dbProvider的變動僅限mssql和mysql
                    if (dbType.ToLower() == "mysql")
                    {
                        targetNode.Attribute("dbProvider").Value = _mysqlDbProvider; //mysql 
                    }
                    else
                    {
                        targetNode.Attribute("dbProvider").Value = _mssqlDbProvider; //mssql
                    }
                    xd.Save(nlogPath);
                    //編輯後重新載入配置檔案(不依靠NLog自己的autoReload,有延遲)
                    LogManager.Configuration = new XmlLoggingConfiguration(nlogPath);
                }
            }
        }

        /// <summary>
        /// 注入Nlog服務
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static IHostBuilder AddNlogService(this IHostBuilder builder)
        {
            return builder
                  .ConfigureLogging(logging =>
                  {
                      logging.ClearProviders();
                      logging.AddDebug();
                      logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                  })
                  .UseNLog()// 替換NLog作為日志管理
                  ;
        }
    }
}
           

說明:

  • 第一個

    EnsureNlogConfig

    方法是確定NLog配置檔案sql連接配接字元串和

    appsettings.json

    檔案中一緻,NLog寫入資料庫功能有對應的DbProvider,我這裡隻内置了MySQL和SQL server的,如有需要自行修改
  • 第二個

    AddNlogService

    是對IHostBuilder的一個方法擴充,把NLog開啟的配置封裝在裡面

NLog.config

MS.WebApi

應用程式中添加Web配置檔案,并改名為NLog.config:

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

右擊

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" throwExceptions="false" internalLogLevel="Warn" internalLogFile="${basedir}/logs/NlogRecords.log">
  <!--指定了當NLog自己遇到Warn等級以上的報錯時,生成日志到./logs/NlogRecords.log下(網站的相對路徑)。除非糾錯,不可以設為Trace否則速度很慢,起碼Debug以上-->
  <extensions>
    <add assembly="NLog.Web.AspNetCore" />
  </extensions>
  <targets>
    <!--通過資料庫記錄日志 配置
    dbProvider請選擇mysql或是sqlserver,同時注意連接配接字元串,需要安裝對應的sql資料提供程式
    dbProvider="MySql.Data.MySqlClient.MySqlConnection, MySql.Data" connectionString="server=192.168.137.10;database=EvMSDB;user=root;password=mysql@local"
    dbProvider="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient" connectionString="Server=192.168.1.204;Database=EvMSDB;User ID=sa;Password=yzhly@126"
    -->
    <target name="log_database" xsi:type="Database" dbProvider="MySql.Data.MySqlClient.MySqlConnection, MySql.Data" connectionString="server=192.168.137.10;database=MSDB;user=root;password=mysql@local;">
      <commandText>
        INSERT INTO TblLogrecords (LogDate,LogLevel,Logger,Message,MachineName,MachineIp,NetRequestMethod,NetRequestUrl,NetUserIsauthenticated,NetUserAuthtype,NetUserIdentity,Exception)
        VALUES(@LogDate,@LogLevel,@Logger,@Message,@MachineName,@MachineIp,@NetRequestMethod,@NetRequestUrl,@NetUserIsauthenticated,@NetUserAuthtype,@NetUserIdentity,@Exception);
      </commandText>
      <parameter name="@LogDate" layout="${date}" />
      <parameter name="@LogLevel" layout="${level}" />
      <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} | ${logger} | ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}" />
    <!--ColoredConsole彩色控制台 xsi:type="Console"是指定輸出到普通控制台-->
    <target name="log_console" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="true" layout="${longdate}|${level}|${logger}|${message} ${exception}">
      <highlight-row condition="level == LogLevel.Trace" foregroundColor="DarkGray" />
      <highlight-row condition="level == LogLevel.Debug" foregroundColor="Gray" />
      <highlight-row condition="level == LogLevel.Info" foregroundColor="White" />
      <highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
      <highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
      <highlight-row condition="level == LogLevel.Fatal" foregroundColor="Magenta" backgroundColor="White" />
    </target>
  </targets>
  <rules>
    <!--跳過所有級别的Microsoft元件的日志記錄-->
    <!--<logger name="Microsoft.*" maxlevel="Info" final="true" />-->
    <!-- BlackHole without writeTo -->
    <!--隻通過資料庫記錄日志,這裡的*,如果給了name名字,代碼裡用日志記錄的時候,取logger需要把name當做參數-->
    <logger name="*" minlevel="Info" writeTo="log_database" />
    <logger name="*" minlevel="Trace" writeTo="log_console" />
    <logger name="*" minlevel="Warn" writeTo="log_file" />
  </rules>
</nlog>
           
  • internalLogLevel="Warn" internalLogFile="${basedir}/logs/NlogRecords.log"

    這段内容,指定了當NLog自己遇到Warn等級以上的報錯時,生成日志到./logs/NlogRecords.log下(網站的相對路徑)
  • 下面介紹一共設了的三種target:
    • log_database 寫入到資料庫:
      • 根據dbProvider不同,寫入到不同類型的資料庫,其他資料庫類型可以檢視官方文檔
      • connectionString即資料庫連接配接字元串,我們會在項目啟動時,調用

        EnsureNlogConfig

        方法確定它和

        appsettings.json

        一緻
    • log_file 輸出到檔案:
      • 檔案名按日期命名寫入到./logs檔案夾下
    • log_console 輸出到彩色控制台:
      • 此時應用的是ColoredConsole即彩色控制台
      • 我自定義了一些highlight-row,自己指定了各等級日志的顔色
  • rules應用規則,給三種target限定了三種等級:
    • 首先寫入控制台最小等級為Trace,這樣我們在調試時,任何日志我們都能在控制台看到
    • 寫入資料庫的最小等級為Info,這樣并不是是以日志都寫入資料庫,僅當日志等級大于等于Info時才寫入
    • 當發生一些警告、緻命錯誤時,必須以檔案形式記錄下來,是以寫入檔案的最小等級為Warn
    • 以上是我自己的配置,可以自行配置等級

網站項目應用NLog服務

MS.WebApi

應用程式的

Program.cs

類中,添加以下代碼至Main方法中:

//using MS.WebCore.Logger
//添加以上using引用
//確定NLog.config中連接配接字元串與appsettings.json中同步
NLogExtensions.EnsureNlogConfig("NLog.config", "MySQL", scope.ServiceProvider.GetRequiredService<IConfiguration>().GetSection("ConectionStrings:MSDbContext").Value);
           

CreateHostBuilder

方法内,追加使用NLog服務

.AddNlogService()

修改後如下圖所示:

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

此時啟動項目,可以看到控制台的資訊已經發生了變化:

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

黃色的那部分日志是EntityFrameworkCore自帶的日志,在5-網站資料庫實體設計及映射配置這一章節中,指定了EFCore使用原生日志方法,而不是NLog日志,因為很可能資料庫還沒有建立,而NLog要寫入到資料庫時找不到資料庫造成性能下降

修改原生日志等級

MS.WebApi

應用程式中,打開

appsettings.Development.json

檔案,修改LogLevel的預設等級為Trace,Microsoft.Hosting.Lifetime的等級為Warning:

{
  "Logging": {
    "LogLevel": {
      "Default": "Trace",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Warning",
      "Microsoft.EntityFrameworkCore": "Information"
    }
  }
}
           

修改後如下圖

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

如此一來,在開發調試階段,微軟原生的Logging日志記錄最小等級為Trace,Microsoft.Hosting.Lifetime的等級為Warning以下的日志都會跳過

測試寫日志

Program.cs

類的Main方法修改為以下内容:

//using NLog;
//添加以上using引用
public static void Main(string[] args)
{
    Logger logger = LogManager.GetCurrentClassLogger();
    try
    {
        var host = CreateHostBuilder(args).Build();
        logger.Trace("網站啟動中...");
        using (IServiceScope scope = host.Services.CreateScope())
        {
            logger.Trace("初始化NLog");
            //確定NLog.config中連接配接字元串與appsettings.json中同步
            NLogExtensions.EnsureNlogConfig("NLog.config", "MySQL", scope.ServiceProvider.GetRequiredService<IConfiguration>().GetSection("ConectionStrings:MSDbContext").Value);

            logger.Trace("初始化資料庫");
            //初始化資料庫
            DBSeed.Initialize(scope.ServiceProvider.GetRequiredService<IUnitOfWork<MSDbContext>>());

            //for test -start
            //用于檢視彩色控制台樣式,以及日志等級過濾
            logger.Trace("Test For Trace");
            logger.Debug("Test For Debug");
            logger.Info("Test For Info");
            logger.Warn("Test For Warn");
            logger.Error("Test For Error");
            logger.Fatal("Test For Fatal");
            //for test -end
        }
        logger.Trace("網站啟動完成");
        host.Run();
    }
    catch (Exception ex)
    {
        logger.Fatal(ex, "網站啟動失敗");
        throw;
    }
}
           

用于測試的代碼使用完記得注釋掉!

WeatherForecastController.cs

類中Get方法添加以下代碼:

_logger.LogTrace("WeatherForecast被調用");
           

完成後代碼如下圖所示

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

啟動項目,可以看到各日志都按對應等級過濾,寫入到不同的目标中:

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

啟動Postman,調用

http://localhost:5000/weatherforecast

,可以看到原生日志方法已經被NLog替換了:

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

說明

  • 和之前的文章ASP.NET Core中使用NLog記錄日志相比:
    • 寫入資料庫時,我去除了LogType這種自定義字段,這樣就可以使用原生的寫日志方法
    • 不用方法去差別寫入資料庫還是寫入檔案,由日志等級自行去過濾,例如之前《ASP.NET Core中使用NLog記錄日志》文章中,日志寫入檔案要調用

      NLogUtil.WriteFileLog

      方法,而現在,日志寫入檔案是按日志等級來的,大于指定的等級自動寫入檔案
  • 文章一共有三處指定了預設的日志等級:
    • NLogExtensions的AddNlogService方法裡

      logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace)

    • appsettings.json的LogLevel裡

      "Default": "Information"

    • appsettings.Development.json的LogLevel裡

      "Default": "Trace"

    • appsettings.json或appsettings.Development.json中的日志等級配置會覆寫SetMinimumLevel方法,也就是同時都配置的情況下,SetMinimumLevel方法其實是無效的
    • 以上三處全是指定的微軟原生Logging的日志預設等級,而不是NLog的,要注意了解和差別。例如把appsettings.Development.json的LogLevel裡

      "Default": "Trace"

      等級提高至Information,此時再用Postman調用

      http://localhost:5000/weatherforecast

      ,你會發現控制台沒有日志輸出了,因為我們在WeatherForecastController中寫的日志等級為LogTrace,被過濾掉了

項目完成後,如下圖所示:

ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】
ASP.NET Core搭建多層網站架構【7-使用NLog日志記錄器】

繼續閱讀