我想了解人们如何在实际应用程序中处理跟踪和登录。这里有一些问题可能有助于解释你的答案。

框架

你使用什么框架?

log4net System.Diagnostics.Trace System.Diagnostics.TraceSource 日志应用程序块 其他的吗?

如果你使用跟踪,你使用Trace.Correlation.StartLogicalOperation吗?

您是手动编写这些代码,还是使用某种形式的面向方面编程来完成这些代码?愿意分享一段代码片段吗?

您是否提供了任何形式的跟踪源粒度?例如,WPF TraceSources允许你在不同的级别上配置它们:

系统。Windows -所有WPF的设置 System.Windows.Animation -重写动画。

听众

您使用什么日志输出?

文本文件 XML文件 事件日志 其他的吗?

如果使用文件,您是使用滚动日志还是单个文件?如何让人们可以使用这些日志?

查看

您使用什么工具查看日志?

记事本 尾巴 事件查看器 系统中心运营经理/微软运营经理 WCF服务跟踪查看器 其他的吗?

如果您正在构建ASP。NET解决方案,你也用ASP。NET运行状况监视?运行状况监控器事件中是否包含跟踪输出?Trace.axd呢?

那么自定义性能计数器呢?


当前回答

我们在工作中使用Log4Net作为日志记录提供者,并为日志实例使用单例包装器(尽管单例正在审查中,质疑它们是否是一个好主意)。

我们选择它的原因如下:

在各种环境上进行简单的配置/重新配置 良好的预建附件数量 我们用的一个CMS已经内置了 大量的日志级别和相关配置

我应该提一下,这是从ASP讲的。NET开发的观点

我可以看到使用. net框架中的Trace的一些优点,但我并不完全相信它,主要是因为我使用的组件并没有真正执行任何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,因为它需要比我们通常想要的站点更高的安全性。

我发现记事本可以很好地读取日志。

其他回答

我们在web应用程序中使用log4net。

当应用程序在运行时出现故障,需要查看更多信息时,它可以通过更改XML配置文件在运行时自定义日志记录。

它还允许您锁定要登录的特定类或属性。当您知道错误发生在哪里时,这是非常方便的。一个经典的例子是NHibernate,你想看到的只是SQL进入数据库。

编辑:

我们将所有事件写入数据库和Trace系统。我们用于错误或异常的事件日志。我们将大多数事件记录到数据库中,这样我们就可以创建自定义报告,如果用户想从应用程序中查看日志的话。

我们在工作中使用Log4Net作为日志记录提供者,并为日志实例使用单例包装器(尽管单例正在审查中,质疑它们是否是一个好主意)。

我们选择它的原因如下:

在各种环境上进行简单的配置/重新配置 良好的预建附件数量 我们用的一个CMS已经内置了 大量的日志级别和相关配置

我应该提一下,这是从ASP讲的。NET开发的观点

我可以看到使用. net框架中的Trace的一些优点,但我并不完全相信它,主要是因为我使用的组件并没有真正执行任何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,因为它需要比我们通常想要的站点更高的安全性。

我发现记事本可以很好地读取日志。


更新:用于系统扩展。诊断,提供您可能需要的一些缺失的侦听器,请参阅基本。CodePlex诊断(http://essentialdiagnostics.codeplex.com/)


框架

问:你使用什么框架?

System.Diagnostics。TraceSource,内置于。net 2.0中。

它为应用程序提供了强大、灵活、高性能的日志记录,但是许多开发人员没有意识到它的功能,也没有充分利用它们。

在某些领域,额外的功能是有用的,或者有时功能存在但没有很好地记录,但这并不意味着整个日志框架(被设计为可扩展的)应该被抛弃,并像一些流行的替代品(NLog, log4net, Common)一样被完全取代。日志,甚至是EntLib日志)。

而不是改变向应用程序添加日志语句的方式,重新发明轮子,只是扩展了系统。诊断框架在少数地方你需要它。

在我看来,其他的框架,甚至是EntLib,都只是遭受了“不是在这里发明的”综合症,我认为他们浪费了时间来重新发明已经在系统中工作得很好的基础知识。诊断(例如如何编写日志语句),而不是填补存在的少数空白。简而言之,不要使用它们——它们不需要。

你可能不知道的特点:

Using the TraceEvent overloads that take a format string and args can help performance as parameters are kept as separate references until after Filter.ShouldTrace() has succeeded. This means no expensive calls to ToString() on parameter values until after the system has confirmed message will actually be logged. The Trace.CorrelationManager allows you to correlate log statements about the same logical operation (see below). VisualBasic.Logging.FileLogTraceListener is good for writing to log files and supports file rotation. Although in the VisualBasic namespace, it can be just as easily used in a C# (or other language) project simply by including the DLL. When using EventLogTraceListener if you call TraceEvent with multiple arguments and with empty or null format string, then the args are passed directly to the EventLog.WriteEntry() if you are using localized message resources. The Service Trace Viewer tool (from WCF) is useful for viewing graphs of activity correlated log files (even if you aren't using WCF). This can really help debug complex issues where multiple threads/activites are involved. Avoid overhead by clearing all listeners (or removing Default); otherwise Default will pass everything to the trace system (and incur all those ToString() overheads).

你可能想要扩展的领域(如果需要):

数据库跟踪侦听器 彩色控制台跟踪侦听器 MSMQ / Email / WMI跟踪监听器(如果需要) 实现一个FileSystemWatcher来调用Trace。为动态配置更改刷新

其他建议:

使用结构化的事件id,并保持一个引用列表(例如,在enum中记录它们)。

为系统中的每个(重要)事件设置唯一的事件id对于关联和查找特定问题非常有用。可以很容易地追踪到记录/使用事件id的特定代码,并且可以很容易地为常见错误提供指导,例如错误5178意味着数据库连接字符串是错误的,等等。

事件id应该遵循某种结构(类似于电子邮件和HTTP中使用的回复代码理论),这允许您在不知道具体代码的情况下按类别处理它们。

例:第一个数字可以详细说明一般类别:1xxx可以用于“开始”操作,2xxx用于正常行为,3xxx用于活动跟踪,4xxx用于警告,5xxx用于错误,8xxx用于“停止”操作,9xxx用于致命错误,等等。

第二个数字可以详细说明区域,例如21xx表示数据库信息(41xx表示数据库警告,51xx表示数据库错误),22xx表示计算模式(42xx表示计算警告等),23xx表示另一个模块,等等。

分配的结构化事件id还允许在过滤器中使用它们。

问:如果你使用跟踪,你会使用Trace.Correlation.StartLogicalOperation吗?

答:跟踪。CorrelationManager对于在任何类型的多线程环境中关联日志语句非常有用(现在几乎是任何环境)。

为了相互关联,您至少需要为每个逻辑操作设置一次ActivityId。

然后,启动/停止LogicalOperationStack可以用于简单的基于堆栈的上下文。对于更复杂的上下文(例如异步操作),使用TraceTransfer到新的ActivityId(在更改它之前),允许相关性。

Service Trace Viewer工具对于查看活动图非常有用(即使您不使用WCF)。

问:您是手动编写这些代码,还是使用某种形式的面向方面编程来实现?愿意分享一段代码片段吗?

答:你可能想要创建一个作用域类,例如LogicalOperationScope,它(A)在创建时设置上下文,(b)在释放时重置上下文。

这允许你编写如下代码来自动换行操作:

  using( LogicalOperationScope operation = new LogicalOperationScope("Operation") )
  {
    // .. do work here
  }

在创建作用域时,如果需要,可以先设置ActivityId,调用StartLogicalOperation,然后记录TraceEventType。消息开始。在Dispose上,它可以记录一个Stop消息,然后调用StopLogicalOperation。

问:你提供任何形式的跟踪源粒度吗?例如,WPF TraceSources允许您在不同级别上配置它们。

答:是的,当系统变得更大时,多个跟踪源是有用/重要的。

虽然您可能希望一致地记录所有警告及以上消息,或所有信息及以上消息,但对于任何合理大小的系统,活动跟踪(启动、停止等)和详细日志的量都太大了。

与其只有一个开关来打开或关闭所有信息,不如能够一次为系统的一个部分打开这些信息。

通过这种方式,您可以从通常的日志(所有警告、错误等)中定位重要的问题,然后“放大”您想要的部分,并将它们设置为活动跟踪甚至调试级别。

所需跟踪源的数量取决于您的应用程序,例如,您可能希望每个程序集或应用程序的每个主要部分都有一个跟踪源。

如果您需要更精细的控制,可以添加单独的布尔开关来打开/关闭特定的大容量跟踪,例如原始消息转储。(或者可以使用单独的跟踪源,类似于WCF/WPF)。

您可能还想考虑为活动跟踪与一般(其他)日志记录考虑单独的跟踪源,因为这样可以更容易地按照您想要的方式配置过滤器。

请注意,即使使用了不同的源,消息仍然可以通过ActivityId进行关联,所以您需要使用多少就使用多少。


听众

问:你使用什么日志输出?

这取决于您正在编写的应用程序类型,以及要记录的内容。通常不同的东西在不同的地方(即多个输出)。

我通常将输出分为三类:

(1)事件- Windows事件日志(和跟踪文件)

例:如果写一个服务器/服务,那么在Windows上的最佳实践是使用Windows事件日志(你没有一个UI来报告)。

在这种情况下,所有致命、错误、警告和(服务级别)信息事件都应该进入Windows事件日志。信息级别应该为这些类型的高级事件保留,那些你想要进入事件日志的事件,例如。“服务已启动”,“服务已停止”,“连接到Xyz”,甚至可能是“计划已启动”,“用户已登录”,等等。

在某些情况下,您可能希望将写入事件日志作为应用程序的内置部分,而不是通过跟踪系统(即直接写入事件日志条目)。这意味着它不会被意外关闭。(请注意,您仍然希望在跟踪系统中记录相同的事件,以便进行关联)。

相反,Windows GUI应用程序通常会向用户报告这些事件(尽管它们也可能记录到Windows事件日志中)。

事件也可能有相关的性能计数器(例如,错误数/秒),协调任何直接写入事件日志、性能计数器、写入跟踪系统和报告给用户,使它们同时发生是很重要的。

例如,如果用户在特定时间看到错误消息,你应该能够在Windows事件日志中找到相同的错误消息,然后在跟踪日志中找到具有相同时间戳的相同事件(以及其他跟踪细节)。

(2)活动——应用程序日志文件或数据库表(和跟踪文件)

这是系统所做的常规活动,例如,提供网页、提交股票市场交易、接收订单、执行计算等。

活动跟踪(启动、停止等)在这里很有用(在正确的粒度上)。

此外,使用特定的应用程序日志(有时称为审计日志)是非常常见的。通常这是一个数据库表或应用程序日志文件,包含结构化数据(即一组字段)。

根据应用程序的不同,这里的情况可能会有点模糊。一个很好的例子可能是一个web服务器,它将每个请求写入一个web日志;类似的例子可能是消息传递系统或计算系统,其中每个操作都记录了特定于应用程序的详细信息。

一个不太好的例子是股票市场交易或销售订单系统。在这些系统中,您可能已经记录了具有重要业务价值的活动,但是将它们与其他操作关联的原则仍然很重要。

除了自定义应用程序日志,活动通常也有相关的性能计数器,例如每秒事务数。

一般来说,您应该协调跨不同系统的活动日志记录,即当您增加性能计数器并记录到跟踪系统时,同时写入应用程序日志。如果您同时执行所有这些操作(或者在代码中依次执行),那么调试问题会更容易(而不是它们都出现在代码中的不同时间/位置)。

(3)调试跟踪-文本文件,或者XML或数据库。

这是Verbose级别或更低级别的信息(例如,打开/关闭原始数据转储的自定义布尔开关)。这提供了系统在子活动级别上所做事情的内容或细节。

这是您希望能够为应用程序的各个部分(因此是多个源)打开/关闭的级别。你不希望这些东西搅乱Windows事件日志。有时会使用数据库,但更有可能使用在特定时间后清除的滚动日志文件。

此信息与应用程序日志文件之间的一个很大区别是,它是非结构化的。虽然应用程序日志可能有To、From、Amount等字段,但详细调试跟踪可能是程序员输入的任何内容,例如。“检查值X={value}, Y=false”,或者像“完成,再试一次”这样的随机注释/标记。

一个重要的实践是确保你放在应用程序日志文件或Windows事件日志中的东西也被记录到跟踪系统中,具有相同的细节(例如时间戳)。这允许您在调查时关联不同的日志。

如果您计划使用特定的日志查看器,因为您有复杂的相关性,例如服务跟踪查看器,那么您需要使用适当的格式,即XML。否则,一个简单的文本文件通常就足够了——在较低的级别上,信息基本上是非结构化的,因此您可能会发现数组转储、堆栈转储等。如果您可以在更高的级别上与更结构化的日志相关联,那么事情应该不会有问题。

问:如果使用文件,您是使用滚动日志还是单个文件?如何让人们可以使用这些日志?

答:对于文件,从可管理性的角度来看,通常需要滚动日志文件(使用System. log)。诊断只需使用VisualBasic.Logging.FileLogTraceListener)。

可用性同样取决于系统。如果您只讨论文件,那么对于服务器/服务,滚动文件只能在必要时访问。(Windows事件日志或数据库应用程序日志将有自己的访问机制)。

如果不容易访问文件系统,那么调试到数据库的跟踪可能会更容易一些。[即实现一个数据库TraceListener]。

我看到的一个有趣的Windows GUI应用程序的解决方案是,它在运行时将非常详细的跟踪信息记录到“飞行记录器”中,然后当你关闭它时,如果它没有问题,它就会简单地删除文件。

然而,如果它崩溃或遇到问题,那么文件不会被删除。如果它捕捉到错误,或者下次运行时它会注意到该文件,然后它可以采取行动,例如压缩它(例如7zip)并通过电子邮件发送或以其他方式提供。

如今,许多系统都将故障自动报告到中央服务器(在与用户进行检查后,例如出于隐私原因)。


查看

问:你使用什么工具来查看日志?

答:如果您由于不同的原因有多个日志,那么您将使用多个查看器。

Notepad/vi/ notepad++或任何其他文本编辑器是纯文本日志的基础。

如果您有复杂的操作,例如带有传输的活动,那么您显然会使用专门的工具,如服务跟踪查看器。(但如果你不需要它,那么文本编辑器更容易)。

由于我通常将高级信息记录到Windows事件日志中,因此它提供了一种以结构化的方式快速获得概述的方法(寻找漂亮的错误/警告图标)。只有当日志中没有足够的文本文件时,您才需要开始查找文本文件,尽管至少日志为您提供了一个起点。(在这一点上,确保您的日志具有协调的整体变得很有用)。

通常,Windows事件日志也使这些重要事件可用于监视工具,如MOM或OpenView。

其他——

如果你登录到一个数据库,它可以很容易地过滤和排序信息(例如,放大一个特定的活动id。(对于文本文件,您可以使用Grep/PowerShell或类似的过滤器对您想要的特定GUID)

Excel(或其他电子表格程序)。这对于分析结构化或半结构化信息非常有用,如果您可以使用正确的分隔符导入信息,以便不同的值放在不同的列中。

当在调试/测试中运行服务时,为了简单起见,我通常将它托管在控制台应用程序中,我发现彩色的控制台记录器很有用(例如,红色表示错误,黄色表示警告,等等)。您需要实现一个自定义跟踪侦听器。

请注意,该框架不包括彩色控制台记录器或数据库记录器,因此,如果您需要它们,您将需要编写它们(这并不太难)。

It really annoys me that several frameworks (log4net, EntLib, etc) have wasted time re-inventing the wheel and re-implemented basic logging, filtering, and logging to text files, the Windows Event Log, and XML files, each in their own different way (log statements are different in each); each has then implemented their own version of, for example, a database logger, when most of that already existed and all that was needed was a couple more trace listeners for System.Diagnostics. Talk about a big waste of duplicate effort.

问:如果你正在构建一个ASP。NET解决方案,你也用ASP。NET运行状况监视?运行状况监控器事件中是否包含跟踪输出?Trace.axd呢?

这些东西可以根据需要打开/关闭。我找到了特蕾西。Axd在调试服务器如何响应某些事情时非常有用,但在大量使用的环境或长期跟踪中通常不太有用。

问:自定义性能计数器如何?

对于专业应用程序,特别是服务器/服务,我希望看到它完全配备了Performance Monitor计数器和日志到Windows事件日志。这些都是Windows中应该使用的标准工具。

您需要确保为您所使用的性能计数器和事件日志包含安装程序;这些应该在安装时创建(以管理员身份安装时)。当您的应用程序正常运行时,它不需要管理权限(因此不能创建丢失的日志)。

这是一个以非管理员身份进行开发的好理由(当您需要安装服务时,请使用单独的管理帐户等)。如果写入事件日志,.NET会在你第一次写入时自动创建一个丢失的日志;如果你以非管理员身份开发,你就能尽早发现这个问题,避免客户安装了你的系统后因为不是管理员身份而无法使用它。

我不经常在asp.net中开发,但是当涉及到记录器时,我认为许多最佳实践是通用的。以下是我多年来对日志的一些随机想法:

框架

Use a logger abstraction framework - like slf4j (or roll your own), so that you decouple the logger implementation from your API. I have seen a number of logger frameworks come and go and you are better off being able to adopt a new one without much hassle. Try to find a framework that supports a variety of output formats. Try to find a framework that supports plugins / custom filters. Use a framework that can be configured by external files, so that your customers / consumers can tweak the log output easily so that it can be read by commerical log management applications with ease. Be sure not to go overboard on custom logging levels, otherwise you may not be able to move to different logging frameworks.

日志输出

Try to avoid XML/RSS style logs for logging that could encounter catastrophic failures. This is important because if the power switch is shut off without your logger writing the closing </xxx> tag, your log is broken. Log threads. Otherwise, it can be very difficult to track the flow of your program. If you have to internationalize your logs, you may want to have a developer only log in English (or your language of choice). Sometimes having the option to insert logging statements into SQL queries can be a lifesaver in debugging situations. Such as:

    -- Invoking Class: com.foocorp.foopackage.FooClass:9021
    SELECT * FROM foo;

You want class-level logging. You normally don't want static instances of loggers as well - it is not worth the micro-optimization. Marking and categorizing logged exceptions is sometimes useful because not all exceptions are created equal. So knowing a subset of important exceptions a head of time is helpful, if you have a log monitor that needs to send notifications upon critical states. Duplication filters will save your eyesight and hard disk. Do you really want to see the same logging statement repeated 10^10000000 times? Wouldn't it be better just to get a message like: This is my logging statement - Repeated 100 times

也看看我的这个问题。

我没有资格评论。net的日志,因为我的面包和butter是Java,但是在过去的8年里,我们在日志方面有了一个迁移,你可能会发现一个有用的类比来回答你的问题。

我们从JVM中的每个线程都使用的Singleton记录器开始,并为整个进程设置日志记录级别。如果我们不得不调试系统的一个非常特定的部分,就会产生大量的日志,所以第一个教训是对日志进行分段。

当前的日志记录器允许多个实例,其中一个被定义为默认实例。我们可以实例化任意数量的具有不同日志级别的子日志记录器,但是这种体系结构最有用的方面是能够通过简单地更改日志属性为单个包和类创建日志记录器。第二课是创建一个灵活的系统,允许在不更改代码的情况下重写其行为。

我们使用的是包裹在Log4J周围的Apache公共日志库。

希望这能有所帮助!

*编辑*

After reading Jeffrey Hantin's post below, I realized that I should have noted what our internal logging wrapper has actually become. It's now essentially a factory and is strictly used to get a working logger using the correct properties file (which for legacy reasons hasn't been moved to the default position). Since you can specify the logging configuration file on command line now, I suspect it will become even leaner and if you're starting a new application, I'd definitely agree with his statement that you shouldn't even bother wrapping the logger.