web-dev-qa-db-ja.com

exception.printStackTrace()が悪い習慣と見なされるのはなぜですか?

lot of material out there があります。これは、例外のスタックトレースを印刷するのは悪い習慣であることを示唆しています。

例えば。 CheckstyleのRegexpSinglelineチェックから:

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

ただし、スタックトレースは例外の原因を追跡するのに非常に役立つため、正当な理由を提供する場所を見つけるのに苦労しています。私が知っていること:

  1. スタックトレースがエンドユーザーに表示されることはありません(ユーザーエクスペリエンスとセキュリティの目的で)

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

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

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

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

118
Chris Knight

Throwable.printStackTrace()は、スタックトレースをSystem.err PrintStreamに書き込みます。 JVMプロセスのSystem.errストリームおよび基礎となる標準の「エラー」出力ストリームは、次の方法でリダイレクトできます。

  • System.setErr() を呼び出して、System.errが指す宛先を変更します。
  • またはプロセスのエラー出力ストリームをリダイレクトします。エラー出力ストリームはファイル/デバイスにリダイレクトされる可能性があります
    • その内容は担当者によって無視される可能性があり、
    • ファイル/デバイスはログのローテーションができない場合があり、ファイル/デバイスの既存のコンテンツをアーカイブする前に、開いているファイル/デバイスハンドルを閉じるにはプロセスの再起動が必要であると推測されます。
    • または、/dev/nullの場合のように、ファイル/デバイスはそれに書き込まれたすべてのデータを実際に破棄します。

上記から推測すると、Throwable.printStackTrace()の呼び出しは、有効な(良い/素晴らしいではない)例外処理動作を構成します。

  • アプリケーションの存続期間中にSystem.errが再割り当てされない場合、
  • アプリケーションの実行中にログのローテーションを必要としない場合、
  • また、アプリケーションのロギングプラクティスが受け入れ/設計されている場合、System.err(およびJVMの標準エラー出力ストリーム)に書き込むことです。

ほとんどの場合、上記の条件は満たされません。 JVMで実行されている他のコードを認識していない可能性があり、ログファイルのサイズやプロセスの実行時間を予測できないため、適切に設計されたロギングプラクティスは「マシン解析可能な」ログファイルの書き込みを中心に展開されますサポートを支援するために、既知の宛先にあるロガーの望ましいがオプションの機能)。

最後に、Throwable.printStackTrace()の出力は、System.err(および両方が同じファイル/デバイスにリダイレクトされる場合は、おそらくSystem.outに書き込まれる他のコンテンツと確実にインターリーブされることを覚えておく必要があります。これは、そのようなイベントでは例外に関するデータを簡単に解析できないため、対処しなければならない面倒なことです(シングルスレッドアプリの場合)。さらに悪いことに、Throwable.printStackTrace()はスレッドセーフではないため、マルチスレッドアプリケーションが非常に紛らわしいログを生成する可能性が高くなります。

複数のスレッドが同時にThrowable.printStackTrace()を呼び出すときに、スタックトレースの書き込みをSystem.errに同期する同期メカニズムはありません。これを解決するには、実際にコードをSystem.err(および宛先ファイル/デバイスが同じ場合はSystem.out)に関連付けられたモニター上で同期する必要があります。 。例を挙げると、ConsoleHandlerクラスとStreamHandlerクラスは、Java.util.loggingが提供するロギング機能で、コンソールにログレコードを追加する役割を果たします。ログレコードの公開の実際の操作は同期されます。ログレコードを公開しようとするすべてのスレッドは、StreamHandlerインスタンスに関連付けられたモニターのロックも取得する必要があります。 System.out/System.errを使用してインターリーブされていないログレコードを保持することを保証する場合は、同じことを確認する必要があります。メッセージはこれらのストリームにシリアル化可能な方法で公開されます。

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


前の段落のいずれかで引数を拡張すると、Throwable.printStackTraceをコンソールに書き込むロガーと組み合わせて使用​​することも適切ではありません。これは、ロガーが別のモニターで同期し、アプリケーションが(おそらく、インターリーブされたログレコードが必要ない場合)別のモニターで同期するためです。この引数は、アプリケーションで同じ宛先に書き込む2つの異なるロガーを使用する場合にも有効です。

115
Vineet Reynolds

ここで複数の問題に触れています:

1)スタックトレースは、エンドユーザーに表示されることはありません(ユーザーエクスペリエンスとセキュリティのため)

はい、エンドユーザーの問題を診断するためにアクセスできる必要がありますが、エンドユーザーは2つの理由でそれらを見るべきではありません。

  • これらは非常に不明瞭で読みにくいため、アプリケーションは非常にユーザーフレンドリーではありません。
  • エンドユーザーにスタックトレースを表示すると、潜在的なセキュリティリスクが生じる可能性があります。私が間違っている場合は修正してください、PHPは実際にスタックトレースで関数パラメーターを出力します-素晴らしいですが、非常に危険です-データベースへの接続中に例外が発生する場合、スタックトレースで何をしますか?

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

スタックトレースの生成は、例外が作成/スローされるときに発生します(そのため、例外のスローには代償が伴います)。印刷はそれほど高価ではありません。実際、カスタム例外でThrowable#fillInStackTrace()をオーバーライドして、簡単なGOTOステートメントと同じくらい簡単に例外をスローすることができます。

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

非常に良い点。ここでの主な問題は、フレームワークが例外をログに記録する場合、何もしないことです(ただし、確認してください!)例外を自分で記録する場合は、 Logback または Log4J 、それを制御するのが非常に難しいので、生コンソールにそれらを置かないため。

ロギングフレームワークを使用すると、スタックトレースをファイル、コンソールに簡単にリダイレクトしたり、指定した電子メールアドレスに送信することもできます。ハードコードされたprintStackTrace()では、sysoutと一緒に暮らす必要があります。

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

再度:SQLExceptionを正しく記録し(ロギングフレームワークを使用して、完全なスタックトレースで)、ニースを表示します:「申し訳ありませんが、現在、リクエストを処理できません」ユーザーはその理由に本当に興味があると思いますか? StackOverflowエラー画面を見たことがありますか?それは非常にユーモラスですが、どんな詳細も明らかにしません。ただし、問題が調査されることをユーザーに保証します。

しかし、彼willはすぐにあなたを呼び出し、あなたは問題を診断できるようにする必要があります。したがって、適切な例外ロギングとユーザーフレンドリーなメッセージの両方が必要です。


最後に:always例外をログに記録します(できれば logging framework を使用します)が、エンドユーザーには公開しません。 GUIのエラーメッセージについて慎重に検討し、開発モードでのみスタックトレースを表示します。

32

最初にprintStackTrace()は、例外が作成されたときにスタックトレースが書き込まれるため、あなたが述べているように高価ではありません。

アイデアは、ロガーフレームワークを介してログに送られるすべてのものを渡すことで、ロギングを制御できるようにします。したがって、printStackTraceを使用する代わりに、Logger.log(msg, exception);のようなものを使用してください

17
Suraj Chandran

例外のスタックトレース自体を印刷することは悪い習慣ではありませんが、only例外が発生したときにステージトレースを印刷することはおそらくここの問題です-しばしばスタックトレースを印刷するだけでは不十分です。

また、catchブロックで実行されているすべてがe.printStackTraceである場合、適切な例外処理が実行されていないと疑う傾向があります。不適切な処理は、せいぜい問題が無視され、最悪の場合未定義または予期しない状態で実行を継続するプログラムを意味します。

次の例を考えてみましょう。

try {
  initializeState();

} catch (TheSkyIsFallingEndOfTheWorldException e) {
  e.printStackTrace();
}

continueProcessingAssumingThatTheStateIsCorrect();

ここでは、初期化が行われたことが必要な処理に進む前に、初期化処理を行いたいと思います。

上記のコードでは、プログラムがcontinueProcessingAssumingThatTheStateIsCorrectメソッドに進まないように例外をキャッチして適切に処理する必要があります。

多くの場合、e.printStackTrace()は、何らかの例外が飲み込まれており、すべての問題が発生していないかのように処理を続行できることを示しています。

なぜこれが問題になったのですか?

おそらく、貧弱な例外処理が普及している最大の理由の1つは、EclipseなどのIDEが例外処理のためにe.printStackTraceを実行するコードを自動生成する方法によるものです。

try {
  Thread.sleep(1000);
} catch (InterruptedException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

(上記は、try-catchによってスローされたInterruptedExceptionを処理するためにEclipseによって自動生成された実際のThread.sleepです。)

ほとんどのアプリケーションでは、スタックトレースを標準エラーに出力するだけではおそらく十分ではありません。不適切な例外処理は、多くの場合、アプリケーションが予期しない状態で実行され、予期しない未定義の動作を引き起こす可能性があります。

11
coobird

理由のリストはかなり包括的なものだと思います。

私が複数回遭遇した特に悪い例の1つは、次のようになります。

    try {
      // do stuff
    } catch (Exception e) {
        e.printStackTrace(); // and swallow the exception
    }

上記のコードの問題は、処理がprintStackTrace呼び出しの全体で構成されていることです。例外は実際には適切に処理されず、そうではありません逃げることができました。

一方、原則として、コードに予期しない例外がある場合は常にスタックトレースを記録します。長年にわたって、このポリシーはデバッグ時間を大幅に節約してくれました。

最後に、より軽いメモで、 神の完全な例外

9
NPE

printStackTrace()はコンソールに出力します。本番環境では、誰もそれを見ていません。 Surajは正しいです。この情報をロガーに渡す必要があります。

4
Oh Chin Boon

サーバーアプリケーションでは、スタックトレースがstdout/stderrファイルを爆破します。通常はコンテキストもタイムスタンプもないなどの理由で、ますます大きくなり、役に立たないデータでいっぱいになります。

例えばTomcatをコンテナとして使用する場合のcatalina.out

3
Hajo Thelen

PrintStackTrace()について何かが「間違っている」のは悪い習慣ではありませんが、「コードのにおい」だからです。ほとんどの場合、誰かが例外を適切に処理できなかったため、PrintStackTrace()呼び出しがあります。適切な方法で例外に対処すると、通常、StackTraceを気にしなくなります。

さらに、stderrでスタックトレースを表示することは、デバッグ時にのみ有用であり、ほとんどの場合stderrはどこにも行かないため、本番環境では役立ちません。ロギングする方が理にかなっています。しかし、PrintStackTrace()を例外のログに置き換えるだけで、失敗したアプリケーションが残りますが、何も起こらなかったように実行し続けます。

3
AVee

ここで既に述べたように、問題はcatchブロックでe.printStackTrace()を呼び出すだけの場合の例外の飲み込みにあります。スレッドの実行は停止せず、通常の状態のようにtryブロックの後も継続します。

その代わりに、例外から回復する(回復可能な場合)か、RuntimeExceptionをスローするか、サイレントクラッシュを回避するために呼び出し元に例外をバブルする(たとえば、不適切なために)必要があります。ロガー構成)。

0
gumkins