138

例外のスタック トレースを出力することは悪い習慣であることを示唆する多くの資料あります。

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

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

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

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

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

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

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

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

4

9 に答える 9

136

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

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

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

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

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

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

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

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


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

于 2011-09-19T12:09:35.140 に答える
34

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

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

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

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

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

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

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

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

ロギング フレームワークを使用すると、スタック トレースをファイルやコンソールに簡単にリダイレクトしたり、指定した電子メール アドレスに送信したりすることもできます。ハードコーディングされprintStackTrace()ているため、sysout.

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

繰り返しますSQLExceptionが、(ロギング フレームワークを使用して、完全なスタック トレースを使用して) 正しくログを記録し、nice: " Sorry, we are not able to process your request " というメッセージを表示します。ユーザーがその理由に興味を持っていると本当に思いますか? StackOverflow エラー画面を見たことがありますか? とてもユーモラスですが、詳細は明らかにされていませ。ただし、問題が調査されることをユーザーに保証します。

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


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

于 2011-09-19T10:23:10.377 に答える
22

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

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

于 2011-09-19T10:18:34.867 に答える
12

例外のスタック トレースを出力すること自体は悪い習慣ではありませんが、例外が発生したときにステータス トレースのみを出力することがおそらくここでの問題です。多くの場合、スタック トレースを出力するだけでは十分ではありません。

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を処理するために Eclipse によって実際に自動生成されたものです。)InterruptedExceptionThread.sleep

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

于 2011-09-19T10:23:54.777 に答える
10

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

私が何度も遭遇した特に悪い例の 1 つは、次のようなものです。

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

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

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

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

于 2011-09-19T10:24:53.527 に答える
5

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

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

于 2011-09-19T10:28:36.237 に答える
5

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

于 2011-09-19T10:20:39.873 に答える
4

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

たとえば、tomcat をコンテナーとして使用する場合は catalina.out

于 2011-09-19T10:19:28.070 に答える