Answers:
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
。通常,您应该指定比default更为具体的内容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
Throwable
Java一样
ADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
然后rescue *ADAPTER_ERRORS => e
在真正的规则是:不要扔掉异常。您的报价作者的客观性值得怀疑,事实证明是这样的事实:
不然我会刺你
当然,请注意,信号(默认情况下)会引发异常,并且通常长时间运行的进程会通过信号终止,因此捕获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!没有人这样做!),但是出于某种原因,您可能希望将其保留一段时间,但不要连续运行调试器。
注意:
如果您已经运行了捕获信号异常并忽略它们的其他人的程序,(例如上面的代码),那么:
pgrep ruby
或ps | grep ruby
,以查找有问题的程序的PID,然后运行kill -9 <PID>
。 如果您正在使用别人的程序,无论出于何种原因,这些程序都充斥着这些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
无论是否引发异常,都会被调用。
kill -9
。
ensure
无论是否引发异常,an 都会运行,而rescue
仅在引发异常时才会运行will。
请勿rescue Exception => e
(也不要重新提出例外)-否则您可能会驶离桥梁。
假设您正在开车(正在运行Ruby)。您最近在空中升级系统上安装了一个新的方向盘(使用eval
),但您不知道其中一位程序员对语法感到困惑。
您在桥上,意识到自己要驶向栏杆,所以您向左转。
def turn_left
self.turn left:
end
哎呀!幸运的是,这可能不是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异常。
信息:记录的错误-继续过程。
您会发现什么是错的,你猛踩紧急中断(^C
:Interrupt
)
哔哔
警告:捕获到中断异常。
信息:记录的错误-继续过程。
是的-没什么帮助。您非常靠近铁路,因此您将汽车停放在了公园(kill
ing:)SignalException
。
哔哔
警告:捕获到SignalException异常。
信息:记录的错误-继续过程。
在最后一秒钟,您拉出(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之类的语言添加救援很容易被遗忘,并且在重新提出问题之前立即发布救援声明会感到有些不愉快。而你不想要忘记的raise
声明。如果这样做,祝您找到这个错误,祝您好运。
值得庆幸的是,Ruby非常棒,您只需使用ensure
关键字即可确保代码能够运行。该ensure
关键字将不管运行代码-如果一个异常被抛出,如果不是,唯一的例外是,如果世界结束(或其他不可能事件)。
begin
# do driving stuff
ensure
self.airbags.inflate if self.exceeding_safe_stopping_momentum?
end
繁荣!而且该代码无论如何都应该运行。您应该使用的唯一原因rescue Exception => e
是,如果您需要访问异常,或者仅希望代码在异常上运行。并记得重新提出该错误。每次。
注意:正如@Niall指出的那样,请确保始终运行。这是件好事,因为有时即使您遇到问题,您的程序也可以骗您,并且不会抛出异常。对于关键任务,例如给安全气囊充气,您需要确保无论发生什么事情都可以发生。因此,每次停车时检查是否引发异常都是一个好主意。尽管在大多数编程情况下,安全气囊的充气并不常见,但实际上在大多数清理任务中还是很常见的。
ensure
的替代部分rescue Exception
具有误导性-该示例暗示它们是等效的,但是ensure
如上所述,无论是否存在异常都将发生,因此现在您的安全气囊将膨胀,因为即使没有发生任何问题,行驶速度超过5英里/小时。
这是该规则的一个特例,即您不应捕获任何您不知道如何处理的异常。如果您不知道如何处理,最好让系统的其他部分捕获并处理它。
我刚刚在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
我猜您真的不想吞下任何这些系统级异常。您只想捕获所有应用程序级错误。异常导致您的代码。
幸运的是,有一个简单的方法可以做到这一点。