记录信息最佳实践

时间:2010-11-07 10:28:18

标签: logging log4net

我正在做生产支持,很依赖日志进行故障排除。我发现日志信息现在非常混乱。

您能提供编写日志信息的最佳做法或指南吗?

BTW:我们正在使用log4Net。你对替代图书馆有什么建议吗?

感谢。

4 个答案:

答案 0 :(得分:7)

理想情况下,您的日志消息应包含“何时”,“内容”,“何处”,“谁”以及触发消息的事件的严重程度的详细信息。

  • 包括日期和时间。如果您的应用程序跨时区分布,则还包括时区指示符。如果每个人都确切知道03:11:04在他们的时区中的时间,那么它将消除混乱。
  • 包括日志记录严重性级别。
  • 在日志消息中包含触发日志按摩的模块或类的一些指示。
  • 如果可能的话,鼓励开发人员在邮件中包含特定信息:例如,'文件损坏',没有用,'文件损坏:'C:\ foo \ bar.dat“'
  • 如果可能,请让开发人员在错误消息中包含某种会话或事务ID。能够过滤来自有错误的事务的消息的日志并忽略所有正常的事务是很方便的。
  • 在日志消息中包含错误代码以获取错误通常是个好主意。

我的第二个建议是@Oded保持信息整洁。对于常规的东西,日期和时间时间,日志级别,错误代码,我会尝试将它们格式化为固定宽度,并将它们放在开头。它使扫描日志变得更加容易。

关于日志消息格式的良好指南,我唯一遇到的是“发布它!”一书的第17章:http://www.pragprog.com/titles/mnee/release-it上述许多建议都基于此。

答案 1 :(得分:2)

ELMAH是一个非常好的日志记录库,并不是log4net的替代品。

关于日志的格式化(你说信息是“凌乱的”,虽然你没有准确解释这意味着什么) - 确保每个条目与其他条目明确分开,并且格式化为可读方式(间距,换行等......)。

答案 2 :(得分:2)

@corriganjc有很多好建议。

我会添加一些细节: 如果可能,请考虑使用UTC记录消息。一方面,查看“你的”时区中生成的消息,然后必须记住正确的数量来抵消它们以“正确地”解释它们可能会很烦人。另一方面,所有日志消息都可按时间/日期排序,无需进一步解释。 (如果您从两个时区记录了消息并且已使用“本地”时间记录它们,则在将它们带到公共时区之前无法对它们进行排序。)

使用GlobalContext,ThreadContext和LogicalThreadContext对象在邮件中注入其他上下文。记录诸如“会话ID”或“事务ID”之类的建议是一个很好的建议,并且可以使用“上下文”对象最有效地完成,而不是通过将这些值显式添加到实际的日志记录调用站点。您可以设置一次上下文,并通过添加格式化选项,使用ever message记录该上下文。

如果您使用上下文对象,请考虑制作“标准”值名称,甚至可以定义开发人员可以引用的字符串常量,以便在尝试添加其上下文时不会产生拼写错误:

//Context value names
public static class DiagnosticContextValueNames
{
  public static string TransactionId = "transactionid";
  public static string SessionId = "sessionid";
}

//In your code
log4net.ThreadContext.Properties[DiagnosticContextValueNames.TransactionId] = GetTransactionId();
log4net.ThreadContext.Properties[DiagnosticContextValueNames.SessionId] = GetSessionId();

//Somewhere later on...
logger.Info("hello");  // this message can be tagged with the transaction id and session id if you use the appropriate formatting options

您甚至可以考虑GlobalContext.Properties,ThreadContext.Properties等上的扩展方法,以帮助指导开发人员正确设置上下文值:

public static class LoggingExtensions
{
  public static void SetTransactionId(this ThreadContextProperties props, string trans)
  {
    props["TransactionId"] = trans;

    // Or, using constants as defined above...
    props[ThreadContextValueNames.TransactionId] = trans;
  }
}


// In your code...
log4net.ThreadContext.Properties.SetTransactionId(GetTransactionId());

// As compared to this:
log4net.ThreadContext.Properties["transactionid"] = GetTransactionId();

如果您从log4net记录器中包装或继承,您可以自动添加一些上下文信息,从而减轻开发人员的负担。有一种正确的方法来包装或继承log4net记录器,但它不是火箭科学。关键是将log4net传递给包装记录器的类型。 cfeduke在this post中的回答提供了一种包装log4net记录器的方法。

您可以按照他的示例,但根据“ILogger.Log”方法实现所有日志记录调用。在“Log”中,您可以为所有日志消息添加所需的属性:

// This approach requires more effort than simply populating the context properties "normally", and
// is probably overkill for most situations.  However, it can prove useful if you are able
// to have access to the context information that you want to log from within the Log method
// of the logger.
public void Log(type loggerBoundaryDeclaringType, LogLevel level, object message, object exception)
{
  log4net.ThreadContext.Properties["transactionid"] = GetTransactionId();
  log4net.ThreadContext.Properties["sessionid"] = GetSessionId();
  _logger.Log(loggerBoundaryDeclaringType, level, message, exception);
}

就其他日志记录平台而言,您可以查看NLog。新版本最近已作为Beta版本发布。它有许多与log4net类似的功能。

您可能还会考虑使用旧的System.Diagnostics.TraceSource。如果你走这条路,请在codeplex上查看Ukadc.Diagnostics。这是System.Diagnostics的插件库,提供丰富的消息格式化功能(类似于您可以使用log4net和NLog执行的操作)。关于Ukadc.Diagnostics的一个好处是它只是一个配置依赖。您不必使用源或引用依赖项来使用它。

答案 3 :(得分:1)

> ... we're using log4Net. Do you have any suggestion on alternative library? 

您可以使用common.logging(http://netcommon.sourceforge.net),这是一个与

一起使用的微小日志包装器
  • log4net的
  • n日志
  • 企业库日志记录

由于您必须配置要使用的日志记录引擎,并且还必须配置日志记录引擎,因此它使日志记录配置更加复杂。

它的log api受到log4net-api

的启发