编辑于 2020/03/12
背景
熟话说得好,没有日志的监控的项目,上线那是不可能的,足以见得日志监控在整个项目中有它独特的地位,本来想使用Log4做日志监控,可是发现log4在asp.net core中并不支持写入数据库,如果想要写入数据库的话就得自己写,想了想还是用第三方框架,所以这篇文章是基于Nlog + Sqlserver。
NLog简介
NLog是一个基于.NET平台编写的类库,我们可以使用NLog在应用程序中添加极为完善的跟踪调试代码。
NLog是一个简单灵活的.NET日志记录类库。通过使用NLog,我们可以在任何一种.NET语言中输出带有上下文的(contextual information)调试诊断信息,根据喜好配置其表现样式之后发送到一个或多个输出目标(target)中。
NLog的API非常类似于log4net,且配置方式非常简单。NLog使用路由表(routing table)进行配置,这样就让NLog的配置文件非常容易阅读,并便于今后维护。
NLog支持输出的目标
- 文件 比如TXT、Excel
- 文本控制台
- 数据库
- 网络中的其它计算机(通过TCP或UDP)
- 基于MSMQ的消息队列
- Windows系统日志
Nlog官网
最新版本:4.6.8 (2020-03-12)
完成功能
- 支持写入到数据库、txt
- appsettings.json数据库连接与appsettings.json保持同步并且以为appsettings.json准,保持同步。
- 写可自定义字段写日志
- 静态方法调用写日志
一. SqlServer
1. 创建数据库
use master
go
if exists (select * from sysdatabases where name='NlogTestDB')
drop database NlogTestDB --检查有没有这个数据库,如果有就删除它。
go
create database NlogTestDB
on
(
name=LogDB_data, ------------ 养成好习惯,数据文件加_data
filename='C:\sqlserverDB\NlogTestDB_data.mdf', ------------ 一定要是.mdf的文件,代表主数据文件
size=10mb,
maxsize=100mb,
filegrowth=10%
)
log on
(
name=LogDB_log, ------------ 养成好习惯,日志文件加_log
filename='C:\sqlserverDB\NlogTestDB_log.ldf', ------------ 一定要是.ldf的文件,代表日志文件
size=10mb,
maxsize=100mb,
filegrowth=10%
)
2. 创建表
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,
)
这样数据库就创建成功了
二. ASP. Net Core
1. 新建ASP. Net Core项目
创建. 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" />
除此之外,还得装操作数据库的包,博主是基于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内部日志记录为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="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 -->
<!--只通过数据库记录日志,如果给了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属性,可以设定路径,例如默认的
D:\Logs\aspNet.log
extensions节点,引用了NLog.Web.AspNetCore
targets是比较重要的节点,里面是整个连接数据的配置
-
第一个target节点,可以看到name是log_database,这里的name和下方logger中writeTo属性对应
-
xsi:type="Database"
写入的是数据库,填File
就是txt - dbProvider属性是数据库适配器
SQL server是
Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient
MySQL是
MySql.Data.MySqlClient.MySqlConnection, MySql.Data
其他数据库适配器在Nlog官网gitHub查看 -
connectionString
:数据库连接字符串 -
commandText
:插入到数据库的脚本 -
parameter
数据库脚本的参数,统一就行了
-
name="@LogType"
是参数,layout="${event-properties:item=LogType}
表示@LogType
参数的值从event-properties
中的LogType中取,详见后文 - 其余参数均是NLog自带的内容,更多的使用方法查看layout render官方文档
-
-
第二个target节点是写入txt文件的节点
-
xsi:type="File"
:写入txt -
fileName
是写入文件的文件名并按日期添加后缀 - layout属性是写入日志的格式
-
-
rules节点是各个日志记录器logger的配置
- 第一个logger配置跳过所有Microsoft日志记录,
final
属性是否为最后一个规则,如果为true,其后的规则即便被匹配也不会被运行。 - 第二个logger name="logdb",该日志记录器名为logdb,是适配log_database规则,即写入数据库,如果要适配多条规则,用逗号隔开
- 第一个logger配置跳过所有Microsoft日志记录,
在这里务必要选中始终复制
4. 新建 .Net core 类库项目命名为Utils
创建文件夹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("网站")]
Web,
[Description("数据库")]
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>
/// 写日志到数据库
/// </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, "网站启动成功");
host.Run();
}
catch (Exception ex)
{
//使用nlog写到本地日志文件(万一数据库没创建/连接成功)
string errorMessage = "网站启动初始化数据异常";
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 默认创建的WeatherForecast控制器中get方法使用Nlog记录日志
在web项目bin目录下的项目路径和数据库都会写入数据
Bin目录下的txt
数据库
源码地址
下载Nlog分支即可