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


126

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

たとえば、CheckstyleのRegexpSinglelineチェックから:

このチェックを使用して、ex.printStacktrace()の呼び出しなどの一般的な悪い習慣を見つけることができます。

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

  1. スタックトレースがエンドユーザーに表示されないようにする必要があります(ユーザーエクスペリエンスとセキュリティ上の理由から)

  2. スタックトレースの生成は比較的コストのかかるプロセスです(ほとんどの「例外的な」状況では問題になる可能性は低いですが)。

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

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

コードでスタックトレースの出力を回避する他の理由は何ですか?


12
定期的にトラブルシューティングを行う必要がある人として、何か問題が発生したときにスタックトレースを出力することを省略しません。はい、ユーザーには表示しませんが、エラーログにダンプします。
ポールグライム、2011

4
他の理由が本当に必要ですか?あなたはあなた自身の質問にかなりよく答えたと思います。ただし、例外的な例外ではスタックトレースを出力する必要があります。:)
Neil

回答:


125

Throwable.printStackTrace()スタックトレースをSystem.errPrintStreamに書き込みます。System.errストリームとJVMプロセスの基礎となる標準「エラー」出力ストリームがでリダイレクトすることができます

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

上記から推論すると、呼び出しThrowable.printStackTrace()は有効な(優れていない)例外処理動作を構成しますが、

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

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

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

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

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


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


すばらしい答え、ありがとう。ただし、ほとんどの場合、そのノイズは不要であることに同意しますが、絶対的に重要な場合もあります。
クリスナイト

1
@クリスはい、そこにあなたがたが使用できない時間があるSystem.out.printlnと、Throwable.printStackTraceそしてもちろん、開発者の判断が必要とされます。スレッドセーフに関する部分が見落とされていることを少し心配しました。ほとんどのロガー実装を見ると、ログレコードが書き込まれる部分(コンソールにも)を同期していることがわかります。ただし、System.errまたはではモニターを取得しませんSystem.out
Vineet Reynolds、2011

2
これらの理由のどれもが、PrintStreamまたはPrintWriterオブジェクトをパラメーターとして取るprintStackTraceオーバーライドに当てはまらないことに注意するのは間違っていますか?
ESRogs 2013

1
@Geek、後者は値2のプロセスファイル記述子です。前者は単なる抽象概念です。
Vineet Reynolds 2014

1
printStackTrace()のJDKソースコードでは、System.err PrintStreamをロックするためにsynchronizedを使用するため、スレッドセーフなメソッドである必要があります。
EyouGo 2018年

33

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

1)スタックトレースがエンドユーザーに表示されないようにする(ユーザーエクスペリエンスとセキュリティの目的で)

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

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

2)スタックトレースの生成は比較的コストのかかるプロセスです(ただし、ほとんどの「例外」状況では問題になる可能性は低いです)

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

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

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

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

4)スタックトレースを出力しても、エラー処理にはなりません。他の情報ロギングや例外処理と組み合わせる必要があります。

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

しかし、彼すぐにあなたに電話し、あなたは問題を診断することができる必要があります。したがって、適切な例外ロギングと使いやすいメッセージの両方が必要です。


まとめ:常に例外をログに記録し(できればロギングフレームワークを使用)、例外をエンドユーザーに公開しないでください。GUIのエラーメッセージについて慎重に検討し、開発モードでのみスタックトレースを表示します。


あなたの答えは私が得たものです。
Blasanka

「ほとんどの「例外」の状況」。いいね。
awwsmm 2018年

19

スタックトレースは例外自体が作成されたときに満たされるため、最初にprintStackTrace()を使用してもコストはかかりません。

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


11

例外のスタックトレース自体を印刷すること自体は悪い習慣ではありませんが、例外が発生したときにトレーストレースを印刷するだけで問題が生じる可能性があります。多くの場合、スタックトレースを印刷するだけでは不十分です。

また、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です。)

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


1
この。ほとんどの場合、少なくともメソッドレベルでは、例外をまったくキャッチない方が、キャッチしてスタックトレースを出力し、問題が発生していないかのように続行するよりも優れています。
eisは2017

10

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

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

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

上記のコードの問題は、処理が完全printStackTrace呼び出しで構成れていることです。例外は実際には適切に処理されず、エスケープすることもできません。

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

最後に、軽いメモとして、神の完全な例外


「例外がエスケープすることは許可されていません」-例外がスタックに伝播されることを意味しましたか?ログとprintStackTrace()の違いは何ですか?
MasterJoe

5

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


有効なポイントですが、本番コンソールの出力を注意深く監視しています。
クリスナイト

注意深く見て、あなたの意味を説明できますか?どのように、誰が?どの頻度で?
Oh Chin Boon

注意深く見れば、必要なときに言っておくべきだった。そのローリングログファイルは、アプリで問題が発生した場合の呼び出しのいくつかのポートの1つです。
クリスナイト

3

サーバーアプリケーションでは、スタックトレースによってstdout / stderrファイルが破壊されます。通常はコンテキストもタイムスタンプもないため、データはますます大きくなり、役に立たないデータでいっぱいになります。

例:コンテナとしてtomcatを使用する場合のcatalina.out


いい視点ね。printStackTrace()の乱用は、ロギングファイルを破壊するか、少なくとも、無用の情報の範囲でいっぱいになる可能性があります
Chris Knight

@ChrisKnight-ログファイルが役に立たない情報でどのように爆破されるかを説明する記事を提案していただけませんか?ありがとう。
MasterJoe

3

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

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


0

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

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


0

@Vineet Reynoldsが言及する、絡まった出力ストリームの問題を回避する

あなたはそれをstdoutに印刷することができます: e.printStackTrace(System.out);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.