你的伐木理念是什么?

时间:2008-09-04 19:24:55

标签: logging

作为Jeff Atwood asked:“您的日志记录理念是什么?所有代码都应该被.logthis().logthat()调用?或者您是否以某种方式注入了日志?”

12 个答案:

答案 0 :(得分:21)

我的记录理念很容易归纳为四个部分:

审核或业务逻辑日志记录

  

记录需要记录的内容。这来自应用程序要求,可能包括记录对任何数据库(如许多财务应用程序)所做的每个更改或记录对数据的访问(健康行业可能需要满足行业法规)

     

由于这是程序要求的一部分,许多人不会将其包含在日志记录的一般讨论中,但是这些领域存在重叠,对于某些应用程序,将所有日志记录活动一起考虑是有用的。

程序记录

  

有助于开发人员测试和调试应用程序的消息,更容易遵循数据流和程序逻辑,以了解可能存在的实现,集成和其他错误。

     

通常,根据调试会话的需要打开和关闭此日志记录。

效果记录

  

根据需要添加以后的日志记录,以查找和解决性能瓶颈和其他程序问题,这些问题不会导致程序失败,但会带来更好的操作。在内存泄漏和一些非严重错误的情况下,使用程序记录重叠。

安全日志记录

  

记录用户操作以及与安全性有关的外部系统的交互。用于确定攻击者在攻击后如何破坏系统,但也可能与入侵检测系统绑定以检测新的或持续的攻击。<​​/ p>

答案 1 :(得分:15)

我使用安全关键的实时系统,并且记录通常是捕获罕见错误的唯一方法,只有在满月的时候,如果你抓住我的漂移,每周五每周五出现一次。这种让你对这个主题的迷恋,所以如果我开始在嘴里泡沫,我现在就道歉。

我设计的系统几乎可以记录所有内容,但我没有默认打开所有内容。调试信息被发送到隐藏的调试对话框,该对话框将其加时间戳并将其输出到列表框(删除前限制为大约500行),对话框允许我将其停止,自动将其保存到日志文件,或将其转移到附加的调试器,如DBWin32。这种转移使我能够看到来自多个应用程序的调试输出都整齐地序列化,有时可以节省生命。每N天自动清除日志文件。我使用来使用数字记录级别(设置级别越高,捕获的越多):

  • 仅错误
  • 基本
  • 详述
  • 一切

但是这太不灵活了 - 当你朝着一个错误的方向努力时,能够将注意力集中在你所需要的东西上而不必趟过大量的碎屑就更有效了,它可能是一种特殊的类型导致错误的事务或操作。如果这需要你打开一切,你只是让自己的工作变得更难。你需要更精细的东西。

所以现在我正在基于标志系统切换到日志记录。记录的所有内容都有一个标志,详细说明它是什么类型的操作,并且有一组复选框允许我定义记录的内容。通常,该列表如下所示:

#define DEBUG_ERROR          1
#define DEBUG_BASIC          2
#define DEBUG_DETAIL         4
#define DEBUG_MSG_BASIC      8
#define DEBUG_MSG_POLL       16
#define DEBUG_MSG_STATUS     32
#define DEBUG_METRICS        64
#define DEBUG_EXCEPTION      128
#define DEBUG_STATE_CHANGE   256
#define DEBUG_DB_READ        512
#define DEBUG_DB_WRITE       1024
#define DEBUG_SQL_TEXT       2048
#define DEBUG_MSG_CONTENTS   4096

此日志记录系统附带发布版本,默认情况下已打开并保存到文件。现在发现你应该在发生错误之后进行记录已经太晚了,如果这个错误平均每六个月发生一次并且你无法复制它。

该软件通常附带ERROR,BASIC,STATE_CHANGE和EXCEPTION,但是可以通过调试对话框(或注册表/ ini / cfg设置,这些内容得到保存)在现场更改。

哦,还有一件事 - 我的调试系统每天生成一个文件。您的要求可能有所不同。但请确保您的调试代码启动每个文件,其中包含您正在运行的代码的日期,版本,以及可能的客户ID,系统位置或其他任何标记。你可以从现场获得一些混合的日志文件,你需要记录一下来自哪里以及它们运行的​​系统版本实际上是在数据本身中的什么,你不能相信客户/现场工程师告诉你他们有什么版本 - 他们可能只是告诉你他们认为他们有什么版本。更糟糕的是,他们可能会报告磁盘上的exe版本,但旧版本仍在运行,因为他们在更换后忘记重新启动。让你的代码告诉你自己。

那是我的大脑倾倒......

答案 2 :(得分:4)

我认为始终总是在出现异常时添加日志记录,包括消息和完整堆栈跟踪。除此之外,我认为你是否经常使用日志是非常主观的......

我经常尝试只在关键位置添加日志记录,我记录的内容很少会被点击,否则就会出现问题,就像他提到的日志变得太大了......这就是为什么日志记录错误案例是理想的总是记录的事情(能够看到这些错误案例实际被击中的时候很棒,这样你就可以进一步检查问题了。)

要记录的其他好处是如果你有断言,并且你的断言失败,那么记录它......比如,这个查询应该在10个结果之下,如果它更大则可能有问题,所以记录它。当然,如果日志语句最终填满日志,则可能是将其置于某种“调试”级别,或调整或删除日志语句的提示。如果日志变得太大,您通常会忽略它们。

答案 3 :(得分:1)

我采用我认为的传统方法;一些日志记录,由条件定义包围。对于生产版本,我关闭了定义。

答案 4 :(得分:1)

我选择故意记录,因为这意味着日志数据是有意义的:

  • 根据日志记录框架,您可以添加级别/严重性/类别信息,以便可以过滤日志数据
  • 您可以确保存在正确的信息级别,而不是太多,而不是太少
  • 您知道何时编写最重要的代码,因此可以确保记录

使用某种形式的代码注入,分析或跟踪工具来生成日志很可能会生成冗长,不太有用的日志,这些日志将更难以深入研究。但是,它们可能有助于作为调试辅助工具。

答案 5 :(得分:1)

我首先在我的代码中声明了很多条件(在C#中使用System.Diagnostics.Assert),但我只在我找到的地方添加日志记录,同时调试或使系统处于压力之下,我真的需要一种方法,可以在没有永久附加调试器的情况下跟踪代码中发生的事情。

否则,我更喜欢使用Visual Studio的功能将代码中的跟踪作为特殊断点(即插入断点并右键单击它,然后选择“当命中时...”并告诉它在该情况下显示什么)。无需重新编译,可以轻松启用/禁用跟踪。

答案 6 :(得分:1)

如果您正在编写一个将被许多人使用的程序,那么最好使用某种机制来选择要记录的内容和不记录的内容。支持.logthis()函数的一个论点是,在某些情况下(如果正确完成),它们可以很好地替代内联注释。

另外,它可以帮助您完全缩小发生错误的位置。

答案 7 :(得分:1)

记录所有并让Grep排序。

答案 8 :(得分:0)

我同意亚当,但我也会考虑记录感兴趣的东西或你可以证明成就的事物,作为它们发生的证据。

答案 9 :(得分:0)

我定义了各种级别,并使用config / invocation传递一个设置。

答案 10 :(得分:0)

如果您真的需要登录系统,那么您的测试就是垃圾,或者至少是不完整的,而且不是很彻底。系统中的所有内容都应尽可能为黑盒子。请注意像String这样的核心类不需要记录 - 主要原因是它们经过了很好的测试并且执行得非常详细。没有惊喜。

答案 11 :(得分:0)

我使用日志记录来缩小在单元测试中无法重现的问题,更不用说重复用户提供的相同步骤:那些只出现在某些非常远程硬件上的罕见故障(有时候,虽然非常很少,甚至是由我们控制之外的司机或第三方库故障引起的。)

我同意这一点应该完全由我们的测试程序捕获,但很难找到一个百万+ LOC代码库,需要非常低级,性能关键的代码才能满足这些要求。我不从事任务关键型软件,但我在图形行业工作,我们经常需要做的一切,从实现内存分配器到利用GPU代码到SIMD。

即使使用非常模块化,松散耦合或甚至完全解耦的代码,系统交互也会导致非常复杂的输入和输出,并且平台之间的行为会有所不同,偶尔我们会遇到无法完成测试的流氓边缘情况。模块化黑匣子可以非常简单,但它们之间的相互作用可能变得非常复杂,并导致偶然的意外边缘情况。

作为记录保存我的屁股的情况的一个示例,有一次我让这个奇怪的用户使用了崩溃的原型英特尔机器。我们列出了应该支持SSE 4的机器的最低要求,但是这台特定的机器满足了这些最低要求,并且仍然不支持超过SSE 3的流式SIMD扩展,尽管它是16核机器。通过查看准确显示使用SSE 4指令的行号的日志,可以快速发现这一点。我们团队中的任何人都无法重现问题,更不用说参与验证报告的其他用户了。理想情况下,我们应该为较旧的SIMD版本编写代码,或者至少进行一些分支和检查以确保硬件支持最低要求,但我们希望通过最低硬件要求进行通信,以简化和经济。在这里,或许可以说,我们的最低系统要求有“#34;故障”。

鉴于我在这里使用日志记录的方式,我们倾向于获得相当大的日志。但是,目标不是可读性 - 当用户遇到某种类型的崩溃时,通常会发送日志的最后一行是什么,我们这些人都没有(更不用说其他几个用户了)在世界上)可以重现。

尽管如此,我经常使用的一个技巧是避免过多的日志垃圾邮件,通常可以合理地假设一段成功执行一次的代码也会随后这样做(不是硬保证,但通常是合理的假设)。所以我经常对粒度函数使用log_once类函数,以避免每次调用时都支付日志记录的开销。

我不会在整个地方撒上日志输出(如果我有时间的话,我可能会这样做)。通常我会将它们保留在看起来最危险的区域:调用GLSL着色器的代码,例如: (GPU供应商在功能方面,甚至编译代码方面都有很大不同),使用SIMD内在函数的代码,非常低级的代码,不可避免地必须依赖于特定于操作系统的行为的代码,低级代码对代码的假设。 POD的表示形式(例如:假设8位到一个字节的代码) - 我们同样会进行大量断言和健全性检查以及编写大量单元测试的情况。通常情况下这已经足够了,并且日志记录已经多次保存了我的屁股,否则我将不得不采取一个不可重现的问题,并且不得不盲目地解决这个问题,需要许多迭代器试图解决世界上一个用户的问题。可以重现这个问题。