編輯于 2020/03/12
背景
熟話說得好,沒有日志的監(jiān)控的項目,上線那是不可能的,足以見得日志監(jiān)控在整個項目中有它獨特的地位,本來想使用Log4做日志監(jiān)控,可是發(fā)現(xiàn)log4在asp.net core中并不支持寫入數(shù)據(jù)庫,如果想要寫入數(shù)據(jù)庫的話就得自己寫,想了想還是用第三方框架,所以這篇文章是基于Nlog + Sqlserver。
NLog簡介
NLog是一個基于.NET平臺編寫的類庫,我們可以使用NLog在應用程序中添加極為完善的跟蹤調(diào)試代碼。
NLog是一個簡單靈活的.NET日志記錄類庫。通過使用NLog,我們可以在任何一種.NET語言中輸出帶有上下文的(contextual information)調(diào)試診斷信息,根據(jù)喜好配置其表現(xiàn)樣式之后發(fā)送到一個或多個輸出目標(target)中。
NLog的API非常類似于log4net,且配置方式非常簡單。NLog使用路由表(routing table)進行配置,這樣就讓NLog的配置文件非常容易閱讀,并便于今后維護。
NLog支持輸出的目標
- 文件 比如TXT、Excel
- 文本控制臺
- 數(shù)據(jù)庫
- 網(wǎng)絡中的其它計算機(通過TCP或UDP)
- 基于MSMQ的消息隊列
- Windows系統(tǒng)日志
Nlog官網(wǎng)
最新版本:4.6.8 (2020-03-12)
完成功能
- 支持寫入到數(shù)據(jù)庫、txt
- appsettings.json數(shù)據(jù)庫連接與appsettings.json保持同步并且以為appsettings.json準,保持同步。
- 寫可自定義字段寫日志
- 靜態(tài)方法調(diào)用寫日志
一. SqlServer
1. 創(chuàng)建數(shù)據(jù)庫
use master
go
if exists (select * from sysdatabases where name='NlogTestDB')
drop database NlogTestDB --檢查有沒有這個數(shù)據(jù)庫,如果有就刪除它。
go
create database NlogTestDB
on
(
name=LogDB_data, ------------ 養(yǎng)成好習慣,數(shù)據(jù)文件加_data
filename='C:\sqlserverDB\NlogTestDB_data.mdf', ------------ 一定要是.mdf的文件,代表主數(shù)據(jù)文件
size=10mb,
maxsize=100mb,
filegrowth=10%
)
log on
(
name=LogDB_log, ------------ 養(yǎng)成好習慣,日志文件加_log
filename='C:\sqlserverDB\NlogTestDB_log.ldf', ------------ 一定要是.ldf的文件,代表日志文件
size=10mb,
maxsize=100mb,
filegrowth=10%
)
2. 創(chuàng)建表
use NlogTestDB
--drop table logs
create table logs
(
Id int IDENTITY(1,1) PRIMARY key,
LogDate datetime not null,
LogLevel VARCHAR(50) not null,
LogType VARCHAR(50) DEFAULT null,
Message VARCHAR(5000) not null,
MachineName VARCHAR(50) not null,
MachineIp VARCHAR(50) not null,
RequestController VARCHAR(50) not null,
RequestAction VARCHAR(50) not null,
RequestMethod VARCHAR(100) not null,
RequestHeaders VARCHAR(500) not null,
RequestPostBody VARCHAR(2000) not null,
RequestQuery VARCHAR(100) not null,
RequestUrl VARCHAR(50) not null,
UserName VARCHAR(50) not null,
UserGuid VARCHAR(50) not null,
EXCEPTION VARCHAR(5000) not null,
)
這樣數(shù)據(jù)庫就創(chuàng)建成功了
二. ASP. Net Core
1. 新建ASP. Net Core項目
創(chuàng)建. net core項目,這里就不過多演示,本項目是基于最新的Core 3.1 WebAPI
2. 安裝Nuget包

也可以通過雙擊web項目進行添加還原包
<PackageReference Include="NLog" Version="4.6.8" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.9.0" />
除此之外,還得裝操作數(shù)據(jù)庫的包,博主是基于SqlServer的。
MySql

SqlServer

3. 添加配置文件
添加配置文件放置到web項目中的根目錄即可
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="Off" internalLogFile="NlogRecords.log">
<!--Nlog內(nèi)部日志記錄為Off關閉。除非糾錯,不可以設為Trace否則速度很慢,起碼Debug以上-->
<extensions>
<add assembly="NLog.Web.AspNetCore" />
</extensions>
<targets>
<!--通過數(shù)據(jù)庫記錄日志 配置
dbProvider請選擇mysql或是sqlserver,同時注意連接字符串,需要安裝對應的sql數(shù)據(jù)提供程序
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="Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient"
connectionString="Server=127.0.0.1;Database=BaseMIS;User ID=sa;Password=123456"
<commandText>
INSERT INTO logs
(LogDate,LogLevel,LogType,Message,MachineName,MachineIp,RequestController,RequestAction,RequestMethod,RequestHeaders
,RequestPostBody,RequestQuery,RequestUrl,UserName,UserGuid,Exception)
VALUES
(@LogDate,@LogLevel,@LogType,@Message,@MachineName,@MachineIp,@RequestController,@RequestAction,@RequestMethod
,@RequestHeaders,@RequestPostBody,@RequestQuery,@RequestUrl,@UserName,@UserGuid,@Exception);
</commandText>
<parameter name="@LogDate" layout="${date}" />
<parameter name="@LogLevel" layout="${level}" />
<parameter name="@LogType" layout="${event-properties:item=LogType}" />
<parameter name="@Message" layout="${message}" />
<parameter name="@MachineName" layout="${machinename}" />
<parameter name="@MachineIp" layout="${aspnet-request-ip}" />
<parameter name="@RequestController" layout="${aspnet-mvc-controller}" />
<parameter name="@RequestAction" layout="${aspnet-mvc-action}" />
<parameter name="@RequestMethod" layout="${aspnet-request-method}" />
<parameter name="@RequestHeaders" layout="${aspnet-request-headers}" />
<parameter name="@RequestPostBody" layout="${aspnet-request-posted-body}" />
<parameter name="@RequestQuery" layout="${aspnet-request-querystring}" />
<parameter name="@RequestUrl" layout="${aspnet-request-url}" />
<parameter name="@UserName" layout="${event-properties:item=UserName}" />
<parameter name="@UserGuid" layout="${event-properties:item=UserGuid}" />
<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 -->
<!--只通過數(shù)據(jù)庫記錄日志,如果給了name名字,cs里用日志記錄的時候,取logger需要把name當做參數(shù)-->
<logger name="logdb" writeTo="log_database" />
<logger name="logfile" writeTo="log_file" />
</rules>
</nlog>
解讀配置文件
-
nlog根節(jié)點:
- autoReload屬性,true時,如果NLog.config文件有變動,會自動應用新配置(但是會有延遲,過幾秒才會應用起來)
- internalLogLevel屬性,設定后,輸出的是NLog內(nèi)部自己的日志記錄,如果遇到NLog異常/配置文件沒配好,可以把Off改為Trace或Debug來查看NlogRecords.log里的內(nèi)容
- internalLogFile屬性,可以設定路徑,例如默認的
D:\Logs\aspNet.log
extensions節(jié)點,引用了NLog.Web.AspNetCore
targets是比較重要的節(jié)點,里面是整個連接數(shù)據(jù)的配置
-
第一個target節(jié)點,可以看到name是log_database,這里的name和下方logger中writeTo屬性對應
-
xsi:type="Database"寫入的是數(shù)據(jù)庫,填File就是txt - dbProvider屬性是數(shù)據(jù)庫適配器
SQL server是
Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient
MySQL是
MySql.Data.MySqlClient.MySqlConnection, MySql.Data
其他數(shù)據(jù)庫適配器在Nlog官網(wǎng)gitHub查看 -
connectionString:數(shù)據(jù)庫連接字符串 -
commandText:插入到數(shù)據(jù)庫的腳本 -
parameter數(shù)據(jù)庫腳本的參數(shù),統(tǒng)一就行了
-
name="@LogType"是參數(shù),layout="${event-properties:item=LogType}表示@LogType參數(shù)的值從event-properties中的LogType中取,詳見后文 - 其余參數(shù)均是NLog自帶的內(nèi)容,更多的使用方法查看layout render官方文檔
-
-
第二個target節(jié)點是寫入txt文件的節(jié)點
-
xsi:type="File":寫入txt -
fileName是寫入文件的文件名并按日期添加后綴 - layout屬性是寫入日志的格式
-
-
rules節(jié)點是各個日志記錄器logger的配置
- 第一個logger配置跳過所有Microsoft日志記錄,
final屬性是否為最后一個規(guī)則,如果為true,其后的規(guī)則即便被匹配也不會被運行。 - 第二個logger name="logdb",該日志記錄器名為logdb,是適配log_database規(guī)則,即寫入數(shù)據(jù)庫,如果要適配多條規(guī)則,用逗號隔開
- 第一個logger配置跳過所有Microsoft日志記錄,

在這里務必要選中始終復制

4. 新建 .Net core 類庫項目命名為Utils
創(chuàng)建文件夾Nlog和文件NlogUtil.cs

NlogUtil.cs
using NLog;
using NLog.Config;
using System;
using System.ComponentModel;
using System.Linq;
using System.Xml.Linq;
namespace Utils.Nlog
{
public enum LogType
{
[Description("網(wǎng)站")]
Web,
[Description("數(shù)據(jù)庫")]
DataBase,
[Description("Api接口")]
ApiRequest,
[Description("中間件")]
Middleware
}
public static class NLogUtil
{
public static Logger MSdbLogger = LogManager.GetLogger("logdb");
public static Logger fileLogger = LogManager.GetLogger("logfile");
/// <summary>
/// 寫日志到數(shù)據(jù)庫
/// </summary>
/// <param name="logLevel">日志等級</param>
/// <param name="message">信息</param>
/// <param name="userName">請求用戶名</param>
/// <param name="userGuid">請求用戶Guid</param>
/// <param name="exception">異常</param>
public static void WriteDBLog(LogLevel logLevel, string message,string userName = null,string userGuid = null, Exception exception = null)
{
LogEventInfo theEvent = new LogEventInfo(logLevel, MSdbLogger.Name, message);
theEvent.Properties["LogType"] = LogType.Web.ToString();
theEvent.Properties["UserName"] = userName;
theEvent.Properties["UserGuid"] = userGuid;
theEvent.Exception = exception;
MSdbLogger.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, string message, Exception exception = null)
{
LogEventInfo theEvent = new LogEventInfo(logLevel, fileLogger.Name, message);
theEvent.Properties["LogType"] = LogType.Web.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);
}
}
}
}
}
5. 在appsettings.json中配置sql連接字符串
其實只需要關心這個連接就可以了,Nlog的連接會被自動同步為該連接
"ConectionStrings": {
"MySqlserverConnection": "Data Source=127.0.0.1;Initial Catalog=NlogTestDB;Persist Security Info=True;User ID=sa;Password=123456"
}
6. 在Program.cs中注冊Nlog并添加Utils的引用
public class Program
{
public static void Main(string[] args)
{
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:MySqlserverConnection").Value;
//確保NLog.config中連接字符串與appsettings.json中同步
NLogUtil.EnsureNlogConfig("NLog.config", sqlString);
}
//throw new Exception("測試異常");//for test
//其他項目啟動時需要做的事情
//code
NLogUtil.WriteDBLog(NLog.LogLevel.Info, "網(wǎng)站啟動成功");
host.Run();
}
catch (Exception ex)
{
//使用nlog寫到本地日志文件(萬一數(shù)據(jù)庫沒創(chuàng)建/連接成功)
string errorMessage = "網(wǎng)站啟動初始化數(shù)據(jù)異常";
NLogUtil.WriteFileLog(NLog.LogLevel.Error, errorMessage, new Exception(errorMessage, ex));
NLogUtil.WriteDBLog(NLog.LogLevel.Info, errorMessage);
throw;
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
//using NLog.Web;
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
}).UseNLog(); // NLog: 依賴注入Nlog
}
7. 使用
在core webapi 默認創(chuàng)建的WeatherForecast控制器中get方法使用Nlog記錄日志

在web項目bin目錄下的項目路徑和數(shù)據(jù)庫都會寫入數(shù)據(jù)
Bin目錄下的txt


數(shù)據(jù)庫

源碼地址
下載Nlog分支即可