为什么将exception.printStackTrace()视为不良做法?

有一个很大的材料了有这表明印刷异常的堆栈跟踪是不好的做法。

例如从Checkstyle中的RegexpSingleline检查:

可以使用此检查找到常见的不良做法,例如调用ex.printStacktrace()

但是,我正在努力寻找能够给出正当理由的任何地方,因为栈跟踪对于肯定导致异常的原因非常有用。我知道的事情:

  1. 最终用户不应看到堆栈跟踪(出于用户体验和安全目的)

  2. 生成堆栈跟踪是一个相对昂贵的过程(尽管在大多数“例外”情况下这不太可能成为问题)

  3. 许多日志记录框架都会为您打印堆栈跟踪记录(我们不这样做,不,我们不能轻易更改它)

  4. 打印堆栈跟踪不构成错误处理。它应与其他信息记录和异常处理结合在一起。

还有什么其他原因可以避免在代码中打印堆栈跟踪?


UYOU
浏览 2418回答 3
3回答

冉冉说

Throwable.printStackTrace()将堆栈跟踪写入System.errPrintStream。System.err可以通过以下方式重定向JVM流程的流和基础标准“错误”输出流:调用System.setErr()会更改所指向的目的地System.err。或通过重定向流程的错误输出流。错误输出流可以重定向到文件/设备人员可能会忽略其内容,文件/设备可能无法进行日志轮换,这意味着在归档文件/设备的现有内容之前,需要重新启动进程才能关闭打开的文件/设备句柄。或文件/设备实际上丢弃所有写入其中的数据,例如的情况/dev/null。从以上推断,调用仅Throwable.printStackTrace()构成有效的(不好/很好的)异常处理行为如果您System.err在应用程序的整个生命周期中都没有被重新分配,如果您在应用程序运行时不需要轮换日志,并且是否接受/设计了应用程序的日志记录实践System.err(包括JVM的标准错误输出流)。在大多数情况下,不满足上述条件。一个人可能不知道JVM中正在运行的其他代码,并且一个人无法预测日志文件的大小或进程的运行时间,因此,精心设计的日志记录实践将围绕编写“机器可解析的”日志文件(记录器中的首选功能),以帮助支持。最后,应该记住,的输出Throwable.printStackTrace()肯定会与写入的其他内容交织在一起System.err(甚至System.out可能两者都重定向到相同的文件/设备)。这是一个必须处理的烦恼(对于单线程应用程序),因为在此类事件中不容易解析异常周围的数据。更糟糕的是,多线程应用程序很可能会生成非常混乱的日志,因为Throwable.printStackTrace() 它不是线程安全的。没有同步机制可以将堆栈跟踪的写入与System.err多个线程同时调用Throwable.printStackTrace()时的同步。要解决此问题,实际上需要您的代码在与之关联的监视器上进行同步System.err(System.out如果目标文件/设备相同,则还要进行同步),这对于支付日志文件的完整性来说是一笔不菲的代价。举一个例子,ConsoleHandlerand StreamHandler类负责在java.util.logging; 提供的日志记录工具中将日志记录追加到控制台。发布日志记录的实际操作是同步的-每个尝试发布日志记录的线程也必须获得与该日志记录关联的监视器上的锁StreamHandler实例。如果希望使用System.out/ 保证具有非交错日志记录System.err,则必须确保相同-消息以可序列化的方式发布到这些流中。考虑到以上所有内容以及在Throwable.printStackTrace()实际中非常有用的非常受限的场景,通常会发现调用它是一种不好的做法。扩展前面段落之一中的参数,Throwable.printStackTrace与写入控制台的记录器结合使用也是一个糟糕的选择。部分原因是由于记录器将在另一个监视器上进行同步,而您的应用程序(可能,如果您不需要交错的日志记录)将在另一个监视器上进行同步。当您在应用程序中使用两个写入相同目的地的不同记录器时,该参数也适用。

有只小跳蛙

打印异常本身的堆栈跟踪并不构成坏习惯,但仅在发生异常时才打印stace跟踪可能是这里的问题-通常,仅打印堆栈跟踪是不够的。此外,如果在一个catch块中执行的所有操作都是a,则有一种可能会怀疑未执行适当的异常处理e.printStackTrace。处理不当可能最多意味着问题会被忽略,最糟糕的是程序会在未定义或意外的状态下继续执行。例让我们考虑以下示例:try {  initializeState();} catch (TheSkyIsFallingEndOfTheWorldException e) {  e.printStackTrace();}continueProcessingAssumingThatTheStateIsCorrect();在这里,我们要进行一些初始化处理,然后再继续进行一些需要进行初始化的处理。在上面的代码中,应该已经捕获了异常并对其进行了适当的处理,以防止程序继续使用continueProcessingAssumingThatTheStateIsCorrect我们认为可能会引起问题的方法。在许多情况下,e.printStackTrace()这表明某些异常正在被吞噬,并且允许进行处理,就好像每个问题都没有发生一样。为什么这会成为问题?糟糕的异常处理变得更加普遍的最大原因之一可能是由于Eclipse之类的IDE如何自动生成e.printStackTrace对异常处理执行代码的代码:try {  Thread.sleep(1000);} catch (InterruptedException e) {  // TODO Auto-generated catch block  e.printStackTrace();}(以上是try-catchEclipse自动生成的实际值,用于处理InterruptedException抛出的异常Thread.sleep。)对于大多数应用程序,仅将堆栈跟踪信息打印为标准错误可能还不够。在许多情况下,不正确的异常处理可能导致应用程序以意外状态运行,并可能导致意外和未定义的行为。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java