如何在 ASP.NET Core 中使用 NLog 的高级特性

核能气质少年 提交于 2021-01-06 14:23:42

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 内容如下:


<?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">
  <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 实现自动轮转日志。


<?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">
   <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 中指定数据库连接串,请注意 connectionStringcommandText 是如何配置的。


<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 文件仅供参考。


<?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">
  <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 将日志记录到 MySQLOracleSQLite 数据库。

使用 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!