回答:
TL; DR:StandardError
一般的な例外のキャッチに代わりに使用します。元の例外が再度発生した場合(例:例外のみをログに記録するように救出した場合)、救出Exception
はおそらく問題ありません。
Exception
根であるRubyの例外階層、だからrescue Exception
あなたがから救うすべてのようなサブクラスを含む、SyntaxError
、LoadError
、とはInterrupt
。
救出Interrupt
すると、ユーザーCTRLCがプログラムを終了するために使用できなくなります。
救出SignalException
すると、プログラムがシグナルに正しく応答できなくなります。以外では殺せませんkill -9
。
救助 SyntaxError
とはeval
、失敗したsが黙ってそうすることを意味します。
これらのすべては、このプログラムを実行している、としようによって示すことができるCTRLCか、kill
それ。
loop do
begin
sleep 1
eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
rescue Exception
puts "I refuse to fail or be stopped!"
end
end
からの救出Exception
はデフォルトではありません。している
begin
# iceberg!
rescue
# lifeboats
end
からException
は救いませんが、からは救出しStandardError
ます。あなたは、一般的に、デフォルトよりも、より具体的なものを指定する必要がありStandardError
ますが、から救出Exception
広がり範囲をではなく、それを狭くし、壊滅的な結果を持っているとバグ狩りは非常に困難になります。
救済したい状況でStandardError
、例外を含む変数が必要な場合は、次の形式を使用できます。
begin
# iceberg!
rescue => e
# lifeboats
end
これは次と同等です:
begin
# iceberg!
rescue StandardError => e
# lifeboats
end
救出するのが常識であるいくつかの一般的なケースの1つは、Exception
ロギング/レポート作成の目的です。この場合、すぐに例外を再発生させる必要があります。
begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end
Throwable
、Javaでキャッチするようなものです
ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
、その後rescue *ADAPTER_ERRORS => e
本当のルールは次のとおりです。例外を捨てないでください。あなたの引用の著者の客観性は疑問です、それがそれで終わるという事実によって証明されるように
または私はあなたを刺します
もちろん、シグナルは(デフォルトで)例外をスローし、通常は長時間実行されるプロセスはシグナルを介して終了するため、例外をキャッチしてシグナル例外で終了しないと、プログラムを停止するのが非常に難しくなります。したがって、これを行わないでください。
#! /usr/bin/ruby
while true do
begin
line = STDIN.gets
# heavy processing
rescue Exception => e
puts "caught exception #{e}! ohnoes!"
end
end
いいえ、本当に、それをしないでください。動作するかどうか確認するためにそれを実行しないでください。
ただし、スレッド化されたサーバーがあり、すべての例外を禁止したいとします。
thread.abort_on_exception = true
)。 次に、これは接続処理スレッドで完全に受け入れられます。
begin
# do stuff
rescue Exception => e
myLogger.error("uncaught #{e} exception while handling connection: #{e.message}")
myLogger.error("Stack trace: #{backtrace.map {|l| " #{l}\n"}.join}")
end
上記は、Rubyのデフォルトの例外ハンドラのバリエーションで機能しますが、プログラムを強制終了しないという利点があります。Railsはリクエストハンドラでこれを行います。
メインスレッドでシグナル例外が発生します。バックグラウンドスレッドはそれらを取得しません。そのため、それらをキャッチしようとしても意味がありません。
これは、何か問題が発生したときにプログラムを単に停止させたくない運用環境で特に役立ちます。次に、ログのスタックダンプを取得してコードに追加し、コールチェーンのさらに下で、より適切な方法で特定の例外を処理できます。
また、ほぼ同じ効果を持つ別のRubyイディオムがあることにも注意してください。
a = do_something rescue "something else"
この行でdo_something
、例外が発生した場合、Rubyによってキャッチされ、破棄され、a
が割り当てられ"something else"
ます。
心配する必要がないことがわかっている特別な場合を除いて、通常はこれを行わないでください。一例:
debugger rescue nil
の debugger
関数は、コードにブレークポイントを設定するためのかなり良い方法ですが、デバッガーやRailsの外で実行すると、例外が発生します。理論的には、デバッグコードをプログラム内に置いたままにしないでください(pff!だれもそれをしません!)。しかし、デバッガを継続的に実行せずに、何らかの理由でしばらくそこに置いておきたい場合があります。
注意:
シグナル例外をキャッチして無視する他の誰かのプログラムを実行した場合(上記のコードを言う):
pgrep ruby
、またはps | grep ruby
を入力して、問題のプログラムのPIDを探し、を実行しkill -9 <PID>
ます。 何らかの理由でこれらのignore-exceptionブロックが実行されている他の誰かのプログラムを使用している場合は、これをメインラインの先頭に配置することが1つの可能なコプトアウトです。
%W/INT QUIT TERM/.each { |sig| trap sig,"SYSTEM_DEFAULT" }
これにより、プログラムは、クリーンアップなしで、例外ハンドラーをバイパスして直ちに終了することにより、通常の終了信号に応答します 。そのため、データの損失などが発生する可能性があります。注意してください!
これを行う必要がある場合:
begin
do_something
rescue Exception => e
critical_cleanup
raise
end
あなたは実際にこれを行うことができます:
begin
do_something
ensure
critical_cleanup
end
2番目のケースでcritical cleanup
は、例外がスローされるかどうかにかかわらず、毎回呼び出されます。
kill -9
。
ensure
かどうかに関係rescue
なく実行されますが、例外は発生した場合にのみ実行されます。
しないでくださいrescue Exception => e
(例外を再発生させないでください)。そうしないと、橋を降りてしまう可能性があります。
車に乗っているとします(Rubyを実行しています)。最近、無線アップグレードシステム(を使用eval
)を備えた新しいステアリングホイールを取り付けましたが、プログラマーの1人が構文をめちゃくちゃにしたことを知りませんでした。
あなたは橋の上にいて、手すりに向かって少し進んでいることに気づいたので、左折します。
def turn_left
self.turn left:
end
おっとっと!それはおそらくNot Good ™ SyntaxError
です。幸い、Rubyはをレイズします。
車はすぐに止まるべきです-そうですか?
いいえ。
begin
#...
eval self.steering_wheel
#...
rescue Exception => e
self.beep
self.log "Caught #{e}.", :warn
self.log "Logged Error - Continuing Process.", :info
end
ビープビープ
警告:SyntaxError例外が発生しました。
情報:ログに記録されたエラー-処理を続行しています。
あなたは、何かが間違っている気づき、あなたは緊急時の休憩にスラム(^C
:Interrupt
)
ビープビープ
警告:割り込み例外をキャッチしました。
情報:ログに記録されたエラー-処理を続行しています。
うん-それはあまり役に立たなかった。あなたはかなりレールに近いので、車を公園に置きます(kill
ing:)SignalException
。
ビープビープ
警告:SignalException例外が発生しました。
情報:ログに記録されたエラー-処理を続行しています。
最後の1秒間に、キー(kill -9
)を引き出し、車が停止し、ステアリングホイールに向かって前方に激突します(プログラムを適切に停止しなかったため、エアバッグは膨張できません-プログラムを終了しました)。あなたの車の後ろの車の前の座席に激突します。半分いっぱいのコーラの缶が紙の上にこぼれます。奥の食料品は砕かれ、ほとんどが卵黄と牛乳で覆われています。車は深刻な修理と清掃を必要とします。(データロス)
うまくいけば、あなたは保険(バックアップ)を持っています。ああそう-エアバッグが膨張しなかったので、おそらくあなたは怪我をしています(発砲するなど)。
ちょっと待って!ありますもっと使用したくなる理由rescue Exception => e
!
あなたがその車で、車が安全な停止の勢いを超えている場合にエアバッグが確実に膨張するようにしたいとします。
begin
# do driving stuff
rescue Exception => e
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
raise
end
ルールの例外は次のとおりです。キャッチできるのは、例外をException
再度発生させた場合のみです。したがって、より良いルールは、決して飲み込まException
ず、常にエラーを再発生させることです。
しかし、レスキューを追加することは、Rubyなどの言語では忘れがちであり、問題を再提起する直前にレスキューステートメントを置くことは、少しDRYではないように感じます。そして、あなたはraise
ステートメントを忘れたくありません。もしそうなら、そのエラーを見つけようと頑張ってください。
ありがたいことに、Rubyは素晴らしいですensure
。キーワードを使用するだけで、コードを確実に実行できます。ensure
キーワードは、どんなコードを実行しません-例外がスローされる場合は、1つでない場合、唯一の例外があればという世界が終了する(または他の可能性は低い事象)。
begin
# do driving stuff
ensure
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
end
ブーム!そして、そのコードはとにかく実行する必要があります。使用rescue Exception => e
する必要がある唯一の理由は、例外にアクセスする必要がある場合、または例外でコードを実行するだけの場合です。そして、エラーを再度発生させることを忘れないでください。毎回。
注:@Niallが指摘したように、常に実行されるようにしてください。問題が発生した場合でも、プログラムが嘘をつき、例外をスローしないことがあるので、これは良いことです。エアバッグの膨張などの重要なタスクでは、何が起こっても確実に発生するようにする必要があります。このため、例外がスローされるかどうかにかかわらず、車が停止するたびに確認することをお勧めします。エアバッグの膨張は、ほとんどのプログラミングコンテキストでは一般的ではないタスクですが、これは実際にはほとんどのクリーンアップタスクでかなり一般的です。
ensure
代替としての部分rescue Exception
は誤解を招く-例はそれらが同等であることを示唆していますがensure
、例外があるかどうかにかかわらず発生するので、何も問題がなくても5mphを超えたため、エアバッグが膨張します。
これはすべての例外をキャプチャするためです。プログラムがそれらのどれからも回復できる可能性はほとんどありません。
回復方法がわかっている例外のみを処理する必要があります。特定の種類の例外が予期されない場合は、それを処理せず、大音量でクラッシュし(詳細をログに書き込み)、ログを診断してコードを修正します。
例外を飲み込むのはよくありません。これをしないでください。
それはあなたがキャッチしてはならないというルールの具体的なケースだ任意のあなたが処理する方法がわからない例外を。それを処理する方法がわからない場合は、システムの他の部分にキャッチさせて処理させることをお勧めします。
honeybadger.ioで素晴らしいブログ投稿を読んだだけです。
例外を救うべきでない理由
Exceptionのレスキューに関する問題は、Exceptionから継承するすべての例外を実際にレスキューすることです。それは...すべてです!
Rubyの内部で使用されるいくつかの例外があるため、これは問題です。彼らはあなたのアプリとは何の関係もありません、そしてそれらを飲み込むと悪いことが起こります。
ここにいくつかの大きなものがあります:
SignalException :: Interrupt-これを救うと、control-cを押してアプリを終了できなくなります。
ScriptError :: SyntaxError-構文エラーを飲み込むと、puts( "Forgot something)などのメッセージが表示されずに失敗します。
NoMemoryError-プログラムがすべてのRAMを使い果たした後、プログラムが実行し続けるとどうなるか知りたいですか?私もダメ。
begin do_something() rescue Exception => e # Don't do this. This will swallow every single exception. Nothing gets past it. end
これらのシステムレベルの例外を実際に飲み込みたくないと思います。アプリケーションレベルのエラーをすべてキャッチしたいだけです。例外が原因でコードが発生しました。
幸いなことに、これを行う簡単な方法があります。