Asp.Net Core 中使用Nlog写入txt 数据库(SqlServer MySql)

编辑于 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
  • 文本控制台
  • Email
  • 数据库
  • 网络中的其它计算机(通过TCP或UDP)
  • 基于MSMQ的消息队列
  • Windows系统日志

Nlog官网
最新版本:4.6.8 (2020-03-12)

完成功能

  1. 支持写入到数据库、txt
  2. appsettings.json数据库连接与appsettings.json保持同步并且以为appsettings.json准,保持同步。
  3. 写可自定义字段写日志
  4. 静态方法调用写日志

一. 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包

Nlog包

也可以通过双击web项目进行添加还原包

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

除此之外,还得装操作数据库的包,博主是基于SqlServer的。
MySql

Mysql

SqlServer
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根节点:

    1. autoReload属性,true时,如果NLog.config文件有变动,会自动应用新配置(但是会有延迟,过几秒才会应用起来)
    2. internalLogLevel属性,设定后,输出的是NLog内部自己的日志记录,如果遇到NLog异常/配置文件没配好,可以把Off改为Trace或Debug来查看NlogRecords.log里的内容
    3. internalLogFile属性,可以设定路径,例如默认的D:\Logs\aspNet.log
  • extensions节点,引用了NLog.Web.AspNetCore

  • targets是比较重要的节点,里面是整个连接数据的配置

  • 第一个target节点,可以看到name是log_database,这里的name和下方logger中writeTo属性对应

    1. xsi:type="Database" 写入的是数据库,填File就是txt
    2. dbProvider属性是数据库适配器
      SQL server是
      Microsoft.Data.SqlClient.SqlConnection, Microsoft.Data.SqlClient
      MySQL是
      MySql.Data.MySqlClient.MySqlConnection, MySql.Data
      其他数据库适配器在Nlog官网gitHub查看
    3. connectionString:数据库连接字符串
    4. commandText:插入到数据库的脚本
    5. parameter数据库脚本的参数,统一就行了
    • name="@LogType"是参数,layout="${event-properties:item=LogType}表示@LogType参数的值从event-properties中的LogType中取,详见后文
    • 其余参数均是NLog自带的内容,更多的使用方法查看layout render官方文档
  • 第二个target节点是写入txt文件的节点

    1. xsi:type="File":写入txt
    2. fileName是写入文件的文件名并按日期添加后缀
    3. layout属性是写入日志的格式
  • rules节点是各个日志记录器logger的配置

    1. 第一个logger配置跳过所有Microsoft日志记录,final 属性是否为最后一个规则,如果为true,其后的规则即便被匹配也不会被运行。
    2. 第二个logger name="logdb",该日志记录器名为logdb,是适配log_database规则,即写入数据库,如果要适配多条规则,用逗号隔开

Nlog文件位置

在这里务必要选中始终复制
Nlog文件属性

4. 新建 .Net core 类库项目命名为Utils

创建文件夹Nlog和文件NlogUtil.cs


Utils

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记录日志


Nlog使用

在web项目bin目录下的项目路径和数据库都会写入数据
Bin目录下的txt


路径
文件

数据库


Nlog写入数据库

源码地址

下载Nlog分支即可

码云

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容