自学内容网 自学内容网

有关NLog及中间件实现日志记录

NLog简介

NLog 是一个非常强大的日志记录库,广泛应用于 .NET 应用程序中。它支持多种日志目标(如文件、数据库、控制台、远程服务器等),并且可以根据日志级别(如 Trace、Debug、Info、Warn、Error、Fatal)灵活地输出日志。NLog 的设计非常灵活,允许开发人员通过配置文件或代码轻松控制日志记录的行为。

在 .NET Core 中使用 NLog 记录日志到数据库

为了在 .NET Core 中使用 NLog 并通过中间件记录所有接口的日志到数据库,你可以按照以下步骤操作:

1. 安装 NLog 包

你需要安装以下 NuGet 包:

  • NLog:核心包
  • NLog.Web.AspNetCore:与 ASP.NET Core 集成
  • NLog.Config:提供 NLog 的配置文件支持
  • NLog.Targets.Database:支持将日志记录到数据库
2. 配置 NLog
  • 顶层属性:控制 NLog 的基本行为,如自动重载、异常抛出和内部日志的配置。
  • 扩展:允许加载额外的 NLog 功能,如 ASP.NET Core 的日志渲染器。
  • 目标(Targets):定义了日志的实际输出位置,比如数据库、文件等。
  • 日志规则(Rules):通过规则来决定不同级别、来源的日志要写入到哪些目标中,实现日志的分类与分发。
3.类型
  • 基本组成部分包括:顶层属性、扩展、变量、目标、规则。
  • 可选扩展可以包括过滤器、布局渲染器、条件、异步目标、异常处理、异步批处理等。

在项目的根目录下创建或修改 nlog.config 文件,配置将日志记录到数据库。示例如下:

<?xml version="1.0" encoding="utf-8" ?>

<!--xmlns:xsi定义 NLog 配置的 XML 命名空间和模式,用于验证配置的合法性。-->
<!--autoReload:启用="" NLog="" 配置文件的自动重载。当配置文件发生变化时,NLog="" 会自动重新加载,不需要重启应用。=""-->
<!--throwConfigExceptions:启用时,如果配置有问题,NLog 将抛出异常,帮助开发人员调试配置错误。-->
<!--internalLogLevel:用于设置内部日志的级别,如设为 off 表示关闭内部日志。-->
<!--internalLogFile:指定 NLog 的内部日志文件路径。此日志仅用于调试 NLog 本身的工作情况。-->
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true"
      throwConfigExceptions="true"
      internalLogLevel="off"
      internalLogFile="nlog.txt">

  <!-- enable asp.net core layout renderers -->
<!--extensions:NLog 的扩展点,允许加载额外的功能或布局渲染器。在此处,
加载了 NLog.Web.AspNetCore,该程序集提供了一些与 ASP.NET Core 特性集成的日志渲染器(如 HTTP 请求信息、用户信息等)。-->
<extensions>
    <add assembly="NLog.Web.AspNetCore"/>
  </extensions>

<!--目标定义了日志要输出的地方。在这个配置中,有多个目标:-->
<targets>
<!--这俩大坨定义了不同的日志记录详细情况,不过目标都是存库里的同一张表。-->
<!--xsi:type="Database":这是一个数据库目标。它将日志条目插入到数据库中的 Sys_Logs 表中。-->
<!--dbProvider:指定数据库提供程序,在此使用 MySqlConnector 用于 MySQL 数据库。-->
<!--connectionString:数据库连接字符串,定义如何连接到数据库。-->
<!--commandText:SQL 插入语句,用于将日志数据插入数据库。参数如 @Logger、@Level 等由 NLog 动态生成并插入到日志表中。-->
<!--parameter:这些是插入 SQL 语句中的参数。每个参数的 layout 定义了如何从日志条目中提取数据,如 ${logger} 表示日志的来源,${message} 表示日志消息,${longdate} 表示日志的创建时间-->

<!--第一个更详细,用来跟踪 API 请求,适合监控 API 的性能、请求内容、耗时等。-->
<target name="ApiTrace" xsi:type="Database" dbProvider="MySqlConnector.MySqlConnection, MySqlConnector" connectionString="Server=localhost;Database=laboratory_master;UID=root;Password=123456" 
    commandText="INSERT INTO Sys_Logs (ID, Logger, Level, Host, Url , Method, Cookie, UserAgent, QueryString, Body , Message, CreateTime, IPAddress, Elapsed) VALUES (upper(uuid()), @Logger, @Level, @Host, @Url , @Method, @Cookie, @UserAgent, @QueryString, @Body , @Message, @CreateTime, @IPAddress, @Elapsed);">
      <parameter name="@Logger" layout="${logger}" />
      <parameter name="@Level" layout="${uppercase:${level}}" />
      <parameter name="@Host" layout="${aspnet-request-host}" />
      <parameter name="@Url" layout="${aspnet-request-url:IncludeScheme=false:IncludeHost=false}" />
      <parameter name="@Method" layout="${aspnet-request-method}" />
      <parameter name="@Cookie" layout="${aspnet-request-headers:HeaderNames=SYSTOKEN:ValuesOnly=true}" />
      <parameter name="@UserAgent" layout="${aspnet-request-useragent}" />
      <parameter name="@QueryString" layout="${aspnet-request-querystring:OutputFormat=JSON}" />
      <parameter name="@Body" layout="${event-properties:item=RequestBody}" />
      <parameter name="@Message" layout="${message}" />
      <parameter name="@CreateTime" layout="${longdate}" />
      <parameter name="@IPAddress" layout="${aspnet-request-ip}" />
      <parameter name="@Elapsed" layout="${event-properties:item=Elapsed}"/>
    </target>
<!--通用,适用于普通的日志记录,记录请求的基本信息,但不涉及请求体的详细信息或耗时。-->
<target name="database" xsi:type="Database" dbProvider="MySqlConnector.MySqlConnection, MySqlConnector"connectionString="Server=localhost;Database=laboratory_master;UID=root;Password=123456" 
    commandText="INSERT INTO Sys_Logs (ID, Logger, Level, Host, Url , Method, Cookie, UserAgent, QueryString, Body , Message, CreateTime, IPAddress, Elapsed) VALUES (upper(uuid()), @Logger, @Level, @Host, @Url , @Method, @Cookie, @UserAgent, @QueryString, @Body , @Message, @CreateTime, @IPAddress, 0);">
      <parameter name="@Logger" layout="${logger}" />
      <parameter name="@Level" layout="${uppercase:${level}}" />
      <parameter name="@Host" layout="${aspnet-request-host}" />
      <parameter name="@Url" layout="${aspnet-request-url:IncludeScheme=false:IncludeHost=false}" />
      <parameter name="@Method" layout="${aspnet-request-method}" />
      <parameter name="@Cookie" layout="${aspnet-request-headers:HeaderNames=SYSTOKEN:ValuesOnly=true}" />
      <parameter name="@UserAgent" layout="${aspnet-request-useragent}" />
      <parameter name="@QueryString" layout="${aspnet-request-querystring:OutputFormat=JSON}" />
      <parameter name="@Body" layout="${aspnet-request-posted-body}" />
      <parameter name="@Message" layout="${message}" />
      <parameter name="@CreateTime" layout="${longdate}" />
      <parameter name="@IPAddress" layout="${aspnet-request-ip}" />
    </target>
<!--xsi:type="File":这是一个文件目标。它将日志写入文件。-->
<!--fileName:日志文件的路径。使用 ${basedir} 表示应用程序的基础目录,日志按日期进行组织,例如 ERROR-20240915.log。-->
<!--layout:定义日志的格式。这里日志条目包括日志时间、日志来源、日志级别和日志内容,格式清晰易读。-->
<target name="error" xsi:type="File" layout="*********************************************************************************
*****************************************${newline} 日志时间 : ${longdate} ${newline} 日
志来源 : ${logger} ${newline} 日志级别 : ${uppercase:${level}} ${newline} 日志内容 : ${message}${newline}" fileName="${basedir}/Logs/${date:format=yyyyMM}/ERROR-${shortdate}.log" />
    <target name="debug" xsi:type="File" layout="*********************************************************************************
*****************************************${newline} 日志时间 : ${longdate} ${newline} 日志来源 : ${logger} ${newline} 日志级别 : ${uppercase:${level}} ${newline} 日志内容 : ${message}${newline}" 
fileName="${basedir}/Logs/${date:format=yyyyMM}/DEBUG-${shortdate}.log" />
  </targets>

<rules>
    <!-- add your logging rules here -->
    <!--
    Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace)  to "f"
    <logger name="*" minlevel="Debug" writeTo="f" />
    -->
    <!--Skip non-critical Microsoft logs and so log only own logs-->
<!--定义了上面目标的使用场景-->
<!--name="Microsoft.*":表示匹配 Microsoft.* 命名空间的日志源,maxlevel="Info" 表示记录最大到 Info 级别的日志,
而不包括 Warn、Error 等。final="true" 表示匹配到这个规则后,不会继续应用后续规则,跳过非关键的 Microsoft 系统日志。-->
<!--name="*":匹配所有日志源。-->
<!--level:指定日志级别。比如 Trace 级别的日志会写入 ApiTrace 目标,而 Debug 级别的日志则写入 debug 文件。-->
<!--writeTo:指定日志写入的目标。例如,writeTo="ApiTrace" 将日志写入到 ApiTrace 目标(即数据库)。-->
<!-- 将所有Trace级别的日志写入到 ApiTrace (详细API跟踪日志) -->
<logger name="Microsoft.*" maxlevel="Info" final="true" />
<!-- 将Info和Warn级别的日志写入到 database (普通日志记录) -->
    <logger name="*" level="Trace" writeTo="ApiTrace" />
    <logger name="*" level="Info" writeTo="database" />
    <logger name="*" level="Warn" writeTo="database" />
    <logger name="*" level="Debug" writeTo="debug" />
    <logger name="*" level="Error" writeTo="error" />

  </rules>
</nlog>
4.在 Program.csStartup.cs 中配置 NLog

Program.cs 中添加 NLog 并将其注册为应用程序的日志记录提供者。

using Autofac.Extensions.DependencyInjection;
using Meiam.System.Common;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using NLog;
using NLog.Web;
using System;
using System.Diagnostics;
using System.IO;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace Meiam.System.Hostd
{
    public class Program
    {

        public static void Main(string[] args)
        {
            //这是初始化 NLog 的第一步。
            //Setup():初始化 NLog 的设置。 
            //LoadConfigurationFromAppSettings():从应用程序的配置文件中(通常是 nlog.config 或 appsettings.json)加载 NLog 的配置文件,
            //这个配置文件定义了日志的目标(例如文件、数据库等)和规则(哪些级别的日志输出到哪些目标)。
            var logger = NLog.LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
            try
            {
                //创建一个记录器实例(logger),这个记录器专门用于 Program 类中。
                //每个类都可以获取它自己的记录器实例,方便将日志与特定类或模块关联
                CreateHostBuilder(args).Build().Run();
            }
            catch (Exception exception)
            {
                logger.Error(exception, "Stopped program because of exception");
                throw;
            }
            finally
            {
                LogManager.Shutdown();
            }
        }


        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
            //UseServiceProviderFactory这里使用 Autofac 作为依赖注入容器。NLog 本身依赖依赖注入(DI)框架来获取相关服务,
            //这一步不是直接与 NLog 相关,但它确保了整个依赖注入系统正常工作。
            .UseServiceProviderFactory(new AutofacServiceProviderFactory())
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>()
                .UseUrls(AppSettings.Configuration["Startup:ApiUrls"].Split(';'))
                .ConfigureKestrel(serverOptions =>
                {
                    serverOptions.AllowSynchronousIO = true;//启用同步 IO
                })
                //logging.ClearProviders():清除默认的日志提供程序,以确保不会与 NLog 或其他日志框架冲突。
                //logging.SetMinimumLevel(LogLevel.Trace):设置最低日志级别为 Trace,
                //即所有级别的日志(Trace、Debug、Info、Warn、Error、Fatal)都会被捕获并输出。
                //logging.AddDebug() 和 logging.AddConsole():分别添加调试输出和控制台输出作为日志目标。
                //这些可以帮助在开发过程中查看日志,但与 NLog 的日志目标不冲突。
                .ConfigureLogging(logging =>
                {
                    logging.ClearProviders();
                    logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
                    logging.AddDebug();
                    logging.AddConsole();
                });
                //这是关键部分,启用了 NLog 作为日志框架。
                //它会替换掉默认的 ASP.NET Core 日志框架,将日志处理委托给 NLog。
                //结合 NLog 的配置文件(nlog.config 或 appsettings.json 中的 NLog 配置),它决定日志记录的目标和规则。
            }).UseWindowsService().UseNLog();
    }
}

 NLog.LogManager.Setup().LoadConfigurationFromAppSettings() 是 NLog 初始化的一个快捷方法,用于从配置文件中加载日志记录的设置。一下是其查找相应配置文件的方法

1) nlog.config 文件(最常见的方式)

NLog 默认会在应用程序的根目录下(或 bin 目录)寻找名为 nlog.config 的文件。

2)  appsettings.json 文件

  • 在某些场景下,你可以在 appsettings.json 文件中定义 NLog 的配置。

  • 通过 LoadConfigurationFromAppSettings(),NLog 还可以读取 appsettings.json 中的 NLog 配置块。要使其生效,需要在 appsettings.json 中包含 NLog 配置部分

3. 显式指定文件路径

var logger = NLog.LogManager.Setup().LoadConfigurationFromFile("path/to/your/nlog.config")
.GetCurrentClassLogger();
5. 创建日志表

确保你的数据库中有一个表来存储日志。

6. 通过中间件记录日志

为了确保所有接口都能记录日志,你可以创建一个自定义中间件来记录每个请求和响应信息:

using Microsoft.AspNetCore.Http;
using NLog;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace Meiam.System.Hostd.Middleware
{
    /// <summary>
    /// 中间件
    /// 记录请求和响应数据
    /// </summary>
    public class RequestMiddleware
    {

        private readonly RequestDelegate _next;

        /// <summary>
        /// 日志接口
        /// LogManager.GetCurrentClassLogger()方法返回一个与当前类关联的日志记录器。
        /// NLog会根据配置文件或代码中定义的规则将日志写入到指定的目标。
        /// </summary>
        private static Logger logger = LogManager.GetCurrentClassLogger();

        private Stopwatch _stopwatch;

        public RequestMiddleware(RequestDelegate next)
        {
            _stopwatch = new Stopwatch();
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            // 过滤,只有接口
            if (context.Request.Path.Value.ToLower().Contains("api"))
            {
                context.Request.EnableBuffering();
                Stream originalBody = context.Response.Body;

                _stopwatch.Restart();

                // 获取 Api 请求内容
                var requestContent = await GetRequesContent(context);


                // 获取 Api 返回内容
                using (var ms = new MemoryStream())
                {
                    context.Response.Body = ms;

                    await _next(context);
                    ms.Position = 0;

                    await ms.CopyToAsync(originalBody);
                }

                context.Response.Body = originalBody;

                _stopwatch.Stop();

                //与日志记录有关的逻辑
                //这是NLog用于记录日志的核心数据结构。它可以包含消息、日志级别、属性等信息。
                var eventInfo = new LogEventInfo();
                eventInfo.Message = "Success";
                eventInfo.Properties["Elapsed"] = _stopwatch.ElapsedMilliseconds;
                eventInfo.Properties["RequestBody"] = requestContent;
                //将日志事件记录到日志目标。这里使用的是Trace级别,这意味着这些日志信息是详细的,通常用于调试目的。
                logger.Trace(eventInfo);
            }
            else
            {
                await _next(context);
            }
        }

        private async Task<string> GetRequesContent(HttpContext context)
        {
            var request = context.Request;
            var sr = new StreamReader(request.Body);

            var content = $"{await sr.ReadToEndAsync()}";

            if (!string.IsNullOrEmpty(content))
            {
                request.Body.Position = 0;
            }

            return content;
        }
    }
}

该中间件会捕获每个请求的路径,并在日志中记录“Handling request”和“Finished handling request”消息。结合 NLog,所有这些日志都将根据你的配置写入到数据库中。

总结

  • 第一步:安装 NLog 相关 NuGet 包。
  • 第二步:配置 nlog.config 文件以指定日志存储目标(如数据库)。
  • 第三步:在 Program.cs 中设置 NLog 作为日志提供者。
  • 第四步:创建一个记录日志的中间件,确保所有接口请求都能自动记录日志。

通过这种方法,你可以在 .NET Core 中使用 NLog 轻松实现全局接口的日志记录,并且能够将这些日志持久化到数据库中。


原文地址:https://blog.csdn.net/m0_73847536/article/details/142284961

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!