有没有不记录堆栈跟踪的理由?

12
今天在我们的应用程序中遇到了一个令人沮丧的问题,最终发现是抛出了一个ArrayIndexOutOfBounds异常。日志只记录了异常类型,这几乎没有用(但是,哦,可怜的旧应用程序,我们仍然喜欢你,大部分时间)。我重新部署了应用程序,并更改了日志记录方式以记录异常处理时的堆栈跟踪(并立即找到了问题的根本原因),想知道为什么之前没有其他人做过这个。您一般是否记录堆栈跟踪,有没有任何理由不这样做?
如果您可以解释(为什么而不是如何)在Java中需要跳跃才能获取堆栈跟踪的字符串表示形式的原理,那将会得到额外的奖励分数!

这个遗留系统是否使用了 Log4J?如果是,它是否使用了 org.apache.log4j.RollingFileAppender?我在银行应用程序中使用它(是的,这个系统非常庞大),但我完全没有遇到任何问题。 - Buhake Sindi
2
从问题中看不出他在记录整个堆栈跟踪方面遇到了任何问题;相反,他似乎在问为什么之前的开发人员没有这样做。 - danben
@danben - 正确,这个问题是关于记录堆栈跟踪的一般惯例,而不是如何记录的。 - Chris Knight
我总是尝试使用try {...} catch (Exception e) { Logger.Error(e); }来处理我不知道如何处理的异常。生成堆栈跟踪非常缓慢,但这并不重要,因为这样的错误不应该经常发生。 - stmax
有些人使用 logger.info()logger.debug() 将“有用”的信息传递到日志中。因此,记录必要/不必要的数据(取决于您的观点)可能是有争议的。我之所以问这个问题,是因为似乎有太多的信息传递到了记录器中,而且开发人员似乎从未充分利用记录器的效率。 - Buhake Sindi
@The Elite Gentleman - 我们的应用程序记录性能没有问题,我们也不使用Log4J。 - Chris Knight
7个回答

11
  • 一些日志可能包含敏感数据,日志设施不一定足够安全以在生产环境中追踪该数据。

  • 过多日志记录会导致信息过载,即对系统管理员来说根本没有任何信息可用。如果日志充满了调试消息,他们将无法识别可疑的模式。(多年前我看到一个为安全原因记录所有系统调用的系统。有这么多日志,当一些非特权用户开始成为root时,没有人注意到它。)

最好的方法是记录所有适当的日志级别,并能够在生产环境中设置日志级别(至少在Java中不是一个大问题)。


1
不确定堆栈跟踪可能包含敏感信息。 - matt b
2
@matt b:已经知道特定的类名就足以构建攻击向量(你还记得 Hibernate Validator x.y.z 中的那个漏洞吗……)在安全领域,如果你无法想象出一种情况,最好假设最坏的情况。 - sibidiba
我不记得那个 bug,但如果你有链接的话,我很想看一下它的描述。 - matt b
这不是真正的 bug。只要你告诉大家你正在使用哪些类,就会更容易找到针对该特定库的 bug。 - sibidiba
1
我们做两种日志记录,一种为信息级别,一种为调试级别,而信息级别故意是相当静默的。这意味着,针对特定问题的初始调查者可以查看信息日志以获取概述,并希望立即解决实际问题。调试日志是供维护人员基于过去发生的情况进行代码修复的。这种区分对我们非常有效。 - Thorbjørn Ravn Andersen
如果您正确地索引日志并使其可搜索(现在拥有类似Splunk这样的东西不是必须的吗?),那么“信息过多”的论点就不相关了。 - masterxilo

3

2

通常我会记录堆栈跟踪,因为它包含了故障排除和调试问题所需的信息。它是仅次于迷你转储的最佳选择,并且通常通过代码检查和识别问题来找到解决方案。

顺便说一下,我同意sibidiba关于完整堆栈可能泄露应用程序内部信息的观点:函数名称以及堆栈调用序列可以向受过教育的读者提供很多信息。这就是为什么有些产品只记录堆栈上的符号地址,并依靠开发人员从内部pdb中解析地址到名称。

但在我看来,将文本记录到包含1行错误和14行堆栈的文件中会使错误日志非常难以导航。它还会在高并发应用程序上引起问题,因为锁定日志文件的时间更长(或更糟糕的是,日志文件会交错)。在支持和故障排除我的自己的应用程序部署时遇到了这些问题,这促使我实际上创建了一个服务来记录错误,网址是bugcollect.com。在设计错误收集策略时,我选择每次收集堆栈转储,并将堆栈用作桶键的一部分(将发生在相同堆栈上的错误分组到同一个桶中)。


1

在开发人员记录过于自由和系统管理员发现应用程序一旦被放置在生产负载下,就会崩溃并填充大量日志文件时,通常会实施有关记录的限制。然后很难说服他们相信你已经看到了自己的错误,并已经足够减少了记录(或调整了日志级别),但是确实需要这些剩余的日志条目。


4
没错,我完全同意,但我无法想象创建带有堆栈跟踪的大型日志文件。如果是这种情况,那么也许大型日志文件并不是你最大的问题 :) Translated: Yes, I completely agree, but I can't imagine creating large log files with stack traces. If that's the case, then perhaps large log files aren't your biggest problem :) - Chris Knight
@Chris:可能是的,但当在大系统的某个模块内部工作时,很容易失去整体视野,并认为某些异常情况只会在生产环境中每秒发生十几次。 - Michael Borgwardt

1
对我们来说很简单:如果出现意外异常,我们记录堆栈跟踪以及尽可能详细的消息。
我猜测,编写原始代码的开发人员可能经验不足,不知道仅仅提供消息是不够的。我曾经也这样想过。
获取堆栈跟踪字符串的原因是因为JRE中没有StringPrintWriter - 我认为他们的思路是提供许多正交构建块,然后按需组合。您必须自己组装所需的PrintWriter。

0
如果您能解释(为什么而不是如何)在Java中为获取堆栈跟踪的字符串表示而必须跳过多个步骤,那么您将获得加分!
您不应该通过跳过多道环节来打印堆栈跟踪,而应该记录可抛出的错误。例如:log.error("Failed to deploy!", ex)。Log4J将打印通过getMessage()方法获取的错误信息和堆栈跟踪。

如上所述,我没有使用Log4J。 - Chris Knight

0

我经常看到的是像这样记录异常的代码:

LOG.error(ex);

因为log4j接受一个对象作为第一个参数,它将记录异常的字符串表示形式,通常只是类的名称。这通常只是开发人员疏忽了。最好像这样记录和报错:

LOG.error("发生错误", ex);

...这样,如果配置正确,日志框架将记录堆栈跟踪。


不确定为什么这里的每个人都认为我在使用Log4J! - Chris Knight

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接