更新:有关System.Diagnostics的扩展,提供了您可能需要的一些缺少的侦听器,请参阅CodePlex上的Essential.Diagnostics(http://essentialdiagnostics.codeplex.com/)
构架
问:您使用什么框架?
答:System.Diagnostics.TraceSource,内置于.NET 2.0。
它为应用程序提供了强大,灵活,高性能的日志记录,但是许多开发人员并未意识到其功能,也没有充分利用它们。
在某些领域中,附加功能很有用,或者有时存在该功能,但没有充分记录,但这并不意味着应丢弃整个日志记录框架(设计为可扩展的),并像一些流行的替代品一样完全替换掉(NLog,log4net,Common.Logging甚至EntLib日志记录)。
无需更改在应用程序中添加日志记录语句和重新发明轮子的方式,只需在需要的几个地方扩展System.Diagnostics框架即可。
在我看来,其他框架,甚至是EntLib,都只是遭受“这里没有发明综合症”之苦,而且我认为他们浪费了时间来重新发明在System.Diagnostics中已经很好运行的基础知识(例如,如何编写日志语句),而不是填补现有的一些空白。简而言之,不要使用它们-不需要它们。
您可能不知道的功能:
- 使用带格式字符串和args的TraceEvent重载可以提高性能,因为将参数作为单独的引用保留,直到Filter.ShouldTrace()成功。这意味着在系统确认消息实际记录之前,不会对参数值进行昂贵的ToString()调用。
- Trace.CorrelationManager允许您将有关同一逻辑操作的日志语句关联起来(请参见下文)。
- VisualBasic.Logging.FileLogTraceListener非常适合写入日志文件,并支持文件旋转。尽管在VisualBasic名称空间中,但只需包含DLL,就可以在C#(或其他语言)项目中轻松使用它。
- 使用EventLogTraceListener时,如果使用多个参数并使用空或null格式的字符串调用TraceEvent,则如果使用的是本地化消息资源,则args将直接传递给EventLog.WriteEntry()。
- Service Trace Viewer工具(来自WCF)对于查看与活动相关的日志文件的图形很有用(即使您不使用WCF)。这确实可以帮助调试涉及多个线程/活动的复杂问题。
- 通过清除所有侦听器(或删除默认值)来避免开销;否则,Default会将所有内容传递给跟踪系统(并产生所有这些ToString()开销)。
您可能需要考虑扩展的区域(如果需要):
- 数据库跟踪侦听器
- 彩色控制台跟踪侦听器
- MSMQ /电子邮件/ WMI跟踪侦听器(如果需要)
- 实现FileSystemWatcher以调用Trace.Refresh进行动态配置更改
其他建议:
使用结构化事件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以便关联。
然后可以将Start / Stop和LogicalOperationStack用于简单的基于堆栈的上下文。对于更复杂的上下文(例如异步操作),将TraceTransfer应用于新的ActivityId(在更改之前)允许关联。
Service Trace Viewer工具对于查看活动图很有用(即使您没有使用WCF)。
问:您是手动编写此代码,还是使用某种形式的面向方面的编程来做到这一点?想要共享一个代码段吗?
答:您可能想要创建一个范围类,例如LogicalOperationScope,该范围类(a)在创建时设置上下文,并且(b)在处置时重置上下文。
这使您可以编写如下代码来自动包装操作:
using( LogicalOperationScope operation = new LogicalOperationScope("Operation") )
{
// .. do work here
}
创建时,作用域可以首先根据需要设置ActivityId,调用StartLogicalOperation,然后记录TraceEventType.Start消息。在Dispose上,它可能会记录一个Stop消息,然后调用StopLogicalOperation。
问:您是否提供跟踪来源的任何形式的粒度?例如,WPF TraceSources允许您在各个级别进行配置。
答:是的,随着系统的扩大,多个跟踪源非常有用/重要。
尽管您可能希望始终记录所有警告和以上消息,或所有信息和以上消息,但是对于任何大小合理的系统,活动跟踪(开始,停止等)和详细记录的数量都变得太多了。
而不是仅用一个开关将其全部打开或关闭,而是能够一次为系统的一个部分打开此信息很有用。
这样,您可以从通常的日志记录中查找重大问题(所有警告,错误等),然后在所需部分上“放大”并将其设置为“活动跟踪”或“调试”级别。
所需的跟踪源数量取决于您的应用程序,例如,您可能希望每个程序集或应用程序的每个主要部分使用一个跟踪源。
如果您需要更精细的控制,请添加单个布尔开关以打开/关闭特定的高容量跟踪,例如原始消息转储。(或者可以使用单独的跟踪源,类似于WCF / WPF)。
您可能还需要为活动跟踪与常规(其他)日志记录考虑单独的跟踪源,因为它可以使按所需的方式精确配置过滤器变得容易一些。
请注意,即使使用了不同的来源,消息仍然可以通过ActivityId进行关联,因此可以根据需要使用多个消息。
听众
问:您使用什么日志输出?
这可能取决于您正在编写的应用程序类型以及正在记录的内容。通常,不同的东西会放在不同的位置(即多个输出)。
我通常将输出分为三类:
(1)事件-Windows事件日志(和跟踪文件)
例如,如果编写服务器/服务,则Windows上的最佳实践是使用Windows事件日志(您没有要向其报告的UI)。
在这种情况下,所有致命,错误,警告和(服务级别)信息事件都应转到Windows事件日志中。对于这些类型的高级事件,应该保留信息级别,这些事件是您要在事件日志中进行的事件,例如“服务已启动”,“服务已停止”,“已连接到Xyz”,甚至可能是“计划已启动” ,“用户登录”等。
在某些情况下,您可能希望将事件日志写入应用程序的内置部分,而不是通过跟踪系统(即,直接写入事件日志条目)。这意味着它不会被意外关闭。(请注意,您仍然还希望在跟踪系统中记录同一事件,以便可以进行关联)。
相反,Windows GUI应用程序通常会将这些报告给用户(尽管他们也可能会记录到Windows事件日志中)。
事件还可能具有相关的性能计数器(例如,错误数/秒),协调任何直接写入事件日志,性能计数器,写入跟踪系统并报告给用户的报告很重要,以便它们发生在同一时间。
即,如果用户在特定时间看到错误消息,则应该能够在Windows事件日志中找到相同的错误消息,然后在跟踪日志中找到具有相同时间戳的同一事件(以及其他跟踪详细信息)。
(2)活动-应用程序日志文件或数据库表(和跟踪文件)
这是系统执行的常规活动,例如提供网页,提交股票交易,收取订单,执行计算等。
活动跟踪(开始,停止等)在此处(正确的粒度)很有用。
同样,使用特定的应用程序日志(有时也称为审核日志)非常普遍。通常,这是数据库表或应用程序日志文件,包含结构化数据(即一组字段)。
根据您的应用程序,这里的情况可能会变得有些模糊。一个很好的例子是一个Web服务器,它将每个请求写入Web日志。类似的示例可能是消息传递系统或计算系统,其中记录了每个操作以及特定于应用程序的详细信息。
一个不太好的例子是股票交易或销售订购系统。在这些系统中,您可能已经在记录活动,因为它们具有重要的业务价值,但是将它们与其他操作相关联的原理仍然很重要。
除自定义应用程序日志外,活动通常还具有相关的性能计数器,例如每秒的事务数。
通常,您应该协调不同系统之间的活动日志记录,即,在增加性能计数器并记录到跟踪系统的同时,写入应用程序日志。如果您同时执行所有操作(或在代码中彼此直接执行),则调试问题会更容易(比它们都在代码中的不同时间/位置发生的情况要容易得多)。
(3)调试跟踪-文本文件,或者XML或数据库。
这是详细级别和更低级别的信息(例如,用于打开/关闭原始数据转储的自定义布尔开关)。这提供了系统在子活动级别上所做的事情的细节。
这是您希望能够为应用程序的各个部分打开/关闭的级别(因此有多个来源)。您不希望这些东西弄乱Windows事件日志。有时会使用数据库,但更有可能是在一定时间后清除的滚动日志文件。
此信息与应用程序日志文件之间的最大区别在于它是非结构化的。尽管“应用程序日志”可能包含“至”,“发件人”,“金额”等字段,但是详细的调试跟踪可能是程序员输入的内容,例如“检查值X = {value},Y = false”或诸如“完成,然后重试”。
一种重要的做法是确保您放入应用程序日志文件中的内容或Windows事件日志也以相同的详细信息(例如时间戳)记录到跟踪系统中。这样,您便可以在调查时关联不同的日志。
如果由于复杂的关联而打算使用特定的日志查看器,例如Service Trace Viewer,则需要使用适当的格式,即XML。否则,一个简单的文本文件通常就足够了-在较低的级别上,信息基本上是非结构化的,因此您可能会发现数组的转储,堆栈的转储等。如果可以在更高级别上与更结构化的日志相关联,则应该好好地。
问:如果使用文件,是使用滚动日志还是仅使用一个文件?您如何使日志可供人们使用?
答:对于文件,通常您希望从可管理性的角度滚动日志文件(使用System.Diagnostics只需使用VisualBasic.Logging.FileLogTraceListener)。
可用性再次取决于系统。如果仅是在谈论文件,那么对于服务器/服务来说,只是在必要时才可以访问滚动文件。(Windows事件日志或数据库应用程序日志将具有其自己的访问机制)。
如果您无法轻松访问文件系统,则对数据库的调试跟踪可能会更容易。[即实现数据库TraceListener]。
我为Windows GUI应用程序看到的一个有趣的解决方案是,它在运行时将非常详细的跟踪信息记录到“飞行记录器”中,然后如果没有问题就将其关闭,则它只是删除了文件。
但是,如果它崩溃或遇到问题,则不会删除该文件。如果它捕获到错误,或者下次运行它将注意到该文件,然后它可以采取措施,例如将其压缩(例如7zip)并通过电子邮件发送,或者以其他方式提供。
如今,许多系统将故障自动报告合并到中央服务器(例如,出于隐私原因与用户核对之后)。
观看中
问:您使用哪些工具查看日志?
答:如果出于不同原因有多个日志,则将使用多个查看器。
Notepad / vi / Notepad ++或任何其他文本编辑器是纯文本日志的基础。
如果您有复杂的操作,例如带有转移的活动,那么您显然会使用诸如Service Trace Viewer之类的专用工具。(但是,如果您不需要它,则文本编辑器会更容易)。
由于我通常将高级信息记录到Windows事件日志中,因此它提供了一种以结构化方式获得概述的快速方法(查找漂亮的错误/警告图标)。尽管日志至少为您提供了一个起点,但您仅需要在日志中没有足够的文本时就开始搜索文本文件。(此时,确保您的日志具有协调的整体非常有用)。
通常,Windows事件日志还使这些重要事件可用于MOM或OpenView等监视工具。
其他 -
如果您登录到数据库,则可以轻松地对信息进行过滤和排序(例如,放大特定的活动ID。(对于文本文件,您可以使用Grep / PowerShell或类似工具在所需的特定GUID上进行过滤)
MS Excel(或其他电子表格程序)。如果可以使用正确的定界符导入信息,以便不同的值进入不同的列,这对于分析结构化或半结构化信息可能很有用。
在调试/测试中运行服务时,为简单起见,我通常将其托管在控制台应用程序中,我发现彩色控制台记录器很有用(例如,红色表示错误,黄色表示警告,等等)。您需要实现自定义跟踪侦听器。
请注意,该框架不包括彩色控制台记录器或数据库记录器,因此,现在,如果需要它们,则需要编写它们(这不太难)。
确实让我感到烦恼的是,几个框架(log4net,EntLib等)浪费了时间重新发明轮子,并重新实现了基本的日志记录,过滤和日志记录到文本文件,Windows事件日志和XML文件,它们各自使用不同的方式(日志语句各不相同);然后,每个人都实现了自己的版本,例如数据库记录器,而现在大多数已经存在,而所需的只是为System.Diagnostics提供几个跟踪侦听器。谈论大量重复工作。
问:如果您要构建ASP.NET解决方案,您是否还使用ASP.NET运行状况监视?您是否在运行状况监视器事件中包括跟踪输出?那么Trace.axd呢?
这些东西可以根据需要打开/关闭。我发现Trace.axd对于调试服务器如何响应某些事件非常有用,但是在频繁使用的环境或长期跟踪中通常没有用。
问:关于自定义性能计数器呢?
对于专业的应用程序,尤其是服务器/服务,我希望看到它同时具有性能监视器计数器和记录到Windows事件日志的功能。这些是Windows中的标准工具,应使用。
您需要确保包括用于所使用的性能计数器和事件日志的安装程序。这些应该在安装时(以管理员身份安装时)创建。当您的应用程序正常运行时,它不需要管理权限(因此将无法创建丢失的日志)。
这是练习以非管理员身份进行开发的好理由(有一个单独的admin帐户用于您需要安装服务的时间,等等)。如果写入事件日志,.NET将在您第一次写入事件日志时自动创建一个丢失的日志。如果您是以非管理员身份开发的,则您将尽早抓住这一点,并避免在客户安装系统后由于客户未以管理员身份运行而无法使用该系统时感到讨厌。