不要挽救Exception。永远否则我会刺你。
为什么不呢?正确的做法是什么?
#1 楼
TL; DR:使用StandardError
代替常规异常捕获。重新引发原始异常时(例如,当救援仅记录异常时),救援Exception
可能还可以。Exception
是Ruby异常层次结构的根,所以当您rescue Exception
您可以从所有内容中进行救援,包括诸如SyntaxError
,LoadError
和Interrupt
之类的子类。救援
Interrupt
可以防止用户使用CTRLC退出程序。救援
SignalException
可以防止程序不能正确响应信号。除kill -9
之外,它将是不可杀死的。抢救
SyntaxError
意味着失败的eval
会无声地执行。通过运行此程序并尝试显示所有这些信息。到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
为从日志记录/报告目的而明智地从
Exception
救援的几种常见情况之一,在这种情况下,您应立即重新举起例外:begin
# iceberg?
rescue Exception => e
# do some logging
raise # not enough lifeboats ;)
end
评论
所以就像在Java中捕获Throwable
–棘轮怪胎
2012年4月7日,0:15
该建议对干净的Ruby环境很有用。但是不幸的是,许多宝石创造了直接源自Exception的异常。我们的环境中有30种: OpenID :: Server :: EncodingError,OAuth :: InvalidRequest,HTMLTokenizerSample。这些是您非常想在标准救援块中捕获的例外。不幸的是,Ruby中没有任何东西可以阻止甚至阻止宝石直接从Exception继承,甚至命名也不是很直观的。
–乔纳森·斯沃兹(Jonathan Swartz)
2013年9月19日17:08
@JonathanSwartz然后从那些特定的子类中抢救,而不是从Exception中抢救出来。越具体越好,总是越清晰越好。
–安德鲁·马歇尔(Andrew Marshall)
2013年9月19日19:15在
@JonathanSwartz-我会调试gem创造者,以更改其异常继承的内容。就个人而言,我希望我的宝石具有所有异常都来自MyGemException,因此,如果需要,您可以进行挽救。
–内森·朗(Nathan Long)
2014年1月31日22:19
您还可以ADAPTER_ERRORS = [:: ActiveRecord :: StatementInvalid,PGError,Mysql :: Error,Mysql2 :: Error,:: ActiveRecord :: JDBCError,SQLite3 :: Exception],然后营救* ADAPTER_ERRORS => e
– j_mcnally
2014-2-27在2:19
#2 楼
真正的规则是:不要丢弃异常。您的报价作者的客观性值得怀疑,事实以它结尾的是,否则我会刺你
当然,请注意,信号(默认情况下)会引发异常,并且通常长时间运行的进程会通过信号终止,因此捕获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在其请求处理程序中执行此操作。
主线程中引发了信号异常。后台线程不会获取它们,因此试图将它们捕获在那里是没有意义的。
这在生产环境中特别有用,在生产环境中,您不希望程序在发生任何事情时就简单地停止错误。然后,您可以将堆栈转储记录到日志中,并添加到代码中,以更优雅的方式在调用链的下方处理特定异常。
还要注意,还有另一个Ruby惯用语效果大致相同:
a = do_something rescue "something else"
在这一行中,如果
do_something
引发异常,它将被Ruby捕获并丢弃,并为a
分配"something else"
。通常,除了在不需要知道的特殊情况下,不要这样做。一个示例:
debugger rescue nil
debugger
函数是在代码中设置断点的一种不错的方法,但是如果在调试器和Rails之外运行,则会引发异常。从理论上讲,现在您不应该将调试代码留在程序中(pff!没人这样做!),但是出于某种原因,您可能希望将其保留一段时间,但又不想继续运行调试器。注意:
如果您运行了别人的程序来捕获信号异常并忽略了它们,(请说上面的代码)然后:
<在Linux的shell中,键入
pgrep ruby
或ps | grep ruby
,查找有问题的程序的PID,然后运行kill -9 <PID>
。 在Windows中,使用任务管理器(CTRL-SHIFT-ESC),转到“进程”选项卡,找到您的进程,右键单击它并选择“结束进程”。
如果您正在使用别人的程序,无论出于何种原因,这些程序都充斥着这些ignore-exception块,那么将其放在主行顶部是一种可能的解决方案:
%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
。评论
抱歉,这是错误的。服务器永远都不能挽救Exception,而只能对其进行记录。这将使其无法杀死,除非被杀死-9。
–约翰
2012年4月8日在1:37
注释3中的示例并不恰当,无论是否引发异常,都将确保运行,而救援仅在引发异常时运行。
–安德鲁·马歇尔(Andrew Marshall)
2012年12月8日,0:13
它们不是/ exactly /等价的,但是我无法弄清楚如何以一种不丑陋的方式简洁地表达对等物。
–迈克尔·斯莱德
13年1月31日下午5:32
在第一个示例中,只需在begin / rescue块之后添加另一个critical_cleanup调用即可。我同意的不是最优雅的代码,但是显然第二个示例是优雅的实现方式,因此,一点点优雅只是该示例的一部分。
–gtd
13年3月6日在12:58
“甚至不要运行它来查看它是否有效。”相反,我建议您运行它,看看它是否失败,并亲自了解如果失败了,而不是盲目地相信别人。无论如何,很好的答案:)
– huelbois
16年11月9日在15:07
#3 楼
TL; DR不要
rescue Exception => e
(并且不要重新引发异常)-否则您可能会开车过桥。假设您正在开车(正在运行Ruby)。您最近使用空中升级系统(使用
eval
)安装了一个新的方向盘,但您不知道其中一位程序员对语法感到迷惑。您正处在桥梁之中,意识到了正在向栏杆方向移动,所以您向左转。
def turn_left
self.turn left:
end
糟糕!幸运的是,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异常。
信息:记录的错误-继续进行过程。
您注意到出了点问题,并猛烈抨击紧急情况(
^C
:Interrupt
) />哔哔声警告:捕获到中断异常。
信息:已记录错误-继续执行过程。
是的-并没有太大帮助。您非常靠近铁路,因此将汽车停在了停车场(
kill
ing:SignalException
)。哔哔声
警告:捕获到SignalException异常。
信息:已记录错误-继续过程。
最后一秒钟,您拔出钥匙(
kill -9
),汽车停下来,您猛烈地撞向方向盘(安全气囊无法充气,因为您没有t正常停止该程序-您将其终止),然后汽车后部的计算机将其撞到其前面的座椅上。半满的可乐罐溢出了报纸。后面的杂物被压碎,大部分被蛋黄和牛奶覆盖。该车需要认真维修和清洁。 (数据丢失)希望您有保险(备份)。哦,是的-由于安全气囊没有膨胀,您可能受到了伤害(被开除等)。
但是请稍等!还有更多原因可能使您想使用
rescue Exception => e
!假设您是那辆车,并且要确保在汽车超过其安全停止动量时安全气囊会充气。
begin
# do driving stuff
rescue Exception => e
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
raise
end
这里是规则的例外:只有重新引发例外时,您才能捕获
Exception
。因此,更好的规则是永远不要吞咽Exception
,并且总是重新引发错误。但是在Ruby之类的语言中添加救援既容易忘记,又可以在重新提出问题之前放下救援声明有点不干。而且您也不想忘记
raise
语句。如果您愿意,可以尝试找到该错误,祝您好运。幸运的是,Ruby非常棒,您可以使用
ensure
关键字来确保代码能够运行。无论如何,关键字ensure
都将运行代码-如果引发异常,如果没有引发异常,则唯一的例外就是世界终结(或其他不太可能的事件)。 begin
# do driving stuff
ensure
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
end
!而且该代码无论如何都应该运行。应该使用
rescue Exception => e
的唯一原因是,如果您需要访问异常,或者仅希望代码在异常上运行。并记住要重新提出该错误。每次。注意:正如@Niall指出的那样,请确保始终运行。这是件好事,因为有时即使您遇到问题,您的程序也可能撒谎,不会抛出异常。对于重要的任务,例如给安全气囊充气,您需要确保无论发生什么事情都会发生。因此,每次停车时检查是否抛出异常都是一个好主意。尽管在大多数编程情况下,安全气囊的充气并不常见,但实际上在大多数清理任务中还是很常见。
评论
哈哈哈哈!这是一个很好的答案。我很震惊,没有人发表评论。您给出了一个清晰的场景,使整个事情真正变得可以理解。干杯! :-)
–詹姆斯·米兰尼(James Milani)
17年7月20日在19:10
@JamesMilani谢谢!
–本·奥宾
17年7月21日在21:14
+💯这个答案。希望我能投票一次以上! 😂
–engineerDave
17-10-5在16:58
喜欢你的答案!
– Atul Vaibhav
17年11月24日在7:28
这个答案是在完全可以理解和正确接受的答案之后的四年,并以一种荒谬的场景重新解释了这个场景,该场景设计得比现实更有趣。抱歉,成为嗡嗡声,但这不是Reddit-答案简洁,正确而不是有趣更重要。另外,关于确保作为挽救异常的替代方法的部分具有误导性-该示例暗示它们是等效的,但如上所述,确保无论是否存在异常都将发生,所以现在您的安全气囊会膨胀,因为即使超过5英里/小时,您的速度超过5英里/小时错误。
–尼尔
18年7月31日在12:37
#4 楼
因为这捕获了所有异常。您的程序不太可能从其中任何一个恢复。您应该只处理知道如何恢复的异常。如果您不希望发生某种异常,请不要处理它,大声崩溃(将详细信息写入日志),然后诊断日志并修复代码。
吞咽异常是不好的,不要做到这一点。
#5 楼
这是规则的一个特例,您不应捕获任何您不知道如何处理的异常。如果您不知道如何处理它,最好让系统的其他部分捕获并处理它。#6 楼
这篇博客文章对它进行了完美的解释:Ruby的Exception与StandardError:有什么区别?
为什么不应该拯救Exception
抢救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
我猜你真的不想吞下所有这些
系统级异常。您只想捕获所有
应用程序级错误。这些异常导致了您的代码。
幸运的是,有一种简便的方法可以解决此问题。 br />挽救StandardError代替
您应该关心的所有例外均从StandardError继承。这些是我们的老朋友: NoMethodError-当您尝试调用不存在的方法时引发
TypeError-由1 +“”
RuntimeError-谁会忘记旧的RuntimeError引起的事情?
要挽救此类错误,您将需要挽救StandardError。您可以通过编写以下代码来做到这一点:
begin
do_something()
rescue StandardError => e
# Only your app's exceptions are swallowed. Things like SyntaxErrror are left alone.
end
但是Ruby使它更易于使用。
当您完全不指定异常类时,ruby假定您的意思是StandardError。因此,下面的代码与上面的代码相同:
begin
do_something()
rescue => e
# This is the same as rescuing StandardError
end
评论
那你可能可以自己写? :)我对这里的暴力呼吁感到非常不自在。只是编程而已。
看看Ruby Exception中的这篇文章,它有一个很好的Ruby Exception层次结构。
因为瑞安·戴维斯(Ryan Davis)会刺你。孩子们永远不要挽救异常。
@DarthEgregious我真的无法分辨你是否在开玩笑。但我认为这很有趣。 (这显然不是严重的威胁)。现在,每当我想到捕获Exception时,我都会考虑是否值得在Internet上将某个随机的家伙刺伤。