记录最佳实践
我想了解人们如何处理实际应用中的跟踪和日志记录。 以下是可能有助于解释您的答案的一些问题。
构架
你使用什么框架?
如果您使用跟踪,您是否使用Trace.Correlation.StartLogicalOperation?
你是手动编写这段代码,还是使用某种形式的面向方面的编程来完成它? 小心分享一段代码片段?
你提供任何形式的粒度超过痕量来源? 例如,WPF TraceSources允许您在各种级别配置它们:
听众
你使用什么日志输出?
如果使用文件,你使用滚动日志还是只使用一个文件? 你如何让日志可供人们消费?
查看
你用什么工具查看日志?
如果您正在构建ASP.NET解决方案,您是否也使用ASP.NET健康监控? 您是否将跟踪输出包含在健康监视器事件中? Trace.axd怎么样?
定制性能计数器呢?
更新:对于System.Diagnostics扩展,提供您可能需要的一些缺少的侦听器,请参阅CodePlex上的Essential.Diagnostics(http://essentialdiagnostics.codeplex.com/)
构架
问:你使用什么框架?
答:内置于.NET 2.0的System.Diagnostics.TraceSource。
它为应用程序提供了强大,灵活和高性能的日志记录,然而许多开发人员并不知道其功能,也没有充分利用它们。
有些地方有些附加功能是有用的,或者有时功能存在但没有很好的文档记录,但这并不意味着整个日志框架(它被设计成可扩展的)应该被丢弃并且完全替换为一些流行的替代方案(NLog,log4net,Common.Logging,甚至EntLib日志记录)。
不要改变将日志记录添加到应用程序并重新发明轮子的方式,只需在几个需要它的地方扩展System.Diagnostics框架即可。
在我看来,其他框架,甚至是EntLib,都只是患有本文未发明的综合症,而且我认为它们浪费时间重新发明已经在System.Diagnostics中完美运行的基础知识(例如如何编写日志语句),而不是填补现有的少数差距。 总之,不要使用它们 - 它们不是必需的。
您可能不知道的功能:
您可能想要查看的领域扩展(如果需要):
其他建议:
使用结构化的事件ID,并保留一个引用列表(例如,将它们记录在一个枚举中)。
对系统中的每个(重要)事件拥有唯一的事件ID对于关联和查找特定问题非常有用。 追踪记录/使用事件id的特定代码很容易,并且可以很容易地为常见错误提供指导,例如错误5178意味着数据库连接字符串错误等。
事件ID应该遵循某种结构(类似于电子邮件和HTTP中使用的回复代码理论),它允许您在不知道特定代码的情况下按类别对待它们。
例如,第一个数字可以详细说明一般类:1xxx可以用于'开始'操作,2xxx用于正常行为,3xxx用于活动跟踪,4xxx用于警告,5xxx用于错误,8xxx用于'停止'操作,9xxx用于致命错误,等等
第二位数字可以详细说明该区域,例如数据库信息21xx(数据库警告41xx,数据库错误51xx),计算模式22xx(计算警告等42xx),其他模块23xx等。
分配的结构化事件ID也允许您在过滤器中使用它们。
问:如果您使用跟踪,您是否使用Trace.Correlation.StartLogicalOperation?
答:Trace.CorrelationManager对于在任何类型的多线程环境中关联日志语句非常有用(这几乎是任何事情)。
您至少需要为每个逻辑操作设置一次ActivityId以关联。
启动/停止和LogicalOperationStack可以用于简单的基于堆栈的上下文。 对于更复杂的上下文(例如异步操作),将TraceTransfer用于新的ActivityId(在更改之前)允许进行关联。
服务跟踪查看器工具可用于查看活动图(即使您未使用WCF)。
问:你是手动编写这段代码,还是使用某种形式的面向方面的编程来完成它? 小心分享一段代码片段?
答:您可能想要创建一个范围类,例如LogicalOperationScope,它可以:(a)在创建时设置上下文,(b)在处置时复位上下文。
这使您可以编写如下所示的代码来自动换行操作:
using( LogicalOperationScope operation = new LogicalOperationScope("Operation") )
{
// .. do work here
}
在创建时,范围可以根据需要首先设置ActivityId,调用StartLogicalOperation,然后记录TraceEventType.Start消息。 在Dispose上它可以记录Stop消息,然后调用StopLogicalOperation。
问:您是否提供了痕迹源的任何形式的粒度? 例如,WPF TraceSources允许您在各个级别配置它们。
答:是的,随着系统变大,多个跟踪源是有用/重要的。
虽然您可能希望持续记录所有Warning及以上,或所有信息及以上信息,但对于任何尺寸合理的系统,活动追踪(开始,停止等)和详细记录的数量变得太多。
而不是只有一个开关将其全部打开或关闭,一次可以打开系统某个部分的这些信息非常有用。
通过这种方式,您可以从通常的日志记录(所有警告,错误等)中找到重要问题,然后“放大”您想要的部分,并将它们设置为“活动跟踪”或甚至“调试”级别。
您需要的跟踪源数量取决于您的应用程序,例如,您可能需要为每个程序集或应用程序的主要部分提供一个跟踪源。
如果您需要更加精细的调整控制,请添加单个布尔开关来打开/关闭特定的高容量跟踪,例如原始消息转储。 (或者可以使用单独的跟踪源,类似于WCF / WPF)。
您可能还需要考虑活动跟踪与一般(其他)日志记录的单独跟踪源,因为它可以使您按照自己的期望精确配置过滤器。
请注意,即使使用不同的来源,仍然可以通过ActivityId关联消息,因此尽可能多地使用您需要的消息。
听众
问:你使用什么日志输出?
这可以取决于你正在写什么类型的应用程序,以及正在记录什么东西。 通常不同的事情发生在不同的地方(即多个输出)。
我通常将输出分为三组:
(1)事件 - Windows事件日志(和跟踪文件)
例如,如果编写服务器/服务,那么Windows上的最佳做法是使用Windows事件日志(您没有要报告的UI)。
在这种情况下,所有致命,错误,警告和(服务级别)信息事件都应该转到Windows事件日志。 应为这些类型的高级别事件保留信息级别,即您想要进入事件日志中的事件,例如“服务已启动”,“服务已停止”,“已连接到Xyz”,甚至可能是“启动计划” ,“用户登录”等。
在某些情况下,您可能想要向应用程序的内置部分写入事件日志,而不是通过跟踪系统(即直接写入事件日志条目)。 这意味着它不会意外关闭。 (请注意,您还需要记录跟踪系统中的相同事件,以便关联)。
相比之下,Windows GUI应用程序通常会向用户报告这些信息(尽管它们也可能会记录到Windows事件日志中)。
事件还可能有相关的性能计数器(例如错误次数/秒),重要的是协调直接写入事件日志,性能计数器,写入跟踪系统并向用户报告,以便它们发生在同一时间。
即,如果用户在特定时间看到错误消息,则应该能够在Windows事件日志中找到相同的错误消息,然后在跟踪日志中具有相同时间戳的相同事件(以及其他跟踪详细信息)。
(2)活动 - 应用程序日志文件或数据库表(和跟踪文件)
这是系统所做的常规活动,例如网页服务,股票交易交易,订单交易,执行的计算等。
活动追踪(开始,停止等)在这里很有用(在正确的粒度)。
另外,使用特定的应用程序日志(有时也称为审计日志)非常常见。 通常这是一个数据库表或应用程序日志文件,并且包含结构化数据(即一组字段)。
取决于您的应用,事情可能会变得有点模糊。 一个很好的例子可能是将每个请求写入网络日志的Web服务器; 类似的例子可能是一个消息传递系统或计算系统,其中每个操作都会记录下特定于应用程序的细节。
股市交易或销售订购系统就不是一个好例子。 在这些系统中,您可能已经记录了具有重要商业价值的活动,但将其与其他活动关联起来的原则仍然很重要。
除了自定义应用程序日志,活动还经常有相关的性能计数器,例如每秒事务数。
一般来说,您应该协调跨不同系统的活动日志记录,即在增加性能计数器并登录到跟踪系统的同时写入应用程序日志。 如果你同时做所有事情(或者直接在代码中),那么调试问题就比较容易(比它们都发生在代码中不同的时间/位置上)。
(3)调试跟踪 - 文本文件,或者XML或数据库。
这是详细级别和更低级别的信息(例如,用于打开/关闭原始数据转储的自定义布尔开关)。 这提供了系统在子活动级别正在做什么的内容或细节。
这是您希望能够打开/关闭应用程序各个部分(因此为多个来源)的级别。 你不希望这些东西混淆Windows事件日志。 有时候会使用数据库,但更有可能的是在特定时间后清除日志文件。
这个信息和应用程序日志文件之间的一个很大的区别在于它是非结构化的。 虽然应用程序日志可能包含To,From,Amount等字段,但详细调试跟踪可能是程序员放入的任何内容,例如“检查值X = {value},Y = false”或随机评论/标记,如“完成它,再试一次“。
一个重要的做法是确保您放入应用程序日志文件中的内容或Windows事件日志也使用相同的详细信息(例如时间戳)记录到跟踪系统。 这使您可以在调查时关联不同的日志。
如果您打算使用特定的日志查看器,因为您有复杂的关联,例如服务跟踪查看器,那么您需要使用适当的格式,即XML。 否则,一个简单的文本文件通常是足够好的 - 在较低的层次上,信息在很大程度上是非结构化的,所以你可能会发现数据转储,堆栈转储等等。假如你可以在更高层次上关联回更多结构化日志,事情应该好好地。
问:如果使用文件,你使用滚动日志还是只使用一个文件? 你如何让日志可供人们消费?
答:对于文件,通常需要从可管理性的角度来滚动日志文件(使用System.Diagnostics只需使用VisualBasic.Logging.FileLogTraceListener)。
可用性依赖于系统。 如果您只是在讨论文件,那么对于服务器/服务来说,可以在必要时访问滚动文件。 (Windows事件日志或数据库应用程序日志将拥有自己的访问机制)。
如果您无法轻松访问文件系统,那么将跟踪调试到数据库可能会更容易。 [即实现一个数据库TraceListener]。
我在Windows GUI应用程序中看到的一个有趣的解决方案是,它在运行时将非常详细的跟踪信息记录到“飞行记录器”,然后当它关闭时如果它没有问题,那么它只是删除文件。
如果它崩溃或遇到问题,则该文件不会被删除。 要么它捕捉到错误,要么下一次运行它会注意到该文件,然后它可以采取行动,例如压缩它(例如7zip)并通过电子邮件发送或以其他方式提供。
现在,许多系统都将自动向中央服务器报告故障(在与用户核对之后,例如出于保密原因)。
查看
问:用什么工具查看日志?
答:如果您因不同的原因有多个日志,那么您将使用多个查看器。
记事本/ vi /记事本++或任何其他文本编辑器是纯文本日志的基本。
如果你有复杂的操作,比如有传输的活动,那么显然你会使用一个专门的工具,比如服务跟踪查看器。 (但是如果你不需要它,那么文本编辑器就更容易了)。
由于我通常会将高级别信息记录到Windows事件日志中,因此它提供了一种以结构化方式(查找漂亮的错误/警告图标)进行概述的快速方法。 如果日志中没有足够的内容,只需要通过文本文件搜索即可,不过至少日志会给出一个起点。 (在这一点上,确保你的日志有协调的有效性)。
通常Windows事件日志也使这些重要事件可用于监视工具,如MOM或OpenView。
其他 -
如果您登录到数据库,可以很容易地过滤和排序信息(例如放大特定的活动ID(对于文本文件,您可以使用Grep / PowerShell或类似的过滤器)
MS Excel(或其他电子表格程序)。 如果您可以使用正确的分隔符导入它,以便不同的值出现在不同的列中,这可以用于分析结构化或半结构化信息。
在调试/测试中运行服务时,为了简单起见,我通常将其托管在控制台应用程序中,我发现一个有颜色的控制台记录器很有用(例如,红色表示错误,黄色表示警告等)。 您需要实现自定义跟踪侦听器。
请注意,该框架不包括彩色控制台记录器或数据库记录器,所以,现在,如果需要它们,您需要编写这些记录(这并不难)。
它让我很烦恼几个框架(log4net,EntLib等)浪费时间重新发明轮子,并重新实现了基本的日志记录,过滤和日志记录到文本文件,Windows事件日志和XML文件,每个都在他们自己的不同的方式(每个日志语句都不相同); 每个人都已经实现了他们自己的版本,例如,一个数据库记录器,当其中大部分已经存在时,所需要的只是System.Diagnostics的几个跟踪侦听器。 谈论重复努力的巨大浪费。
问:如果您正在构建ASP.NET解决方案,您是否也使用ASP.NET健康监控? 您是否将跟踪输出包含在健康监视器事件中? Trace.axd怎么样?
这些东西可以根据需要打开/关闭。 我发现Trace.axd对于调试服务器如何响应某些事情非常有用,但它在常用环境或长期跟踪中通常不会有用。
问:定制性能计数器如何?
对于专业应用程序,特别是服务器/服务,我期望看到它完全装备了性能监视器计数器并登录到Windows事件日志。 这些是Windows中的标准工具,应该使用。
您需要确保包含您使用的性能计数器和事件日志的安装程序; 这些应该在安装时创建(当以管理员身份安装时)。 当你的应用程序正常运行时,它不应该需要管理权限(所以不能创建缺少的日志)。
这是以非管理员身份进行开发(在需要安装服务时需要单独管理员帐户等)的一个很好的理由。 如果写入事件日志,.NET将在您第一次写入时自动创建缺失的日志; 如果您以非管理员身份进行开发,您会尽早发现并避免在客户安装系统时发生令人不愉快的惊喜,并且因为它们未以管理员身份运行而无法使用它。
我必须加入合唱团推荐的log4net,在我的情况下来自平台灵活性(桌面.Net / Compact Framework,32/64-bit)的观点。
但是,将其包装在私有标签API中是一种主要的反模式 。 log4net.ILogger
是Commons Logging wrapper API的.Net对应物,所以耦合已经最小化了,因为它也是一个Apache库,通常这不是一个问题,因为你不会放弃任何控制:fork它如果你必须。
我见过的大多数封装类库也提交了一个或多个错误:
Exception
参数 ,导致多个问题: ILayout
装饰器,对异常执行详细的深入分析以确定事件链。 IsLevelEnabled
属性 ,该属性在关闭记录的区域或级别时会丢弃跳过格式化代码的功能。 我不经常在asp.net中开发,但是对于伐木工人来说,我认为很多最佳实践都是通用的。 以下是我多年来对日志记录的一些随意想法:
构架
记录器输出
</xxx>
标签的情况下关闭,则日志会被破坏。 -- Invoking Class: com.foocorp.foopackage.FooClass:9021 SELECT * FROM foo;
This is my logging statement - Repeated 100 times
也看到我的这个问题。
链接地址: http://www.djcxy.com/p/52389.html