logging最佳实践
我想知道人们如何处理实际应用中的跟踪和日志logging。 这里有一些问题可能有助于解释你的答案。
构架
你用什么框架?
- log4net的
- System.Diagnostics.Trace
- System.Diagnostics.TraceSource
- logging应用程序块
- 其他?
如果您使用跟踪,您是否使用Trace.Correlation.StartLogicalOperation?
你是手动编写这个代码,还是使用某种forms的面向方面的编程来做到这一点? 小心分享一个代码片段?
你提供任何forms的痕量来源的粒度? 例如,WPF TraceSources允许你在不同的级别configuration它们:
- System.Windows – 所有WPF的设置
- System.Windows.Animation – 专门为animation覆盖。
听众
你使用什么日志输出?
- 文本文件
- XML文件
- 事件日志
- 其他?
如果使用文件,你使用滚动日志或只是一个文件? 你如何让日志可供人们消费?
查看
你用什么工具查看日志?
- 记事本
- 尾巴
- 事件查看器
- 系统中心运营经理/微软运营经理
- WCF服务跟踪查看器
- 其他?
如果您正在构buildASP.NET解决scheme,您是否还使用ASP.NET健康监控? 您是否将跟踪输出包含在健康监视器事件中? Trace.axd怎么样?
定制性能计数器呢?
更新:对于System.Diagnostics的扩展,提供您可能需要的一些缺less的监听器,请参阅CodePlex上的Essential.Diagnostics( http://essentialdiagnostics.codeplex.com/ )
构架
问:你使用什么框架?
答:内置到.NET 2.0的System.Diagnostics.TraceSource。
它为应用程序提供了function强大,灵活,高性能的日志logging,然而许多开发人员并没有意识到它的function,也没有充分利用它们。
有一些地方有些额外的function是有用的,或者有时function存在但没有很好的文档logging,但这并不意味着整个日志框架(被devise成可扩展的)应该被丢弃,并像一些stream行的替代品一样被完全替代(NLog,log4net,Common.Logging,甚至EntLib日志)。
不要改变将日志报表添加到应用程序并重新发明轮子的方式,只需在需要的地方扩展System.Diagnostics框架即可。
在我看来,其他框架,甚至是EntLib,都只是患有“不在这里发明综合症”,我认为他们浪费时间重新发明已经在System.Diagnostics中完美运行的基础知识(比如如何编写日志语句),而不是填补存在的less数差距。 总之,不要使用它们 – 它们不是必需的。
您可能不知道的function:
- 使用带格式string和参数的TraceEvent重载可以帮助性能,因为在Filter.ShouldTrace()成功之后,参数将保留为单独的引用。 这意味着在参数值上不需要昂贵的ToString()调用,直到系统确认消息被实际logging为止。
- Trace.CorrelationManager允许你关联相同逻辑操作的日志语句(见下文)。
- VisualBasic.Logging.FileLogTraceListener适用于写入日志文件并支持文件旋转。 尽pipe在VisualBasic命名空间中,只需通过包含DLL,就可以轻松地在C#(或其他语言)项目中使用它。
- 如果使用EventLogTraceListener,如果使用多个参数调用TraceEvent并使用空格式或空格式string,那么如果使用本地化的消息资源,那么参数将直接传递给EventLog.WriteEntry()。
- 服务跟踪查看器工具(来自WCF)可用于查看与活动相关的日志文件的graphics(即使您不使用WCF)。 这可以真正帮助debugging涉及多个线程/活动的复杂问题。
- 通过清除所有侦听器(或删除Default)来避免开销。 否则Default将把所有事情都传递给跟踪系统(并引发所有的ToString()开销)。
您可能想要查看的领域扩展(如果需要):
- 数据库跟踪监听器
- 彩色的控制台跟踪侦听器
- MSMQ / Email / WMI跟踪侦听器(如果需要)
- 实现FileSystemWatcher以调用Trace.Refresh以进行dynamicconfiguration更改
其他build议:
使用结构化的事件ID,并保留一个引用列表(例如,在一个枚举中logging它们)。
为系统中的每个(重大)事件设置独特的事件ID对于关联和查找特定问题非常有用。 追踪logging/使用事件ID的特定代码很容易,并且可以很容易地为常见错误提供指导,例如错误5178意味着数据库连接string错误等。
事件ID应该遵循某种结构(类似于电子邮件和HTTP中使用的回复代码原理),它允许您按照类别对待它们,而不必知道特定的代码。
例如,第一个数字可以详细说明一般类:1xxx可用于“开始”操作,2xxx用于正常行为,3xxx用于活动跟踪,4xxx用于警告,5xxx用于错误,8xxx用于“停止”操作,9xxx用于致命错误,等等
第二个数字可以详细说明数据库信息的21xx(数据库警告41xx,数据库错误51xx),计算模式22xx(计算警告等42xx),其他模块23xx等。
分配的结构化事件ID也允许您在filter中使用它们。
问:如果您使用跟踪,您是否使用Trace.Correlation.StartLogicalOperation?
答:Trace.CorrelationManager对于在任何types的multithreading环境(这几天几乎任何事情)中关联日志语句都非常有用。
您至less需要为每个逻辑操作设置一次ActivityId以关联。
启动/停止,然后LogicalOperationStack可以用于简单的基于堆栈的上下文。 对于更复杂的上下文(例如asynchronous操作),使用TraceTransfer到新的ActivityId(在改变之前)允许相关性。
服务跟踪查看器工具可用于查看活动图(即使您不使用WCF)。
问:你是手动编写这个代码,还是使用某种forms的面向方面的编程来做到这一点? 小心分享一个代码片段?
答:您可能需要创build一个范围类,例如LogicalOperationScope(a)在创build时设置上下文,(b)在处置时复位上下文。
这使您可以编写如下所示的代码来自动换行:
using( LogicalOperationScope operation = new LogicalOperationScope("Operation") ) { // .. do work here }
在创build时,范围可以首先根据需要设置ActivityId,调用StartLogicalOperation,然后loggingTraceEventType.Start消息。 在处置它可能会logging停止消息,然后调用StopLogicalOperation。
问:你提供任何forms的粒度超过痕量来源? 例如,WPF TraceSources允许您在各个层次configuration它们。
答:是的,随着系统变大,多个跟踪源是有用/重要的。
虽然您可能希望持续logging所有警告及以上,或所有信息及以上信息,但对于任何尺寸合理的系统,活动追踪(开始,停止等)和详细logging的数量变得太多了。
而不是只有一个开关将其全部打开或closures,一次可以打开系统某个部分的这些信息是非常有用的。
通过这种方式,您可以从通常的日志logging(所有警告,错误等)中find重要的问题,然后“放大”您想要的部分,并将其设置为“活动跟踪”或甚至“debugging”级别。
您需要的跟踪源的数量取决于您的应用程序,例如,您可能需要为每个程序集或应用程序的主要部分提供一个跟踪源。
如果你需要更好的调整控制,添加单独的布尔开关打开/closures特定的高容量跟踪,例如原始消息转储。 (或者可以使用一个单独的跟踪源,类似于WCF / WPF)。
您可能还需要考虑活动跟踪与一般(其他)日志logging的单独跟踪源,因为它可以使您按照自己的需要configurationfilter更容易一些。
请注意,即使使用不同的来源,仍然可以通过ActivityId关联消息,因此尽可能多地使用它们。
听众
问:你使用什么日志输出?
这可以取决于你正在写什么types的应用程序,以及正在logging什么东西。 通常不同的事情发生在不同的地方(即多个产出)。
我通常将输出分为三组:
(1)事件 – Windows事件日志(和跟踪文件)
例如,如果编写服务器/服务,那么在Windows上的最佳实践是使用Windows事件日志(您没有要报告的UI)。
在这种情况下,所有致命,错误,警告和(服务级别)信息事件应该转到Windows事件日志。 信息级别应该保留给这些types的高级事件,例如“服务启动”,“服务停止”,“连接到Xyz”,甚至可以是“计划启动” ,“用户login”等。
在某些情况下,您可能希望向应用程序的内置部分写入事件日志,而不是通过跟踪系统(即直接写入事件日志条目)。 这意味着它不会意外closures。 (请注意,您还需要logging跟踪系统中的相同事件,以便关联)。
相比之下,Windows GUI应用程序通常会向用户报告这些信息(尽pipe它们也可能会logging到Windows事件日志中)。
事件也可能具有相关的性能计数器(例如错误次数/秒),协调任何直接写入事件日志,性能计数器,写入跟踪系统并报告给用户以使其发生在同一时间。
即,如果用户在特定时间看到错误消息,则应该能够在Windows事件日志中find相同的错误消息,然后在跟踪日志中使用相同的时间戳(以及其他跟踪详细信息)find相同的事件。
(2)活动 – 应用程序日志文件或数据库表(和跟踪文件)
这是系统所做的常规活动,例如网页服务,股市交易,订单执行,执行计算等。
活动追踪(开始,停止等)在这里是有用的(在正确的粒度)。
此外,使用特定的应用程序日志(有时也称为审计日志)是非常普遍的。 通常这是一个数据库表或应用程序日志文件,并包含结构化数据(即一组字段)。
根据您的应用,事情可能会变得有点模糊。 一个很好的例子可能是将每个请求写入networking日志的Web服务器; 类似的例子可能是消息系统或计算系统,其中每个操作都与应用程序特定的细节一起logging。
股票市场交易或销售订购系统就是一个不太好的例子。 在这些系统中,您可能已经logging了具有重要业务价值的活动,但将其与其他活动关联起来的原则仍然很重要。
除了自定义应用程序日志,活动还经常具有相关的性能计数器,例如每秒事务数量。
一般来说,您应该协调跨不同系统的活动日志logging,即在增加性能计数器并login到跟踪系统的同时写入应用程序日志。 如果你在同一时间完成所有的工作(或者直接在代码中完成),那么debugging问题就比在代码中不同时间/位置发生的情况要容易得多。
(3)debugging跟踪 – 文本文件,或者XML或数据库。
这是详细级别和更低的信息(例如,自定义布尔开关打开/closures原始数据转储)。 这提供了系统在子活动级别正在做什么的内容或细节。
这是您希望能够打开/closures应用程序的各个部分(因此多个来源)的级别。 你不希望这个东西混乱了Windows事件日志。 有时使用数据库,但更有可能是滚动日志文件,在一段时间后清除。
这个信息和一个应用程序日志文件之间的一个很大的区别是它是非结构化的。 虽然应用程序日志可能包含To,From,Amount等字段,但详细的debugging跟踪可能是程序员放入的任何东西,例如“检查值X = {value},Y = false”或随机评论/标记,如“完成它,再试一次“。
一个重要的做法是确保您放入应用程序日志文件或Windows事件日志中的内容也使用相同的详细信息(例如时间戳)logging到跟踪系统。 这使您可以在调查时关联不同的日志。
如果您打算使用特定的日志查看器,因为您有复杂的相关性,例如服务跟踪查看器,那么您需要使用适当的格式,即XML。 否则,一个简单的文本文件通常是足够好的 – 在较低的层次上,信息在很大程度上是非结构化的,所以你可能会发现数组,堆栈转储等的转储。如果你可以关联到更高级别的更多结构化日志,应该好好地。
问:如果使用文件,你使用滚动日志还是只使用一个文件? 你如何让日志可供人们消费?
答:对于文件,通常您需要从可pipe理性的angular度来滚动日志文件(使用System.Diagnostics只需使用VisualBasic.Logging.FileLogTraceListener)。
可用性依赖于系统。 如果您只是在讨论文件,那么对于服务器/服务来说,滚动文件可以在必要时被访问。 (Windows事件日志或数据库应用程序日志将有自己的访问机制)。
如果您不便于访问文件系统,那么将跟踪debugging到数据库可能会更容易。 [即实现一个数据库TraceListener]。
我在Windows GUI应用程序中看到的一个有趣的解决scheme是,它在运行时将非常详细的跟踪信息logging到“飞行logging器”,然后当它closures时,如果没有问题,则简单地删除文件。
如果它崩溃或遇到问题,那么文件不会被删除。 要么捕捉到错误,要么在下一次运行时会注意到该文件,然后可以采取措施,例如压缩它(例如7zip)并通过电子邮件发送或以其他方式提供。
现在的许多系统都将故障自动报告给中央服务器(例如出于隐私原因与用户核对之后)。
查看
问:用什么工具查看日志?
答:如果您有多个日志出于不同的原因,那么您将使用多个查看器。
记事本/ vi /记事本++或任何其他文本编辑器是纯文本日志的基本。
如果你有复杂的操作,比如有传输的活动,那么显然你会使用一个专门的工具,比如服务跟踪查看器。 (但如果你不需要它,那么文本编辑器就更容易了)。
由于我通常将高级别信息logging到Windows事件日志中,因此它提供了一种以结构化方式(查找漂亮的错误/警告图标)进行概述的快速方法。 如果日志中没有足够的内容,只需要通过文本文件search,尽pipe至less日志会给出一个起点。 (在这一点上,确保你的日志有协调的有效性)。
一般来说,Windows事件日志也使这些重要的事件可用于像MOM或OpenView这样的监视工具。
其他 –
如果您login到数据库,可以很容易地过滤和sorting信息(例如,放大特定的活动ID)(对于文本文件,您可以使用Grep / PowerShell或类似的filter在你想要的partiular GUID上)
MS Excel(或其他电子表格程序)。 如果您可以使用正确的分隔符导入它,以便不同的值进入不同的列,这可以用于分析结构化或半结构化信息。
在debugging/testing中运行服务时,为了简单起见,我通常将其放在控制台应用程序中,我发现有一个有用的控制台logging器(例如红色表示错误,黄色表示警告等)。 您需要实现自定义跟踪侦听器。
请注意,该框架不包括彩色的控制台logging器或数据库logging器,所以,现在,如果需要,您可能需要编写这些logging(这并不难)。
这让我非常恼火,几个框架(log4net,EntLib等)浪费时间重新发明轮子,并重新实现了基本的日志logging,过滤和日志logging到文本文件,Windows事件日志和XML文件不同的方式(每个日志语句是不同的); 每一个都已经实现了自己的版本,例如,一个数据库logging器,当其中大部分已经存在,所需要的只是System.Diagnostics的几个跟踪侦听器。 谈论重复努力的巨大浪费。
问:如果您正在构buildASP.NET解决scheme,您是否也使用ASP.NET健康监控? 您是否将跟踪输出包含在健康监视器事件中? Trace.axd怎么样?
这些东西可以根据需要打开/closures。 我发现Trace.axd对于debugging服务器如何响应某些事情非常有用,但是在高度使用的环境或长期跟踪中通常不是很有用。
问:定制性能计数器如何?
对于一个专业的应用程序,特别是服务器/服务,我期望看到它充分利用性能监视器计数器和日志logging到Windows事件日志。 这些是Windows中的标准工具,应该使用。
您需要确保包含您使用的性能计数器和事件日志的安装程序; 这些应该在安装时创build(当以pipe理员身份安装时)。 当你的应用程序正常运行时,它不应该需要pipe理权限(所以不能创build缺less的日志)。
这是以非pipe理员身份进行开发的一个很好的理由(当你需要安装服务时有一个单独的pipe理员帐户等)。 如果写入事件日志,.NET会在您第一次写入时自动创build一个缺less的日志; 如果您是以非pipe理员身份进行开发,那么您可以尽早采取措施,避免在客户安装系统时出现令人不愉快的惊喜,并且因为没有以pipe理员身份运行而无法使用。
我必须join合唱团推荐的log4net,在我的情况下来自平台的灵活性(桌面.Net / Compact Framework,32/64-bit)的观点。
但是,将其封装在私有标签API中是一种主要的反模式 。 log4net.ILogger
是Commons Logging包装器API的.Net对应件,所以耦合已经最小化了,因为它也是一个Apache库,所以通常不会担心,因为你不放弃任何控制:fork如果你必须。
我见过的大多数封装类库也提交了一个或多个错误:
- 使用全球单点login器 (或等同于静态入口点),其失去了build议的每类logging器的精细分辨率,没有其他select性增益。
- 无法公开可选的
Exception
参数 ,导致多个问题:- 这使得exception日志logging策略更加难以维护,因此没有任何事情可以与exception一致。
- 即使使用一致的策略,将exception格式化为string也会过早丢失数据。 我写了一个定制的
ILayout
装饰器,对exception执行详细的深入分析以确定事件链。
- 无法公开
Is Level Enabled
了Is Level Enabled
属性 ,这些属性在closureslogging的区域或级别时会丢弃跳过格式化代码的function。
我不经常在asp.net中开发,但是对于伐木工人来说,我认为很多最佳实践是普遍的。 以下是我多年来对日志logging的一些随意想法:
构架
- 使用一个logging器抽象框架 – 就像slf4j(或者你自己的),这样你就可以将logging器的实现从API中分离出来。 我已经看到了一些logging器框架的来来去去,你最好能够采取一个新的没有太多的麻烦。
- 尝试find一个支持各种输出格式的框架。
- 尝试find支持插件/自定义filter的框架。
- 使用一个可以由外部文件configuration的框架,以便您的客户/消费者可以轻松调整日志输出,以便轻松地通过商业日志pipe理应用程序读取它。
- 一定不要在自定义日志logging级别上放手,否则可能无法移动到不同的日志logging框架。
logging器输出
- 尝试避免可能遇到灾难性故障的日志loggingXML / RSS样式。 这很重要,因为如果没有logging器写入closures
</xxx>
标记,closures电源开关,则日志将被破坏。 - logging线程。 否则,追踪程序的stream程将非常困难。
- 如果您必须国际化您的日志,您可能希望开发人员只能使用英文(或您select的语言)login。
- 有时可以select将日志语句插入到SQL查询中,这可以成为debugging情况的救星。 如:
- 调用类:com.foocorp.foopackage.FooClass:9021 SELECT * FROM foo;
- 你想要类级别的日志logging。 您通常不希望logging器的静态实例 – 这是微不足道的优化。
- 标记和分类logging的exception有时很有用,因为并不是所有的exception都是相同的。 因此,如果您有一个需要在关键状态下发送通知的日志监视器,那么了解重要exception的一个子集,则时间头是有帮助的。
- 重复filter将保存您的视力和硬盘。 你真的想看到相同的日志语句重复10 ^ 10000000次吗? 如果得到这样的信息不是更好:
This is my logging statement - Repeated 100 times
也看到我的这个问题 。
我没有资格对.Net的日志logging发表评论,因为我的面包和黄油是Java,但是在过去的8年中,我们在日志logging中进行了一次迁移,您可能会发现对您的问题有用的类比。
我们开始使用JVM中每个线程使用的单例logging器,并为整个过程设置日志级别。 如果我们必须debugging系统的一个非常特殊的部分,那么这会导致大量的日志,因此第一课就是对日志进行细分。
我们目前的logging器的化身允许一个定义为默认的多个实例。 我们可以实例化具有不同日志logging级别的任意数量的子日志logging器,但是这种体系结构最有用的方面是通过简单地更改日志logging属性来为单个包和类创buildlogging器。 第二课是创build一个灵活的系统,可以在不改变代码的情况下覆盖它的行为。
我们正在使用围绕Log4J的Apache commons-logging库。
希望这可以帮助!
*编辑*
在阅读下面的Jeffrey Hantin的post后,我意识到我应该注意到我们的内部日志封装实际上已经变成了什么。 它现在基本上是一个工厂,并严格用于使用正确的属性文件(由于遗留原因尚未移动到默认位置)获取工作logging器。 既然你现在可以在命令行上指定日志configuration文件,我怀疑它会变得更加精简,如果你开始一个新的应用程序,我肯定会同意他的说法,你甚至不应该打扰包装logging器。
我们使用Log4Net作为日志logging提供程序,为日志实例提供单例包装(尽pipe单例正在审查中,质疑是否是个好主意)。
我们之所以select它是因为以下原因:
- 简单的configuration/重新configuration各种环境
- 良好的预制appenders数量
- 我们使用的一个CMS已经内置了
- 很好的日志级别和周围的configuration
我应该提一下,这是从ASP.NET开发的angular度来讲的
我可以看到使用.NET框架中的Trace的一些优点,但是我并没有完全销售它,主要是因为我使用的组件并不真正执行任何跟踪调用。 我唯一经常使用的就是System.Net.Mail
。
所以我们有一个包装log4net的库,在我们的代码中我们只需要这样的东西:
Logger.Instance.Warn("Something to warn about"); Logger.Instance.Fatal("Something went bad!", new Exception()); try { var i = int.Parse("Hello World"); } catch(FormatException, ex) { Logger.Instance.Error(ex); }
在这些方法中,我们检查是否启用了日志logging级别,所以没有对log4net API进行多余的调用(所以如果Debug未启用,debugging语句将被忽略),但是当我花些时间我会更新它来揭露这些,以便你可以自己做检查。 这将阻止在不应该进行的评估,例如:
Logger.Instance.Debug(string.Format("Something to debug at {0}", DateTime.Now);
这将成为:
if(Logger.DebugEnabled) Logger.Instance.Debug(string.Format("Something to debug at {0}", DateTime.Now);
(节省一点时间)
默认情况下,我们在两个位置login:
- 网站的文件系统(在未提供文件的扩展名中)
- 电子邮件发送错误和致命
文件是每天滚动或10MB(IIRC)。 我们不使用EventLog,因为它可能需要比我们经常想要给站点更高的安全性。
我发现记事本工作正常阅读日志。
你用什么框架?
我们使用日志logging应用程序块和一个围绕.Net框架位工作的自定义日志助手。 LABconfiguration为输出相当广泛的日志文件,包括用于服务方法进入/退出的单独一般跟踪文件以及用于意外问题的特定错误文件。 configuration包括date/时间,线程,pId等,用于debugging帮助以及完整的exception详细信息和堆栈(在出现意外exception的情况下)。
自定义日志助手使用Trace.Correlation,在loginWF的情况下特别方便。 例如,我们有一个调用一系列顺序工作stream的状态机。 在每个这些调用活动中,我们logging开始(使用StartLogicalOperation),然后在最后我们用gereric返回事件处理程序停止逻辑操作。
在尝试debugging复杂业务序列中的故障时,这已经certificate是有用的,因为它允许我们根据活动执行顺序更快速地确定诸如“否”或“否”分支决策等事情。
你使用什么日志输出?
我们使用文本文件和XML文件。 文本文件是通过应用程序块configuration的,但是我们的WF服务也有XML输出。 这使我们能够捕获运行时事件(持久性等)以及通用的业务typesexception。 文本文件是按date和大小滚动的滚动日志(我相信总大小为1MB是翻转点)。
你用什么工具查看日志?
我们正在使用记事本和WCF服务跟踪查看器,具体取决于我们正在查看的输出组。 如果您的输出设置正确并且可以使读取输出更简单,那么WCF服务跟踪查看器确实非常方便。 也就是说,如果我大概知道错误在哪里 – 只要阅读一个注释良好的文本文件也是好的。
日志被发送到一个单独的目录,然后根据源服务将其分割成子目录。 根目录是通过一个由支持用户组访问控制的网站公开的。 这使我们可以查看生产日志,而无需发出请求,并通过冗长繁琐的繁琐的生产数据处理stream程。
作为该工具的作者,我们当然使用SmartInspect来logging和跟踪.NET应用程序。 我们通常使用命名pipe道协议进行实时日志logging,并为最终用户日志使用(encryption)二进制日志文件。 我们使用SmartInspect控制台作为查看器和监控工具。
实际上有很多用于.NET的日志框架和工具。 DotNetLogging.com上有不同工具的概述和比较。
答案中有很多很棒的build议。
一般的最佳做法是考虑谁将阅读日志。 在我的情况下,它将是客户端的pipe理员。 所以我会logging消息,给他们一些他们可以采取行动的东西。 例如,“无法初始化应用程序,这通常是由……引起的”
我们在我们的web应用程序上使用log4net。
通过在运行时更改XMLconfiguration文件非常方便,您可以在运行时自定义日志logging,并且需要查看更多信息。
它还允许您将特定的类或属性作为目标进行login。 当你知道错误发生的位置时,这非常方便。 一个经典的例子是NHibernate,你只想看到SQL去数据库。
编辑:
我们将所有事件写入数据库和Trace系统。 我们用于错误或exception的事件日志。 我们将大多数事件logging到数据库中,以便我们可以创build自定义报告,并让用户查看日志,如果他们想要从应用程序中正确的。
至于面向方面的日志logging,我被推荐在另一个SO问题PostSharp –
面向方面日志与Unity \ T4 \其他任何东西
如果您正在评估日志框架,那么在答案中提供的链接值得一看。