如何在 ASP.NET Core 中使用 NLog 的高级特性
NLog 是一个开源的轻量级日志框架,提供了丰富的日志路由和管理功能,同时 NLog 也是非常容易的去配置和扩展,其实在之前的文章中我已经讨论过了 Nlog,在这篇我准备继续和大家讨论一下 NLog 的更多高级功能。
接下来看看如何通过 .config
和 代码方式
配置 NLog,如何去 轮转日志
,如何将 Log 对接 database,如何使用异步的模式提高日志写入性能,同时我还会分享一些 NLog 的经验技巧。
安装 NLog
可以通过 NuGet Package Manager
可视化界面 或者 NuGet Package Manager Console
控制台 安装以下包文件。
NLog.Web.AspNetCore
NLog.Extensions.Logging
NLog.Config
当你安装完 NLog.Config
之后,有一个叫做 NLog.config
文件会自动引用到你的项目中,值得注意的是,NLog.Config
对 NLog 来说不是唯一的,言外之意就是你即可以用 config 模式配置,也可以用 基于代码
的模式配置。
使用 .config 文件配置 NLog
NLog 提供了两种配置方式。
file-based
配置模式code-based
配置模式
回到刚才的问题,如何采用 file-based
模式,刚才被引入的 NLog.Config 内容如下:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
extensions>
<targets>
<target name="logfile" xsi:type="File" fileName="D:\logs\LogMessages-${shortdate}.log" />
targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="logfile" />
rules>
nlog>
下面的代码展示了如何在 Controller 下用 NLog 记录日志。
public class HomeController : Controller
{
Logger _logger = (Logger)LogManager.GetCurrentClassLogger(typeof(Logger));
public IActionResult Index()
{
_logger.Info("Application started");
return View();
}
//Other action methods
}
如果你想通过编程的方式找到当前 NLog 的 target,可使用如下代码:
var fileTarget = (FileTarget)LogManager.Configuration.FindTargetByName("logfile");
使用 代码配置 NLog
同样你也可以使用编码的形式配置 NLog,只需要调用 NLog 提供的 API 接口即可,下面的代码展示了如何配置 Nlog。
private static void ConfigureNLog()
{
var logConfiguration = new LoggingConfiguration();
var dbTarget = new DatabaseTarget();
dbTarget.ConnectionString = "Data Source=JOYDIP;initial catalog=NLogDemo;User Id=sa;Password=sa1@3#.;"; dbTarget.CommandText = "INSERT INTO DbLog (level, callsite, message, logdatetime)" +" Values(@level, @callsite, @message, @logdatetime)";
dbTarget.Parameters.Add(new DatabaseParameterInfo("@level", "${level}"));
dbTarget.Parameters.Add(new DatabaseParameterInfo("@callSite", "${callSite}"));
dbTarget.Parameters.Add(new DatabaseParameterInfo("@message", "${message}"));
dbTarget.Parameters.Add(new DatabaseParameterInfo("@logdatetime","${date:s}"));
var rule = new LoggingRule("*", LogLevel.Debug, dbTarget);
logConfiguration.LoggingRules.Add(rule);
LogManager.Configuration = logConfiguration;
}
配置轮转日志
你可以让 NLog 自动实现 轮转日志
,什么叫 轮转
呢?简单来说就是:你可以让 Nlog 只保存近 N 个小时的日志 并且自动删除大于 N 小时的日志,这个特性太实用了,否则的话,你需要经常到生产上去删除日志,下面的代码展示了如何使用 .config
实现自动轮转日志。
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<targets>
<target name="logfile"
xsi:type="File"
fileName="${basedir}/logs/App.log"
layout="${longdate} ${message}"
archiveFileName="${basedir}/logs/archive.{#}.log"
archiveEvery="Day"
archiveNumbering="Rolling"
maxArchiveFiles="7"
concurrentWrites="true"
keepFileOpen="true" />
targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logfile" />
rules>
nlog>
记录日志到数据库
创建数据库
你可以使用 NLog 将日志接入到 database 中,下面的脚本用于创建几张记录日志的表。
CREATE TABLE [dbo].[DbLog](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Level] [varchar](max) NULL,
[CallSite] [varchar](max) NULL,
[Message] [varchar](max) NULL,
[AdditionalInfo] [varchar](max) NULL,
[LogDateTime] [datetime] NOT NULL,
CONSTRAINT [PK_DbLogs] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
数据库连接串和参数属性
接下来如何在 NLog 的 target 中指定数据库连接串,请注意 connectionString
和 commandText
是如何配置的。
<target name="database" xsi:type="Database" keepConnection="true"
useTransactions="true"
dbProvider="System.Data.SqlClient"
connectionString="data source=localhost;initial
catalog=NLogDemo;integrated security=false;
persist security info=True;User ID=sa;Password=sa1@3#."
commandText="INSERT INTO DbLog (level, callsite, message, additionalInfo,
logdatetime) Values (@level, @callsite, @message, @additionalInfo,
@logdatetime)">
使用参数化
最后,使用 参数化查询
来防止注入攻击,详细代码如下。
<parameter name="@level" layout="${level}" />
<parameter name="@callSite" layout="${callsite}" />
<parameter name="@message" layout="${message}" />
<parameter name="@additionalInfo" layout="${var:AdditionalInfo}" />
<parameter name="@logdatetime" layout="${date:s}" />
完整的 NLog
以下是完整的 NLog 文件仅供参考。
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
extensions>
<variable name="AdditionalInfo" value=""/>
<targets>
<target name="database" xsi:type="Database" keepConnection="true"
useTransactions="true"
dbProvider="System.Data.SqlClient"
connectionString="data source=localhost;initial
catalog=NLogDemo;integrated security=false;persist security
info=True;User ID=sa;Password=sa1@3#."
commandText="INSERT INTO DbLog
(level, callsite, message, additionalInfo, logdatetime)
Values (@level, @callsite, @message, @additionalInfo, @logdatetime)">
<parameter name="@level" layout="${level}" />
<parameter name="@callSite" layout="${callsite}" />
<parameter name="@message" layout="${message}" />
<parameter name="@additionalInfo" layout="${var:AdditionalInfo}" />
<parameter name="@logdatetime" layout="${date:s}" />
target>
targets>
<rules>
<logger levels="Debug,Info,Error,Warn,Fatal" name="databaseLogger" writeTo="database"/>
rules>
nlog>
除了 SQL Server
之外,还可以使用 NLog 将日志记录到 MySQL
,Oracle
和 SQLite
数据库。
使用 AsyncWrapper 提高性能
NLog 支持多种 targets,比如:AsyncWrapper, BufferingWrapper, FallbackGroup 和 RetryingWrapper,异步的 target 为了提升性能采用 消息的队列化
并在多个线程中提取队列消息,下面的代码展示了如何使用 AsyncWrapper
。
<targets>
<target xsi:type="AsyncWrapper"
name="String"
queueLimit="Integer"
timeToSleepBetweenBatches="Integer"
batchSize="Integer"
overflowAction="Enum">
<target ... />
target>
targets>
你可以实现 AsyncWrapper
来实现日志记录的异步化,详细配置如下:
<targets>
<target name="asyncFile" xsi:type="AsyncWrapper">
<target xsi:type="File" name="fileLog"
fileName="${basedir}/Logs/${shortdate}.log"
layout="${longdate} ${uppercase:${level}} ${message}"/>
target>
targets>
<rules>
<logger levels="Debug,Info,Error,Warn,Fatal" writeTo="asyncFile"/>
rules>
除了这种方式,你还可以在所有的 targets 上用 async=true
直接进行标记为异步化 target,如下配置所示:
<targets async="true">
... Write your targets here ...
targets>
NLog 的最佳实践
这一小节列举了一些使用 NLog 的一些最佳实践
logger 实例应该静态化,这样就可以避免在程序中出现多次 logger 初始化带来的性能开销。
利用好 NLog 的 Format(当你想结构化日志)支持,避免你自己对 string 的创建和拼接。
指定
throwConfigExceptions="true"
,可以确保当 NLog 配置错误的时候有详细的错误信息,例子如下:
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true" throwConfigExceptions="true">
当记录完日志后,可以调用 LogManager.Shutdown()
来实现数据刷新并且关闭内部所有的线程和定时器。
NLog.LogManager.Shutdown();
不要将
async
属性用在AsyncWrapper
之上,否则性能会变慢。慎用 Trace 级别,因为 Trace 会记录所有的日志,考虑使用 Debug 或者 Info 代替。
NLog 是轻量级并且快速的,可以使用
asynchronous wrappers
的方式来提升性能。
关于 NLog 还是有太多的话要说,NLog 提供了日志的结构化,方便在大量日志上进行快速过滤和分析,在未来的文章中我会讨论 NLog 的更多高级特性。
译文链接:https://www.infoworld.com/article/3438540/using-advanced-nlog-features-in-aspnet-core.html