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

絮凝协程
• 阅读 1513

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/art...

更多高质量干货:参见我的 GitHub: csharptranslate

点赞
收藏
评论区
推荐文章
Easter79 Easter79
4年前
Springboot项目搭配ELK日志平台
上一篇讲过了elasticsearch和kibana的可视化组合查询,这一篇就来看看大名鼎鼎的ELK日志平台是如何搞定的。elasticsearch负责数据的存储和检索,kibana提供图形界面便于管理,Logstash是个日志中转站负责给elasticsearch输出信息。1安装logstash这里使用和elasti
Easter79 Easter79
4年前
SwiftUI Tips 004:奇妙而强大的修饰符 (modifier)
在SwiftUI中,修饰符的功能类似于CSS,用来在应用布局中定位和配置视图,如修改视图的大小、背景、添加动画、添加手势等等。View协议通过扩展提供了大量的修饰符,它们以协议方法的形式给出,同时提供了默认实现。以我们熟悉的 frame() 为例,来看看它的声明:@available(iOS13.0,OSX10.15,
Stella981 Stella981
4年前
IIS 处理.net页面内部运行机制
  ASP.NET是一个非常强大的构建Web应用的平台,它提供了极大的灵活性和能力以致于可以用它来构建所有类型的Web应用。  绝大多数的人只熟悉高层的框架如:WebForms和WebServices—这些都在ASP.NET层次结构的最高层。  这篇文章的资料收集整理自各种微软公开的文档,通过比较IIS5、IIS6、IIS7这三代II
Stella981 Stella981
4年前
Net Core 控制台程序使用Nlog 输出到log文件
usingCoreImportDataApp.Common;usingMicrosoft.Extensions.Configuration;usingMicrosoft.Extensions.DependencyInjection;usingSystem;usingCoreImportDataApp.
Stella981 Stella981
4年前
Linux日志安全分析技巧
0x00前言我正在整理一个项目,收集和汇总了一些应急响应案例(不断更新中)。GitHub地址:https://github.com/Bypass007/EmergencyResponseNotes本文主要介绍Linux日志分析的技巧,更多详细信息请访问Github地址,欢迎Star。0x01日志简介Lin
铁扇公主 铁扇公主
2年前
可以替代访达的文件管理器 Path Finder 激活中文版
PathFinder是一款为Mac操作系统设计的文件管理工具,它提供了更强大和高级的文件管理功能,以增强用户在Mac上的文件浏览和操作体验。以下是PathFinder可能提供的一些主要功能和特点:强大的文件浏览器:PathFinder提供了一个功能丰富的文
赵融 赵融
2年前
InDesign 2024 for Mac(ID 2024)v19.0中英文已发布,最新介绍
ID2024是一款功能强大的页面设计和排版软件,具有丰富的工具和功能,适用于各种出版需求。它提供了高级的排版和布局工具,支持快速创建和调整页面布局,以及精确的文字处理和段落样式控制。新版本还引入了字体管理的改进、变量字体的支持和云端文档的同步和共享功能。此
子桓 子桓
2年前
DBeaverUltimate 终极激活安装包:mac上好用的数据库管理软件
DBeaverUltimate是一款功能强大的数据库管理和开发工具,它提供了广泛的功能和支持多种数据库系统。DBeaverUltimate是DBeaver社区版的商业版本,提供了更多高级功能和专业支持。以下是DBeaverUltimate的一些主要特点和功
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这