Log4net for .NET日志记录:您唯一需要了解的教程和14个技巧
如果您已经在一段合理的时间内编写了代码,那么您根本就不可能以任何方式处理日志,因为它是现代“现实生活”应用开发中最重要的部分之一。
如果您是.NET开发人员,那么您可能已经使用了许多可以在该平台上使用的著名日志记录框架。今天的帖子将介绍以下框架之一:log4net。
日志记录入门以及日志记录框架的概念可能是一项艰巨的任务。这篇文章将对log4net进行温和而完整的介绍。
学习完本教程后,您将掌握log4net的内容,如何安装和使用它以及应该尝试采用的最重要的最佳实践。让我们开始吧。
什么是log4net,为什么要使用它或任何C#日志记录框架?
在深入探讨如何使用log4net之前,我们需要了解为什么会这样。
那么,什么是log4net?
Log4net是.NET平台的日志记录框架。绝对不是唯一的一个,但它是其中最受欢迎的框架之一。
日志记录框架是一种可以大大减少处理日志的负担的工具。
PM> Install-Package log4net
2.添加log4net.config文件
在Visual Studio中将新文件添加到项目中,名为log4net.config,并确保为文件设置属性。将“复制到输出目录”设置为“始终复制”。这很重要,因为在构建和运行应用程序时,我们需要将log4net.config文件复制到bin文件夹中。
3.告诉log4net加载您的配置
我们需要做的下一件事是告诉log4net从何处加载其配置,以使其真正起作用。我建议将其放在您的AssemblyInfo.cs文件中。
您可以在项目的“属性”部分下找到它:
(assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config"))
4.记录一下
现在,您可以修改您的应用以登录并尝试一下
class Program { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); static void Main(string() args) { log.Info("Hello logging world!"); Console.WriteLine("Hit enter"); Console.ReadLine(); } }
Log Appender:它们是什么,您需要知道哪些
追加者是您如何将日志发送到的位置。最受欢迎的标准附加程序很可能是RollingFileAppender和ConsoleAppender。
如果您想在Visual Studio调试窗口中查看日志语句,也可以尝试使用DebugAppender,这样就不必打开日志文件。
如果您使用的是控制台,请检出ColoredConsoleAppender。
用于log4net的彩色控制台
充分利用多个日志级别并按其筛选
确保在代码中适当地使用调试,信息,警告,错误和致命日志记录级别。
不要将所有内容都记录为Debug。在编写日志记录语句时,请务必考虑一下如何查看日志以及以后要查看的内容。
您可以在log4net配置中指定要记录的log4net日志记录级别。
如果您只想指定要记录到特定日志附加程序的特定级别或减少生产中的日志记录,这真的很有价值。这使您无需更改代码即可记录更多或更少的数据。
log4net级别:
- 全部(记录所有内容)
- 除错
- 信息
- 警告
- 错误
- 致命
- 关闭(不记录任何内容)
高级主题和log4net最佳实践
1.将LogManager对象定义为静态
在代码中声明任何变量都有开销。过去我进行一些性能分析会话以优化代码时,我注意到LogManager对象上的构造函数可能会占用大量CPU。
将其声明为静态,并使用此小技巧,因此您不必对类类型进行硬编码。
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
2.如何启用log4net自己的内部调试日志记录
有时,您可能对特定的附加程序有疑问,或者在使用特定的附加程序时遇到问题。
为了帮助解决这些问题,请通过web.config文件启用内部log4net日志记录。
You can then specify where the logging is written to. ... ...
3.不要使用AdoAppender将日志发送到数据库表
如果您记录任何实际成交量,则尝试在SQL中查询日志非常困难。您最好将日志发送到Elasticsearch或可以为日志提供全文索引和更多功能的日志管理服务。
4.不要在每次异常时发送电子邮件
您要做的最后一件事是从附加程序发送各种电子邮件。随着时间的推移,它们要么被忽略,要么开始引发大量异常,然后您的应用程序开始发送数千个错误。虽然,如果您确实要执行此操作,则有一个SmtpAppender。
5.如何发送异常警报
如果要发送有关异常的警报,请将异常发送到为此设计的错误跟踪产品(如Retrace)。
他们还可以消除您的错误,从而您可以确定何时确实是一个新错误,跟踪其历史记录以及跟踪错误率。
6.将您的日志发送到日志管理系统以跨服务器查看它们
捕获日志并将其记录到磁盘上的文件很棒。但是,如果要跨多个服务器和应用程序搜索日志,则需要将所有日志发送到中央存储库。
有很多日志管理解决方案可以帮助您解决此问题,或者甚至可以为其设置自己的elasticsearch集群。
在这里,您可以按日志级别进行过滤:
8.您可以使自己的自定义log4net Appenders
如果要执行标准附加程序不支持的操作,则可以在线搜索一个或编写自己的内容。
一个示例可能是用于写入Azure存储的附加程序。曾几何时,我们编写了一个日志以将日志发送到Azure Table存储以对其进行中心化。由于缺少全文本索引,我们无法真正查询它们,但我们可以查看它们。
作为自定义追加程序的示例,您可以查看我们的追加程序的源代码,以将日志发送到Retrace。
9.使用log4net模式布局自定义日志中的布局
您可以修改配置,以使用模式布局更改要输出的字段和格式。
使用上面的布局,输入级别(%p),当前日期时间(%d),线程号(%thread),消息(%m)和换行(%n)。 %-5p中的-5用于将字段的宽度设置为5个字符。
您可以记录以下一些其他值得注意的字段,尽管它们可能会对您的应用程序产生很大的性能影响,并且不建议在生产应用程序上进行大量日志记录。
- %method:写入日志消息的方法的名称
- %stacktrace {level}:输出堆栈跟踪以显示日志消息的写入位置
- %type:发出日志请求的呼叫者的类型。通常是您的班级名称
- %line:记录日志语句的行号
这样的布局:
产生这样的日志消息:
ERROR 2017-02-06 09:38:10 – (10) Error downloading web request method:ThrowWebException stacktrace:Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly > System.AppDomain.ExecuteAssembly > System.AppDomain._nExecuteAssembly > ConsoleApplication1.Program.Main > ConsoleApplication1.Class1.ThrowWebException type:ConsoleApplication1.Class1 line: 26
10.使用诊断上下文记录其他字段
您还可以记录自定义字段,以帮助提供有关与日志语句相关的用户,客户或交易的更多上下文。
下面的示例设置一个名为customer的自定义属性。然后,您可以修改log4net模式布局以包括%property {customer},以将其输出到日志中。
log4net.ThreadContext.Properties("customer") = "My Customer Name"; log.Debug("We are going to try and do a web request"); try { Class1.ThrowWebException(); } catch (Exception ex) { log.Error("Error trying to do something", ex); } log.Debug("We are done with the web request");
11.如何通过Web请求事务关联日志消息
此外,您可以在上下文中分配对象以使用其所谓的“活动属性值”。写入日志消息时,将调用ToString()方法,该方法可以动态地执行操作。
这可用于编写事务ID,以帮助将消息与Web请求或事务相关联
//Create our little helper class public class ActivityIdHelper { public override string ToString() { if (Trace.CorrelationManager.ActivityId == Guid.Empty) { Trace.CorrelationManager.ActivityId = Guid.NewGuid(); } return Trace.CorrelationManager.ActivityId.ToString(); } }
在您的global.asax或初创公司.cs类中,订阅一个请求首次启动时的事件。
public override void Init() { base.Init(); this.Error += WebApiApplication_Error; this.BeginRequest += WebApiApplication_BeginRequest; this.EndRequest += WebApiApplication_EndRequest; } void WebApiApplication_BeginRequest(object sender, EventArgs e) { //set the property to our new object log4net.LogicalThreadContext.Properties("activityid") = new ActivityIdHelper(); log.Debug("WebApiApplication_BeginRequest"); }
如果您将%property {activity}添加到模式布局中,则现在可以在日志中看到事务ID,如下所示。
您的日志消息看起来仍然像是意大利面条,但是至少您可以轻松地看到哪些消息在一起。
DEBUG 02-06 02:51:58.6347 – a69640f7-d47d-4aa4-99c9-13cfd9ab93c2 WebApiApplication_BeginRequest DEBUG 02-06 02:51:58.6382 – a69640f7-d47d-4aa4-99c9-13cfd9ab93c2 Starting KitchenAsync - Call redis DEBUG 02-06 02:51:58.9315 – b8a3bcee-e82e-4298-b27f-6481b256b5ad Finished KitchenAsync DEBUG 02-06 02:51:59.1285 – a69640f7-d47d-4aa4-99c9-13cfd9ab93c2 Call Webclient DEBUG 02-06 02:51:59.1686 – 54218fab-bd1b-4c77-9ff8-ebef838dfb82 WebApiApplication_BeginRequest DEBUG 02-06 02:51:59.1746 – 54218fab-bd1b-4c77-9ff8-ebef838dfb82 Starting KitchenAsync - Call redis DEBUG 02-06 02:51:59.4378 – a69640f7-d47d-4aa4-99c9-13cfd9ab93c2 Finished KitchenAsync DEBUG 02-06 02:51:59.6450 – 54218fab-bd1b-4c77-9ff8-ebef838dfb82 Call Webclient DEBUG 02-06 02:51:59.9294 – 54218fab-bd1b-4c77-9ff8-ebef838dfb82 Finished KitchenAsync
12.如何记录ASP.NET请求详细信息
您可以使用与上述相同的策略来动态获取ASP.NET请求信息以添加到您的日志消息中。
public class WebRequestInfo { public override string ToString() { return HttpContext.Current?.Request?.RawUrl + ", " + HttpContext.Current?.Request?.UserAgent; } } void WebApiApplication_BeginRequest(object sender, EventArgs e) { log4net.LogicalThreadContext.Properties("activityid") = new ActivityIdHelper(); log4net.LogicalThreadContext.Properties("requestinfo") = new WebRequestInfo(); log.Debug("WebApiApplication_BeginRequest"); }
13.如何进行结构化日志记录或使用消息记录对象或属性
默认情况下,您可以将对象记录到该对象,它将使用其默认渲染器对其进行序列化。
log.Debug(new {color="red", int1 = 1});
输出:
DEBUG 2017-02-06 15:07:25 – (8) { color = red, int1 = 1 }
但是,如果您想将整个日志消息记录为JSON怎么办?
有几个与log4net和JSON相关的nuget程序包,但是对它们的支持和文档似乎有点粗略。
我建议您仅制作自己的JsonLayout类即可。 GitHub上有一个很好的示例。然后,您可以精确控制如何记录JSON以及记录哪些字段。
GitHub JsonLayout的输出:
{ "processSessionId" : "225ba696-6607-4abc-95f6-df8e0438e898", "level" : "DEBUG", "messageObject" : "Finished KitchenAsync", "renderedMessage" : "Finished KitchenAsync", "timestampUtc" : "2017-02-06T21:20:07.5690494Z", "logger" : "WebApp2.Controllers.TestController", "thread" : "69", "exceptionObject" : null, "exceptionObjectString" : null, "userName" : "IIS APPPOOL\WebApp2", "domain" : "/LM/W3SVC/1/ROOT/WebApp2-10-131308895921693643", "identity" : "https://stackify.com/", "location" : "WebApp2.Controllers.TestController+d__27.MoveNext(C:\BitBucket\PrefixTests\WebApp2\Controllers\TestController.cs:477)", "pid" : 14428, "machineName" : "LAPTOP-1UJ70V4E", "workingSet" : 352481280, "osVersion" : "Microsoft Windows NT 10.0.14393.0", "is64bitOS" : true, "is64bitProcess" : true, "properties" : { "requestinfo" : {}, "activityid" : {}, "log4net:UserName" : "IIS APPPOOL\WebApp2", "log4net:Identity" : "https://stackify.com/", "log4net:HostName" : "LAPTOP-1UJ70V4E" } }
如果您想真正获得结构化日志的价值,则需要将日志发送到日志管理工具,该工具可以索引所有字段并启用强大的搜索和分析功能。
在此处了解更多信息:什么是结构化日志以及开发人员为何需要它。
14.如何通过ASP.NET Web请求查看log4net C#日志
日志文件很快就会变成一团糟的日志消息。尤其是对于包含大量AJAX请求的Web应用程序,它们都进行日志记录。
我强烈建议您使用Prefix,Stackify的免费.NET Profiler查看每个Web请求的日志以及SQL查询,HTTP调用等。
开始尽快记录
日志记录是现代软件开发的重要组成部分。
如今,将软件部署到生产环境而不使用任何类型的日志记录将是不可想象的。这样做相当于在高峰时段被蒙住眼睛在一个大城市里散步。
软件是一件非常复杂的事情。
当您发布应用程序并将其部署到(可能)未知的环境中时,您不确定是否一切都会按预期进行。
如果出现问题(也可能会发生问题),日志记录是您可以“追溯过去”,了解问题并加以解决的几种方法之一。
今天的帖子介绍了log4net日志记录框架。您了解了什么是日志记录框架,以及它们如何减轻开发人员不得不提出日志记录策略时可能面临的负担。
我们还共享了最佳实践和技巧的列表,您可以立即开始采用它们,以使使用log4net的旅程更加轻松。
感谢您的阅读,下次见。