在本文中,将介绍.NET中三种最受欢迎的日志记录框架:log4net,NLog和Serilog。
回溯到开始或至少很接近它的时候,.NET只有一个日志记录框架:log4net。它于2001年作为Java框架log4j的端口开始。它托管在Sourceforge上,以供您那些足以记住该平台的人使用。多年来,Apache Logging Services项目继续进行开发。
在过去的17年中,log4net已在成千上万的应用程序中使用。当然,它是所有现代.NET日志记录框架的祖父母。日志级别,日志记录器和附加器等概念在日志记录框架中几乎是通用的。这可能是喜忧参半的事情:没有其他框架能像log4net那样经过艰苦的考验。
过去,我们提供了有关使用log4net的一些技巧,以及在.NET Core中使用它的快速教程 。冒着重复自己的风险,让我们至少看看如何设置记录器和记录消息。我们将对每个.NET日志记录框架执行相同的操作:生成一条消息,并将其记录到控制台和一个日志文件中。本文的所有代码都在Github上提供。
只需安装log4net软件包即可从NuGet安装log4net。
我们将针对.NET Core编写所有代码,因为这是未来。前面我提到过log4net很旧。该遗产的一部分是,它不能与.NET Core配合使用。您在网上的大多数地方都找不到配置示例,但是幸运的是,配置的答案就在此博客上(谢谢,Matt!)。
using log4net;
using log4net.Config;
using System;
using System.IO;
using System.Reflection;
namespace LoggingDemo.Log4Net
{
class Program
{
private static readonly ILog log = LogManager.GetLogger(typeof(Program));
static void Main(string[] args)
{
var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config"));
log.Debug("Starting up");
log.Debug("Shutting down");
Console.ReadLine();
}
}
}
我们将从一个名为log4net.config的文件中加载配置。这是一个XML配置文件,其中包含用于记录的设置。不要忘记将文件设置为复制到输出目录。
<log4net> <appender name="Console" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <!-- Pattern to output the caller's file name and line number --> <conversionPattern value="%utcdate %5level [%thread] - %message%newline" /> </layout> </appender> <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"> <file value="logfile.log" /> <appendToFile value="true" /> <maximumFileSize value="100KB" /> <maxSizeRollBackups value="2" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%utcdate %level %thread %logger - %message%newline" /> </layout> <appender> <root> <level value="DEBUG" /> <appender-ref ref="Console" /> <appender-ref ref="RollingFile" /> </root> </log4net>
此配置文件非常长且复杂。定义了两个附加程序:一个用于写入控制台,另一个用于写入滚动文件,该文件的大小为100KB。我们必须在conversionPattern中定义自定义日志格式,因为默认的log4net配置为垃圾桶。设置conversionPattern后,我们将获得如下日志:
2018-08-18 18:57:37,278 DEBUG 1 LoggingDemo.Log4Net.Program - Starting up 2018-08-18 18:57:37,298 DEBUG 1 LoggingDemo.Log4Net.Program - Shutting down
此处的配置是log4net失败。它很复杂,而且XML总是很难实现。可以使用code对其进行配置,但是没有充分的文档说明。实际上,“记录不充分”几乎是所有log4net的口号。这些示例很复杂,并且侧重于日志记录到SQL数据库(2009年这样)之类的事情。log4net有很多不同的附加程序,因此对于特定的日志记录情况,您可能不会挂在嘴上。
NLog也是一个相当老的项目。1.0版于2006年发布,但仍在积极开发中,最新版本已于2017年12月发布。log4net尚未在18个月内发布,这不一定很糟糕,因为该项目很稳定。NLog的最新版本增加了结构化日志记录并支持.NET Standard。
可以通过安装nlog软件包从NuGet进行安装。
using NLog; using System; namespace LoggingDemo.Nlog { class Program { static void Main(string[] args) { LogManager.LoadConfiguration("nlog.config"); var log = LogManager.GetCurrentClassLogger(); log.Debug("Starting up"); log.Debug("Shutting down"); Console.ReadLine(); } } }
此处的代码与log4net非常相似:加载配置文件,然后登录。
<?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"> <targets> <target name="logfile" xsi:type="File" fileName="logfile.txt" /> <target name="logconsole" xsi:type="Console" /> </targets> <rules> <logger name="*" minlevel="Debug" writeTo="logconsole" /> <logger name="*" minlevel="Debug" writeTo="logfile" /> </rules> </nlog>
嗯,现在这是NLog和log4net之间的区别。配置文件仍然是XML,但是看起来更干净。我们不需要编写自己的格式。
日志格式如下:
2018-08-18 13:27:10.5022|DEBUG|LoggingDemo.Nlog.Program|Starting up 2018-08-18 13:27:10.5623|DEBUG|LoggingDemo.Nlog.Program|Shutting down
还有一种使用代码配置日志记录的方法。
var config = new NLog.Config.LoggingConfiguration(); var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "logfile.txt" }; var logconsole = new NLog.Targets.ConsoleTarget("logconsole"); config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); NLog.LogManager.Configuration = config;
对我来说,基于代码的配置看起来还不错,但是肯定不那么流畅。
NLog和log4net之间有一些细微的差异。与Log4net相比,NLog易于配置,并且支持基于代码的配置更加简洁。我认为NLog中的默认设置比log4net中的设置更合理。
我对这两个框架都遇到的一个问题是,如果记录器出现问题(在我的情况下,是忘记复制配置文件),那么就没有任何提示。这个想法是,日志记录问题不应使应用程序崩溃。我可以体会到这种愿望,但是如果启动过程中记录失败,那将是致命的。记录不是,也不应该是事后的想法。许多自动问题检测都依赖于日志输出,而没有那么严重。
该系列的最新日志记录框架Serilog于2013年发布。Serilog与其他框架之间的最大区别在于,该框架旨在进行结构化的现成日志记录。NLog还支持 结构化日志记录,但它只是在最近才添加 , 基准测试表明 使用它会严重影响性能。 编辑:似乎有一些问题与基准而 这一次 是一个更好的基础。
等待!什么是结构化日志记录?天哪,我以为你永远不会问。
通常,您会发现编写的日志包含两件事:消息和值。
例:
log.Debug($"User id is: ${userId}");
在大多数日志记录框架中,这只是转换为日志文件中的文本。文本很不错,但是知道记录的值称为userId并能够搜索该值实际上非常有用。Serilog保持属性一直可用到目的地。
log.Debug("User id is {@userId}", userId);
Serilog由一家商业公司https://getseq.net/提供支持,后者提供了一个非常不错的日志聚合工具。当然,您也可以将日志发送到我们最喜欢的日志聚合器:Retrace。Retrace还支持在日志中保留属性。
安装Serilog比NLog或log4net稍微复杂些,因为它努力做到高度模块化。对于我们的示例代码,您将需要软件包serilog,serilog.sinks.file和serilog.sinks.console。
这次无需任何XML配置!Serilog的配置使用流畅的界面,使其非常美观和干净。比较与log4net相比,设置滚动文件记录器有多么容易。
using Serilog; using System; namespace LoggingDemo.Serilog { class Program { static void Main(string[] args) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File("logfile.log", rollingInterval: RollingInterval.Day) .CreateLogger(); Log.Debug("Starting up"); Log.Debug("Shutting down"); Console.ReadLine(); } } } The logging output from Serilog looks like:
该 日志 输出 从 Serilog 看起来 像:
2018-08-18 14:37:21.463 -06:00 [DBG] Starting up 2018-08-18 14:37:21.560 -06:00 [DBG] Shutting down
Serilog中的结构化日志支持非常好,而且配置简便。我能够比其他任何人更轻松地启动和运行Seirlog。Serilog中的日志可以发送到很多目的地。Serilog称这些东西为“接收器”,您可以在https://github.com/serilog/serilog/wiki/Provided-Sinks中查看非常全面的列表。
我在Serilog中真正喜欢的另一个概念是浓缩器。这是一段针对每个日志请求运行的代码,向其中添加了新信息。例如,如果您需要在ASP.NET中包含有关请求的信息,则可以使用扩充器将属性附加到日志消息中。您甚至可以使用扩充程序来更改日志条目。例如,有一个丰富器可以提高堆栈跟踪的可理解性。
我保证我们将在本文结尾处给出一个建议,即:Serilog。该API更现代,更易于设置,维护更好,并且默认情况下进行结构化日志记录。添加浓缩器的功能使您能够拦截和修改消息,这非常有用。我选择Serilog作为最佳的.NET日志记录框架。
日志记录是任何应用程序的重要方面,不应被视为事后考虑。用右手启动您的应用程序并使用结构化日志记录。您还应该使用日志聚合器(如Retrace)在一个位置查看所有应用程序日志。