zoukankan      html  css  js  c++  java
  • ASP.NET Core中使用NLog记录日志

    2019/10/28, ASP.NET Core 3.0, NLog 4.6.7, NLog.Web.AspNetCore 4.9.0

    摘要:NLog在ASP.NET Core网站中的使用,NLog日志写入数据库,NLog日志写入文件
    案例代码

    编辑于 2020/02/21 :
    本文的记录日志封装了统一的NLogUtil方法进行调用写日志,其实可以使用依赖注入的方式得到logger,并且日志按等级过滤,可以考虑看我写的新的关于NLog使用的方法:《ASP.NET Core搭建多层网站架构【7-使用NLog日志记录器】》

    需求

    1.日志自动写入到数据库、写入到文件
    2.appsettings.json数据库连接更改后,不需要去改NLog中的连接地址,启动网站或项目时自动检测变动然后去更改,以appsettings.json为准,保持同步。
    3.写入日志时,除了NLog自带的字段,新增LogType自定义字段记录日志类型,例如网站日志、中间件日志等
    4.统一的写日志方法,不用每次get一个logger对象(或依赖注入)来记日志

    安装包

    在nuget中安装NLogNLog.Web.AspNetCore ,这两个是NLog相关的包。
    还需要安装NLog写入数据库的数据库适配器,我这里写入到MySQL数据库,所以安装MySql.Data
    如果是写入到SQL server数据库,需要安装Microsoft.Data.SqlClient

    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="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="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: emp log-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.SqlConnection, Microsoft.Data.SqlClient(此处更新时间2020-01-06,NLog在新版本中对于mssql的dbProvider有所变动,原先只要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规则,即写入数据库,如果要适配多条规则,用逗号隔开
      • 其余规则可以参考博客

    数据库配置

    数据表结构

    这里数据库为TestNLog:

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

    "ConectionStrings": {
        "MySqlConnection": "server=192.168.137.10;database=TestNLog;user=root;password=mysql@local"
      }
    

    统一日志记录方法

    网站下新建CommonUtils文件夹,添加NLogUtil.cs文件(包含LogType定义):

    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方法中添加以下内容:

    //using NLog.Web;
    .ConfigureLogging(logging => { 
                    logging.ClearProviders(); 
                    logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); 
                }).UseNLog();  // NLog: 依赖注入Nlog
    

    完成后如下图所示:

    启动项目同步连接字符串

    修改网站启动Program.cs中的逻辑:

    //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;
        }
    }
    

    修改完成后,如下图所示:

    启动验证

    启动项目,可以正常记录日志到数据库和文件:

  • 相关阅读:
    rabbitMq交换机direct、topics
    rabbitMq及安装、fanout交换机-分发(发布/订阅)
    rabbitmq
    .sh文件启动 jenkins
    雪花算法、二进制运算
    收银台数据库存储AES加解密
    数据库事物
    excel技巧
    日期计算、正则、sequence、索引、表连接、mybatis
    oracle常用函数介绍
  • 原文地址:https://www.cnblogs.com/kasnti/p/11748306.html
Copyright © 2011-2022 走看看