【发布时间】:2010-09-21 04:52:08
【问题描述】:
我最近在我的项目代码库中发现了一条日志语句,上面写着 “我在这里有搜索参数==>===========11/30/2008===1====00:00 AM”
对于在应用程序中编写良好的日志消息,您遵循哪些准则?
【问题讨论】:
标签: logging
我最近在我的项目代码库中发现了一条日志语句,上面写着 “我在这里有搜索参数==>===========11/30/2008===1====00:00 AM”
对于在应用程序中编写良好的日志消息,您遵循哪些准则?
【问题讨论】:
标签: logging
那个“日志”语句看起来更像是一个跟踪语句。
日志记录:显示正常事件和错误
跟踪:显示执行路径以及所有日志记录事件
就我个人而言,我希望通过日志来了解我的程序已经完成了哪些工作,并且我希望跟踪语句来验证执行路径并找出问题所在。我会将跟踪语句路由到一个单独的文件,该文件包含两种类型的上下文日志消息。
因此,您至少应该有 2 个日志级别,并且能够关闭跟踪以提高性能。您应该能够将这些事件流路由到不同的位置。这使您可以轻松地保留历史记录的日志,同时不会让它们与您只想用于跟踪问题的调试信息混在一起。
这里的很多答案都集中在您将包含在日志消息中的字段上,但我认为日志调用的位置和日志级别更为重要。如果您使用 log4net,您将能够通过配置文件随意包含/排除日期戳,但您将无法在不重新编译的情况下放置或删除日志语句,因此仔细考虑它们的去向是有意义的。除了时间戳和线程 ID 等标准字段之外,您还想知道调用的类和方法名称。如果您按照他们关注的类型命名记录器的最佳实践,Log4net 等人已经处理了类名。除此之外,我通常包括方法名称。这对于跟踪尤其必要,但我将它包含在我的所有日志消息中。
日志记录:
您想知道有关将要执行的操作的足够信息,以便在出现问题时返回并四处寻找。好的候选者是消息 ID、电子邮件地址,以及唯一标识工作项的东西。此类数据应在此类数据可用时立即出现,因此当您阅读日志文件时,您会看到类似“尝试用 y 执行 x”之类的信息,然后如果我们看到异常,我们知道需要查看哪个工作项才能了解失败的原因。
记录异常应与“尝试”日志消息配对,以便在读取日志时异常消息在上下文中有意义。这意味着要考虑异常处理的结构。如果您正在使用 .net 并且只想记录异常,而不是处理它,您想重新抛出 same 异常,而不是新异常,所以只需“throw”而不是“throw e”其中“e”是您的异常实例。如果没有意义,请查看这个。
追踪:
这其实比较简单,通常我会在感兴趣的方法的开头和结尾都有一条消息,比如书挡。在条目中,您可以打印影响程序流程的关键方法参数。在方法末尾记录一条消息是一种可选的方式,通常您会对查看类似于堆栈跟踪的跟踪感兴趣。你可以找出没有它们的执行路径。
性能:
对于字符串性能,如果您使用 log4net 或类似的东西,请使用 '*Format' 方法。这在内部将使用 StringBuilder,这样您就不会一直支付不可变的字符串惩罚。通常,尽管关键是能够关闭跟踪以提高性能,并让日志记录足够简洁以保持打开状态,即使日志消息很昂贵。如果正确完成,它们应该不会成为问题。
【讨论】:
日志语句中最有用的部分(当然日期和时间除外)是唯一的 ID。我们所有的日志条目都以 MSG-xxxxx 开头,其中 xxxxx 是一个唯一的整数。这个 MSG-xxxxx 标签总是硬编码在源代码中,从不在资源文件中,因此很容易找到它。它也是文档中一个非常简单的搜索关键字。
另外有用的是始终将变量字符串内容用双引号括起来,以轻松区分消息本身:
MSG-12345 试图打开“myfile.txt”,返回错误代码“123 - 找不到文件”。
当您尝试使用一些正则表达式从日志中提取信息时,这也很有帮助。
【讨论】:
不要在日志语句中进行字符串连接,至少在调试语句中。
我不久前正在开发的一个 Java 应用程序的性能很差。所以我们破解了分析器来查看瓶颈在哪里。事实证明,这主要是由于组装调试级别日志记录语句引起的字符串连接操作,这些语句总是在嵌套的内部循环等中发生(哈,并认为首先添加它们是为了弄清楚为什么性能如此糟糕!)
不要这样做
LOGGER.debug("The variable was " + myVariable + " and we are doing " + foo);
改为这样做
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("put your debug statement here " + foo + " and " + bar);
}
【讨论】:
对我来说有5个方面很重要(从不太重要的开始):
【讨论】:
在我的日志中,我需要日期和时间、创建日志的进程以及 PID。没有什么比查看日志并想知道它们是否在五分钟前进入,或者它们是否在您更改之前的 5 年前被抛在后面更糟糕的了。日期和时间很重要。
当我报告错误时,我会简明扼要地说明调用了什么、发生了什么以及返回了什么错误代码。如果是errno,我也会报告strerror(errno)。我正在阅读这篇文章来寻找问题,通常我会急于找到问题。我不想查资料。我想让它告诉我发生了什么,我更喜欢冗长而不是无用。如果我正在调试,即使我没有调试,我也会记录所有数据,例如 SQL 语句或低级记录、键,以及任何能帮助我尽快找到问题的数据。
有时我们会疯狂地登录,它会稍微减慢系统速度。但是,当事情发生了,我们收到了传票,就松散的诉讼提起了诉讼,婴儿杀手的指控被抛出,我们喜欢我们有足够的日志可以准确地显示发生了什么以及为什么以及是谁造成的。这是责任管理。
【讨论】:
代码可变级别的日志记录,因此您可以在无内容、程序流程的基本信息和内部工作的极其详细的信息之间切换。然后通过调整控制日志级别的变量,您可以控制要接收多少输出。
在您希望能够从遇到问题的客户那里接收日志记录的大型项目中,如果您可以控制应用程序的哪些部分将记录日志,这也会有所帮助;例如,如果读取 AD 用户信息时出现问题,您可以添加“AD_LOGGING=EVERYTHING”并接收详细的日志记录信息,而无需查看程序所有其他部分的日志信息。
【讨论】:
我们使用“二维”日志记录,即按单个模块和按级别进行日志记录。在尝试调试客户问题时,这使我们能够真正控制。
每个日志条目都有一个唯一的 id 加上日期加上时间加上模块加上调试级别。 id 反映(如果需要)在向用户显示的异常/错误消息中。因此,如果用户记录了某个事件,我们可以快速调出该部分的日志并查看该点附近发生了什么。
调试级别消息不使用“无效输入”之类的通用性,我们记录导致问题的实际值。如果该字段与其他字段有关系,我们也会记录它们。
【讨论】: