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.cs
或 Startup.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 轻松实现全局接口的日志记录,并且能够将这些日志持久化到数据库中。