调试/记录代码确实是侵入性的。在我们的 C++ 项目中,我们将常见的调试/日志代码包装在宏中——非常类似于断言。我经常发现日志记录在较低级别的组件中最有用,因此它不必无处不在。
在其他答案中有很多同意和不同意:) 拥有调试/记录代码可能是解决问题的非常有价值的工具。在 Windows 中,有很多技术 - 两个主要的技术是:
- 广泛使用已检查 (DBG) 构建断言并且对 DBG 构建进行大量测试。
- 在我们所说的“fre”或“retail”构建中使用 ETW。
检查过的构建(大多数人称之为调试构建)对我们也很有帮助。我们在 'fre' 和 'chk' 版本上运行所有测试(同样在 x86 和 AMD64 上,所有服务器的东西也在 Itanium 上运行......)。有些人甚至在已检查的版本上自行托管(dogfood)。这有两件事
- 发现了许多其他情况下无法发现的错误。
- 快速消除嘈杂或不必要的断言。
在 Windows 中,我们广泛使用 Event Tracing for Windows (ETW)。 ETW 是一种高效的静态日志记录机制。 NT 内核和许多组件都被很好地检测了。 ETW 有很多优点:
- 可以在运行时动态启用/禁用任何 ETW 事件提供程序 - 无需重新启动或进程重新启动。大多数 ETW 提供程序提供对单个事件或事件组的精细控制。
- 可以将来自任何提供程序(最重要的是内核)的事件合并到单个跟踪中,以便关联所有事件。
- 可以从盒子中复制合并的跟踪并进行完全处理 - 使用符号。
- NT 内核示例 pofile 中断可以生成 ETW 事件 - 这会产生一个非常轻量级的示例分析器,可以随时使用
- 在 Vista 和 Windows Server 2008 上,记录事件是无锁且完全支持多核的 - 每个处理器上的线程可以独立记录事件,而无需在它们之间进行同步。
这对我们来说非常有价值,也适用于您的 Windows 代码 - ETW 可用于任何组件 - 包括用户模式、驱动程序和其他内核组件。
我们经常做的一件事是编写一个流式 ETW 消费者。我没有将 printfs 放入代码中,而是将 ETW 事件放在有趣的地方。当我的组件运行时,我可以随时运行我的 ETW 观察器 - 观察器接收事件并显示它们、控制它们或用它们做其他有趣的事情。
我非常尊重地不同意 tvanfosson。即使是最好的代码也可以从良好实现的日志记录中受益。良好实施的静态运行时日志记录可以直接发现许多问题 - 没有它,您对组件中发生的事情的可见性为零。您可以查看输入、输出和猜测——仅此而已。
这里的关键是“很好地暗示”这个词。仪表必须在正确的位置。像其他任何事情一样,这需要一些思考和计划。如果它不在有用/有趣的地方,那么它不会帮助您在开发、测试或部署场景中发现问题。您还可能有太多的仪器在打开时导致性能问题 - 甚至关闭!
当然,不同的软件产品或组件会有不同的需求。有些事情可能需要很少的工具。但是,一个广泛分散或关键的组件可以极大地受益于精心设计的仪器。
这是一个适合您的场景(注意,这很可能不适用于您...:))。假设您在公司的每个桌面上部署了一个业务线应用程序 - 成百上千的用户。当有人遇到问题时你会怎么做?你会在他们的办公室停下来连接调试器吗?如果是这样,你怎么知道他们有什么版本?你从哪里得到正确的符号?你如何在他们的系统上安装调试器?如果它每隔几个小时或几天才发生一次怎么办?您是否要让系统在一直连接调试器的情况下运行?
您可以想象 - 在这种情况下连接调试器会造成破坏。
如果您的组件使用 ETW 检测,那么您可以要求您的用户简单地打开跟踪;继续做他/她的工作;然后在问题发生时点击“WTF”按钮。更好的是:您的应用程序可能能够自我记录 - 在运行时检测问题并自动神奇地打开记录。它甚至可以在出现问题时向您发送 ETW 文件。
这些只是简单的示例 - 可以通过多种不同方式处理日志记录。我在这里的主要建议是考虑日志记录如何能够帮助您在开发时、测试时以及部署后发现、调试和修复组件中的问题。