我想了解人们如何处理跟踪和登录实际应用程序的故事.以下是一些可能有助于解释您的答案的问题.
构架
你使用什么框架?
log4net的
System.Diagnostics.Trace
System.Diagnostics.TraceSource
记录应用程序块
其他?
如果使用跟踪,是否使用Trace.Correlation.StartLogicalOperation?
您是手动编写此代码,还是使用某种形式的面向方面编程来执行此操作?小心共享代码片段?
您是否在跟踪源上提供任何形式的粒度?例如,WPF TraceSources允许您在不同级别配置它们:
System.Windows - 所有WPF的设置
System.Windows.Animation - 专门为动画覆盖.
听众
你使用什么日志输出?
文本文件
XML文件
事件簿
其他?
如果使用文件,您使用滚动日志还是仅使用单个文件?如何使日志可供人们使用?
查看
您可以使用哪些工具查看日志?
记事本
尾巴
事件查看器
Systems Center Operations Manager/Microsoft Operations Manger
WCF服务跟踪查看器
其他?
如果要构建ASP.NET解决方案,是否还使用ASP.NET运行状况监视?您是否在运行状况监视器事件中包含跟踪输出?那么Trace.axd呢?
自定义性能计数器怎么样?
更新:对于System.Diagnostics的扩展,提供您可能需要的一些缺少的侦听器,请参阅CodePlex上的Essential.Diagnostics(http://essentialdiagnostics.codeplex.com/)
构架
答:System.Diagnostics.TraceSource,内置于.NET 2.0.
它为应用程序提供了强大,灵活,高性能的日志记录,但是许多开发人员并不了解其功能,也没有充分利用它们.
在某些方面,其他功能很有用,或者有时功能存在但没有很好的文档记录,但这并不意味着整个日志框架(设计为可扩展)应该被抛弃并完全替换为一些流行的替代方案(NLog,log4net,Common.Logging,甚至EntLib Logging).
您只需在您需要的几个地方扩展System.Diagnostics框架,而不是更改将日志语句添加到应用程序并重新发明轮子的方式.
在我看来,其他框架,甚至是EntLib,只是遭受了Not Invented Here Syndrome,我认为他们浪费时间重新发明已经在System.Diagnostics中完美运行的基础知识(例如你如何编写日志语句),而不是填补存在的少数差距.简而言之,不要使用它们 - 它们不是必需的.
您可能不知道的功能:
使用带有格式字符串和args的TraceEvent重载可以帮助提高性能,因为参数在Filter.ShouldTrace()成功之后保持为单独的引用.这意味着在系统确认消息实际将被记录之前,不会对参数值进行昂贵的ToString()调用.
Trace.CorrelationManager允许您关联有关相同逻辑操作的日志语句(参见下文).
VisualBasic.Logging.FileLogTraceListener适用于写入日志文件并支持文件轮换.虽然在VisualBasic命名空间中,只需包含DLL就可以轻松地在C#(或其他语言)项目中使用它.
如果使用多个参数调用TraceEvent并使用空格式或空格式字符串,则在使用EventLogTraceListener时,如果使用本地化消息资源,则会将args直接传递给EventLog.WriteEntry().
服务跟踪查看器工具(来自WCF)可用于查看活动相关日志文件的图形(即使您未使用WCF).这可以真正帮助调试涉及多个线程/活动的复杂问题.
通过清除所有侦听器(或删除默认值)来避免开销; 否则默认会将所有内容传递给跟踪系统(并导致所有这些ToString()开销).
您可能希望扩展的区域(如果需要):
数据库跟踪侦听器
彩色控制台跟踪监听器
MSMQ/Email/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.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.
答:是的,随着系统变大,多个跟踪源很有用/重要.
虽然您可能希望始终记录所有警告及以上,或所有信息及以上消息,但对于任何合理大小的系统,活动跟踪(开始,停止等)和详细日志记录的数量都会变得太多.
不是只有一个开关可以打开或关闭它,而是能够一次为系统的一个部分打开此信息.
这样,您可以从通常的日志记录中找到重大问题(所有警告,错误等),然后"放大"您想要的部分,并将它们设置为Activity Tracing甚至Debug级别.
您需要的跟踪源数量取决于您的应用程序,例如,您可能需要每个程序集或应用程序的每个主要部分都有一个跟踪源.
如果您需要更精细的调整控件,请添加单独的布尔开关以打开/关闭特定的高音量跟踪,例如原始消息转储.(或者可以使用单独的跟踪源,类似于WCF/WPF).
您可能还需要考虑活动跟踪与常规(其他)日志记录的单独跟踪源,因为它可以更轻松地按照您希望的方式配置过滤器.
请注意,即使使用了不同的源,仍然可以通过ActivityId关联消息,因此请根据需要使用多个消息.
问:您使用了哪些日志输出?
这可能取决于您正在编写的应用程序类型以及正在记录的内容.通常不同的东西会在不同的地方(即多个输出).
我通常将输出分为三组:
(1)事件 - Windows事件日志(和跟踪文件)
例如,如果编写服务器/服务,那么Windows上的最佳做法是使用Windows事件日志(您没有要报告的UI).
在这种情况下,所有致命,错误,警告和(服务级别)信息事件都应该转到Windows事件日志.信息级别应保留给这些类型的高级事件,即您希望在事件日志中进行的事件,例如"服务已启动","服务已停止","已连接到Xyz",甚至可能是"已启动计划" ,"用户登录"等
在某些情况下,您可能希望将事件日志写入应用程序的内置部分,而不是通过跟踪系统(即直接写入事件日志条目).这意味着它不会被意外关闭.(注意,您还需要在跟踪系统中记下相同的事件,以便进行关联).
相反,Windows GUI应用程序通常会向用户报告这些内容(尽管它们也可能会记录到Windows事件日志中).
事件也可能有相关的性能计数器(例如错误数/秒),协调任何直接写入事件日志,性能计数器,写入跟踪系统以及向用户报告以便它们发生在同一时间.
即如果用户在特定时间看到错误消息,您应该能够在Windows事件日志中找到相同的错误消息,然后在跟踪日志中找到具有相同时间戳的相同事件(以及其他跟踪详细信息).
(2)活动 - 应用程序日志文件或数据库表(和跟踪文件)
这是系统所做的常规活动,例如网页服务,提交的股票市场交易,订单执行,计算执行等.
活动跟踪(开始,停止等)在这里很有用(在正确的粒度上).
此外,使用特定的应用程序日志(有时称为审核日志)非常常见.通常,这是数据库表或应用程序日志文件,包含结构化数据(即一组字段).
根据您的应用,这里可能会有些模糊.一个很好的例子可能是将每个请求写入Web日志的Web服务器; 类似的示例可能是消息传递系统或计算系统,其中记录每个操作以及特定于应用程序的详细信息.
一个不太好的例子是股票市场交易或销售订购系统.在这些系统中,您可能已经记录了活动,因为它们具有重要的业务价值,但是将它们与其他操作相关联的原则仍然很重要.
除了自定义应用程序日志外,活动通常还具有相关的性能计数器,例如每秒的事务数.
通常,您应该协调跨不同系统的活动记录,即在增加性能计数器和登录跟踪系统的同时写入应用程序日志.如果您同时执行所有操作(或在代码中直接执行),则调试问题会更容易(比如果它们都发生在代码中的不同时间/位置).
(3)调试跟踪 - 文本文件,或者XML或数据库.
这是详细级别和更低级别的信息(例如,用于打开/关闭原始数据转储的自定义布尔开关).这提供了系统在子活动级别所做的内容或细节.
这是您希望能够为应用程序的各个部分打开/关闭的级别(因此是多个源).你不希望这些东西弄乱Windows事件日志.有时使用数据库,但更有可能是在一定时间后清除的滚动日志文件.
此信息与应用程序日志文件之间的最大区别在于它是非结构化的.虽然应用程序日志可能包含To,From,Amount等字段,但详细的调试跟踪可能是程序员输入的内容,例如"检查值X = {value},Y = false",或随机注释/标记,如"做完了,再试一次".
一个重要的做法是确保您放入应用程序日志文件或Windows事件日志中的内容也会以相同的详细信息(例如时间戳)记录到跟踪系统中.这允许您在调查时关联不同的日志.
如果您计划使用特定的日志查看器,因为您具有复杂的关联,例如服务跟踪查看器,那么您需要使用适当的格式,即XML.否则,一个简单的文本文件通常就足够了 - 在较低级别,信息很大程度上是非结构化的,因此您可能会发现数组转储,堆栈转储等.如果您可以在更高级别关联更多结构化日志,那么应该好好地.
问:如果使用文件,您使用滚动日志还是仅使用单个文件?如何使日志可供人们使用?
答:对于文件,通常您希望从可管理性的角度滚动日志文件(使用System.Diagnostics只需使用VisualBasic.Logging.FileLogTraceListener).
可用性再次取决于系统.如果您只是谈论文件,那么对于服务器/服务,可以在必要时访问滚动文件.(Windows事件日志或数据库应用程序日志将具有自己的访问机制).
如果您无法轻松访问文件系统,则可以更轻松地对数据库进行调试跟踪.[即实现数据库TraceListener].
我在Windows GUI应用程序中看到的一个有趣的解决方案是它在运行时将非常详细的跟踪信息记录到"飞行记录器",然后当它关闭时如果它没有问题那么它只是删除了文件.
如果它崩溃或遇到问题,则文件未被删除.如果它捕获错误,或者下次运行它将注意到文件,然后它可以采取行动,例如压缩它(例如7zip)并通过电子邮件发送或以其他方式提供.
如今,许多系统都将故障的自动报告结合到中央服务器中(在与用户核对之后,例如出于隐私原因).
问:您使用哪些工具查看日志?
答:如果您出于不同原因有多个日志,那么您将使用多个查看器.
Notepad/vi/Notepad ++或任何其他文本编辑器是纯文本日志的基础.
如果您有复杂的操作,例如带有传输的活动,那么您显然会使用像服务跟踪查看器这样的专用工具.(但如果你不需要它,那么文本编辑器就更容易了).
由于我通常将高级别信息记录到Windows事件日志中,因此它以结构化方式提供了一种快速获取概述的方法(查找漂亮的错误/警告图标).如果日志中没有足够的内容,您只需要开始搜索文本文件,尽管至少日志会为您提供一个起点.(此时,确保您的日志具有协调的entires变得有用).
通常,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中的标准工具,应该使用它们.
您需要确保包含您使用的性能计数器和事件日志的安装程序; 这些应该在安装时创建(以管理员身份安装时).当您的应用程序正常运行时,它不需要具有管理权限(因此将无法创建丢失的日志).
这是练习以非管理员身份进行开发的一个很好的理由(在需要安装服务时有一个单独的管理员帐户等).如果写入事件日志,.NET将在您第一次写入时自动创建缺少的日志; 如果您以非管理员的身份开发,您将尽早发现并避免在客户安装您的系统时出现令人讨厌的意外,然后由于他们没有以管理员身份运行而无法使用它.
我必须加入推荐log4net的合唱团,在我的情况下来自平台灵活性(桌面.Net/Compact Framework,32/64-bit)的观点.
但是,将其包装在私有标签API中是一种主要的反模式. log4net.ILogger
已经是Commons Logging包装器API的.Net版本,因此耦合已经为您最小化,并且因为它也是一个Apache库,所以通常甚至不关心,因为您没有放弃任何控制:如果你已经解决它必须.
我见过的大多数房屋包装库也会犯一个或多个故障:
使用全局单例记录器(或等效的静态入口点),它会丢失推荐的每类记录器模式的精细分辨率,而不会产生其他选择性增益.
未公开可选Exception
参数,导致多个问题:
它使异常日志记录策略更难以维护,因此不会对异常执行任何操作.
即使使用一致的策略,将异常格式化为字符串也会过早丢失数据.我编写了一个自定义ILayout
装饰器,对异常执行详细的深入分析以确定事件链.
未公开属性IsLevelEnabled
,这会在关闭区域或级别的日志记录时放弃跳过格式化代码的能力.
我不经常在asp.net中开发,但是当涉及到伐木工时,我认为很多最佳实践都是通用的.以下是我多年来所学习的关于伐木的一些随机想法:
使用记录器抽象框架 - 比如slf4j(或自己动手),以便将记录器实现与API分离.我已经看到一些记录器框架来来去去,你最好能够采用一个新的没有太多麻烦.
尝试找到支持各种输出格式的框架.
尝试找到支持插件/自定义过滤器的框架.
使用可由外部文件配置的框架,以便您的客户/消费者可以轻松调整日志输出,以便轻松地通过商业日志管理应用程序读取.
请确保不要过度使用自定义日志记录级别,否则您可能无法迁移到不同的日志记录框架.
尽量避免使用XML/RSS样式日志进行可能遇到灾难性故障的日志记录.这很重要,因为如果在没有记录器写入结束标记的情况下关闭电源开关,则会破坏日志.
记录线程.否则,跟踪程序的流程可能非常困难.
如果您必须将日志国际化,您可能希望开发人员仅使用英语(或您选择的语言)登录.
有时可以选择将日志语句插入到SQL查询中,这可以成为调试情况的救星.如:
-- Invoking Class: com.foocorp.foopackage.FooClass:9021 SELECT * FROM foo;
您需要类级别的日志记录.您通常也不希望记录器的静态实例 - 这不值得进行微优化.
对记录的异常进行标记和分类有时很有用,因为并非所有异常都是相同的.因此,如果您有一个需要在关键状态下发送通知的日志监视器,那么知道一个重要异常的子集就会有所帮助.
复制过滤器可以节省您的视力和硬盘.你真的想看到相同的日志声明重复10 ^ 10000000次吗?获得如下消息不是更好吗?
This is my logging statement - Repeated 100 times
另见我的这个问题.
我没有资格评论.Net的日志记录,因为我的面包和黄油是Java,但是我们在过去的8年里有一次迁移,你可能会发现你的问题有用的类比.
我们从一个Singleton记录器开始,该记录器由JVM中的每个线程使用,并设置整个过程的记录级别.如果我们不得不调试系统的一个非常特定的部分,这会产生巨大的日志,所以第一课就是分割你的日志记录.
我们当前的记录器版本允许多个实例,其中一个被定义为默认值.我们可以实例化具有不同日志记录级别的任意数量的子记录器,但是该体系结构最有用的方面是能够通过简单地更改日志记录属性来为各个包和类创建记录器.第二课是创建一个灵活的系统,允许在不更改代码的情况下覆盖其行为.
我们正在使用围绕Log4J的Apache commons-logging库.
希望这可以帮助!
*编辑*
在阅读下面的Jeffrey Hantin的帖子之后,我意识到我应该注意到我们的内部日志包装器实际上已经变成了什么.它现在基本上是一个工厂,并且严格用于使用正确的属性文件(由于遗留原因尚未移动到默认位置)来获取工作记录器.既然你现在可以在命令行上指定日志配置文件,我怀疑它会变得更加精简,如果你正在开始一个新的应用程序,我肯定同意他的说法,你甚至不应该费心包装记录器.
我们使用Log4Net作为日志记录提供程序,使用日志实例的单例包装器(尽管单例正在审核中,质疑它们是否是个好主意).
我们选择它的原因如下:
各种环境下的简单配置/重新配置
良好数量的预制appender
我们使用的其中一个CMS已经内置了它
周围的日志级别和配置数量很多
我应该提一下,这是从ASP.NET开发的角度来讲
我可以看到使用.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); }
在这些方法中,我们检查日志级别是否已启用,因此您没有对log4net API进行冗余调用(因此,如果未启用Debug,则会忽略调试语句),但是当我得到一些时间时我将更新它以暴露那些,以便您可以自己进行检查.这将阻止在不应进行评估时进行评估,例如:
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);
(节省一点时间)
默认情况下,我们登录两个位置:
网站的文件系统(在非服务文件扩展名中)
错误和致命的电子邮件发送
文件按每天滚动或10mb(IIRC)进行.我们不使用EventLog,因为它可能需要比我们通常想要提供站点更高的安全性.
我发现Notepad可以很好地读取日志.
你使用什么框架?
我们使用日志应用程序块和一个围绕.Net框架位的自定义日志记录助手的混合.LAB配置为输出相当广泛的日志文件,包括用于服务方法进入/退出的单独的常规跟踪文件以及用于意外问题的特定错误文件.配置包括调试帮助的日期/时间,线程,pId等,以及完整的异常详细信息和堆栈(在意外异常的情况下).
自定义日志记录助手使用Trace.Correlation,在登录WF的上下文中特别方便.例如,我们有一个调用一系列顺序工作流的状态机.在每个调用活动中,我们记录开始(使用StartLogicalOperation),然后在最后我们使用gereric返回事件处理程序停止逻辑操作.
在尝试调试复杂业务序列中的故障时,这几次证明是有用的,因为它允许我们根据活动执行顺序更快地确定If/Else分支决策等事情.
你使用什么日志输出?
我们使用文本文件和XML文件.文本文件是通过app块配置的,但我们也从WF服务获得了XML输出.这使我们能够捕获运行时事件(持久性等)以及通用业务类型异常.文本文件是按日期和大小滚动的滚动日志(我相信总大小为1MB是翻转点).
您可以使用哪些工具查看日志?
我们正在使用记事本和WCF服务跟踪查看器,具体取决于我们正在查看的输出组.如果您的输出设置正确并且可以使输出读取更简单,那么WCF服务跟踪查看器非常方便.也就是说,如果我大致知道错误的位置 - 只需读取带注释的文本文件也是好的.
日志将发送到单个目录,然后根据源服务将其拆分为子目录.根目录通过一个网站公开,该网站的访问权限由支持用户组控制.这使我们可以查看生产日志,而无需提交请求并对生产数据进行冗长的繁文缛节流程.
作为该工具的作者,我们当然使用SmartInspect来记录和跟踪.NET应用程序.我们通常使用命名管道协议进行实时日志记录,并将(加密)二进制日志文件用于最终用户日志.我们使用SmartInspect控制台作为查看器和监视工具.
实际上有很多针对.NET的日志框架和工具.有关DotNetLogging.com上不同工具的概述和比较.
答案中有很多很棒的建议.
一般的最佳做法是考虑谁将阅读日志.在我的情况下,它将是客户端站点的管理员.所以我记录的消息给了他们可以采取行动的东西.例如,"无法初始化应用程序.这通常是由......引起的"