2020/01/29, ASP.NET Core 3.1, VS2019, NLog.Web.AspNetCore 4.9.0
摘要:基於ASP.NET Core 3.1 WebApi搭建后端多層網站架構【7-使用NLog日志記錄器】
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:
右擊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,自己指定了各等級日志的顏色
- log_database 寫入到數據庫:
- 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()
修改后如下圖所示:
此時啟動項目,可以看到控制台的信息已經發生了變化:
黃色的那部分日志是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"
}
}
}
修改后如下圖
如此一來,在開發調試階段,微軟原生的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被調用");
完成后代碼如下圖所示
啟動項目,可以看到各日志都按對應等級過濾,寫入到不同的目標中:
啟動Postman,調用http://localhost:5000/weatherforecast
,可以看到原生日志方法已經被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,被過濾掉了
- NLogExtensions的AddNlogService方法里
項目完成后,如下圖所示: