需求
1.日志自動寫入到數據庫、寫入到文件
2.appsettings.json數據庫連接更改后,不需要去改NLog中的連接地址,啟動網站或項目時自動檢測變動然后去更改,以appsettings.json為准,保持同步。
3.寫入日志時,除了NLog自帶的字段,新增LogType自定義字段記錄日志類型,例如網站日志、中間件日志等
4.統一的寫日志方法,不用每次get一個logger對象(或依賴注入)來記日志
安裝包
在nuget中安裝NLog
和NLog.Web.AspNetCore
,這兩個是NLog相關的包。
還需要安裝NLog寫入數據庫的數據庫適配器,我這里寫入到MySQL數據庫,所以安裝MySql.Data
如果是寫入到SQL server數據庫,需要安裝Microsoft.Data.SqlClient
NLog.config 配置文件內容
網站根目錄下新建NLog.config
配置文件,記得右擊該文件“屬性”,復制到輸出目錄:“始終復制”
NLog.config文件內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
<?xml version=
"1.0"
encoding=
"utf-8"
?>
<nlog xmlns=
"http://www.nlog-project.org/schemas/NLog.xsd"
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"
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
,其他數據庫適配器可在官方文檔內查看 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規則,即寫入數據庫,如果要適配多條規則,用逗號隔開其余規則可以參考https://www.jb51.net/article/173004.htm 數據庫配置數據表結構
這里數據庫為TestNLog:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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節點:
1
2
3
|
"ConectionStrings"
: {
"MySqlConnection"
:
"server=192.168.137.10;database=TestNLog;user=root;password=mysql@local"
}
|
統一日志記錄方法
網站下新建CommonUtils文件夾,添加NLogUtil.cs文件(包含LogType定義):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
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方法中添加以下內容:
1
2
3
4
5
|
//using NLog.Web;
.ConfigureLogging(logging => {
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
}).UseNLog();
// NLog: 依賴注入Nlog
|
完成后如下圖所示:
啟動項目同步連接字符串
修改網站啟動Program.cs中的邏輯:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
//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
;
}
}
|
修改完成后,如下圖所示:
啟動驗證
啟動項目,可以正常記錄日志到數據庫和文件:
以上就是本次介紹的全部相關知識點,感謝大家的學習和對腳本之家的支持。