1. ホーム
  2. java

[解決済み] なぜexception.printStackTrace()はバッドプラクティスと考えられているのですか?

2022-06-07 04:07:19

疑問点

多くの 材料 アウト そこ で、例外のスタックトレースを表示することは悪い習慣であることを示唆しています。

例:CheckstyleのRengexpSinglelineチェックから。

このチェックは、ex.printStacktrace()を呼び出すような一般的な悪い習慣を見つけるために[...]使用することができます。

しかし、確かにスタックトレースは例外を引き起こしたものを追跡するのに非常に有用であるため、正当な理由を与えてくれる場所を見つけるのに苦労しています。私が認識していること。

  1. スタックトレースはエンドユーザーから決して見えないようにすべきです (ユーザーエクスペリエンスとセキュリティのため)

  2. スタック トレースの生成は比較的高価な処理です (ただし、ほとんどの「例外的な」状況で問題になることはほとんどありません)。

  3. 多くのロギングフレームワークは、あなたのためにスタックトレースを表示します (私たちのフレームワークはそうしませんし、簡単に変更することもできません)。

  4. スタックトレースを印刷することは、エラー処理を構成しません。 他の情報ロギングや例外処理と組み合わせる必要があります。

あなたのコードでスタックトレースを表示しないようにする他の理由は何ですか?

どのように解決するのですか?

Throwable.printStackTrace() にスタックトレースを書き込む。 System.err PrintStream。は System.err ストリームと、JVMプロセスの基礎となる標準的な "error"出力ストリームは、次のようにリダイレクトすることができます。

  • 呼び出す 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 も)、そしてそれはログファイルの正しさのために支払うべきかなり重い代償です。一例を挙げると ConsoleHandlerStreamHandler クラスは、コンソールにログレコードを追加する役割を担っています。 java.util.logging ログレコードを発行する実際の操作は同期化されており、ログレコードを発行しようとするすべてのスレッドは、ログレコードの発行に関連するモニタのロックを取得する必要があります。 StreamHandler インスタンスに関連付けられたモニターのロックを取得する必要があります。を使用してインターリーブされていないログレコードを持つのと同じ保証を得たい場合は、以下のようになります。 System.out / System.err を使用する場合、同じことを保証する必要があります - メッセージは、シリアライズ可能な方法でこれらのストリームに発行されます。

上記のすべて、および Throwable.printStackTrace() が実際に有用である非常に限定されたシナリオを考慮すると、それを呼び出すことはしばしば悪い習慣であることがわかります。


前の段落の議論を発展させると、それはまた、以下のような悪い選択です。 Throwable.printStackTrace をコンソールに書き込むロガーと一緒に使用するのは、あまり良い選択ではありません。これは、ロガーが別のモニター上で同期する一方で、アプリケーションは (おそらく、インターリーブされたログ レコードが必要ない場合は) 別のモニター上で同期するという理由によるものです。この議論は、アプリケーションで、同じ宛先に書き込む 2 つの異なるロガーを使用する場合にも当てはまります。