TL; DR:一般的な例外キャッチのために代わりにStandardError
を使用してください。元の例外が再度発生した場合(例:例外のみをログに記録するために救済した場合)、Exception
を救済することはおそらく大丈夫です。
Exception
は Rubyの例外階層 のルートであるため、rescue Exception
を使用した場合はeverythingを使用します。SyntaxError
、LoadError
、Interrupt
などのサブクラスも含まれます。
Interrupt
を救済すると、ユーザーは使用できなくなります CTRLC プログラムを終了します。
SignalException
を救済すると、プログラムがシグナルに正しく応答しなくなります。 kill -9
以外では殺せないでしょう。
SyntaxError
を救済することは、失敗したeval
が黙ってそうすることを意味します。
これらすべては、このプログラムを実行して、 CTRLC またはkill
it:
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
broadensで範囲を狭めるのではなく範囲から救い出し、壊滅的な結果をもたらし、バグ探しを非常に困難にします。
あなたがStandardError
から救いたいという状況があり、例外のある変数が必要な場合は、このフォームを使うことができます。
begin
# iceberg!
rescue => e
# lifeboats
end
これは以下と同等です。
begin
# iceberg!
rescue StandardError => e
# lifeboats
end
Exception
から救うのが普通である数少ない一般的なケースの1つはロギング/報告の目的のためです、その場合あなたはすぐに例外を再発生させるべきです:
begin
# iceberg?
rescue Exception => e
# do some logging
raise e # not enough lifeboats ;)
end
realの規則は次のとおりです。例外を捨てないでください。それがで終わるという事実によって証明されるように、あなたの引用の著者の客観性は疑問です。
または私はあなたを刺します
もちろん、シグナルは(デフォルトで)例外をスローし、通常は長時間実行されているプロセスはシグナルを介して終了します。そのため、Exceptionをキャッチしてシグナル例外で終了しないとプログラムを停止できなくなります。だからこれをしないでください:
#! /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はこれをリクエストハンドラで行います。
メインスレッドでシグナル例外が発生します。バックグラウンドスレッドはそれらを取得しないので、それらをそこで捕まえようとしても意味がありません。
これは本番環境で特に便利です。not何か問題が発生したときにプログラムを単に停止させたい場合です。その後、スタック内のログをログに記録してコードに追加し、特定の例外をさらにコールチェーンの下位に、より優雅な方法で処理できます。
同じ効果を持つもう一つのRubyイディオムがあることにも注意してください。
a = do_something rescue "something else"
この行で、do_something
が例外を発生させると、それはRubyによってキャッチされて捨てられ、a
に"something else"
が割り当てられます。
あなたがknowである特別な場合を除いて、一般的に、そうしないでください、あなたは心配する必要はありません。一例です。
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
後者の場合、例外がスローされるかどうかにかかわらず、critical cleanup
が毎回呼び出されます。
あなたが車に乗っている(Rubyを実行している)としましょう。最近、無線アップグレードシステム(eval
を使用)を備えた新しいステアリングホイールを取り付けましたが、シンタックスを台無しにしたプログラマの1人を知りませんでした。
あなたは橋の上にいて、手すりに向かって少し進んでいることに気付き、左に曲がります。
def turn_left
self.turn left:
end
おっとっと!それはおそらくNot Good™です。幸いなことに、RubyはSyntaxError
を発生させます。
車はすぐに停止するはずですよね?
いや。
begin
#...
eval self.steering_wheel
#...
rescue Exception => e
self.beep
self.log "Caught #{e}.", :warn
self.log "Logged Error - Continuing Process.", :info
end
ビープ音
警告:SyntaxError例外をキャッチしました。
Info:ログに記録されたエラー-継続プロセス。
何かがおかしいことに気づき、緊急休憩をたたく(^C
:Interrupt
)
ビープ音
警告:割り込み例外をキャッチしました。
Info:ログに記録されたエラー-継続プロセス。
ええ-それはあまり役に立ちませんでした。レールにかなり近いので、車を駐車します(kill
ing:SignalException
)。
ビープ音
警告:SignalException例外をキャッチしました。
Info:ログに記録されたエラー-継続プロセス。
最後の瞬間に、キー(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
キーワードは、何があってもコードを実行します-例外がスローされた場合、そうでない場合、唯一の例外はワールドが終了した場合(またはその他のありそうもないイベント)です。
begin
# do driving stuff
ensure
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
end
ブーム!とにかくそのコードは実行されるはずです。 rescue Exception => e
を使用する唯一の理由は、例外へのアクセスが必要な場合、または例外に対してのみコードを実行する場合です。そして、エラーを再発生することを忘れないでください。毎回。
注:@Niallが指摘したように、alwaysが実行されることを確認してください。問題が発生した場合でも、プログラムがあなたに嘘をつき、例外をスローしないことがあるため、これは良いことです。エアバッグの膨張などの重要なタスクでは、何が起きてもそれを確実に行う必要があります。このため、車が停止するたびに、例外がスローされるかどうかを確認することをお勧めします。エアバッグの膨張は、ほとんどのプログラミングコンテキストでは一般的ではないタスクですが、実際にはほとんどのクリーンアップタスクではかなり一般的です。
rescue Exception => e
(および例外を再発生させない)-またはmight橋を運転しないでください。
これはすべての例外を捉えるためです。あなたのプログラムが any から回復できる可能性は低いです。
回復方法を知っている例外だけを処理する必要があります。特定の種類の例外を予期していない場合は、それを処理しないで、大音量でクラッシュし(ログに詳細を書き込み)、ログを診断してコードを修正します。
例外を飲み込むのは悪いことです、しないでください。
これは、 any 例外をキャッチしてはいけないという特別なケースです。処理方法がわからない場合は、システムの他の部分で処理して処理することをお勧めします。