在某些功能必须die
的情况下,我想在错误消息中包含更多上下文。在某些情况下,我可以提供它,而在另一些情况下,我找不到好的方法。例如,将RaiseError
选项与Postgres结合使用时,每次失败都会导致异常,但是我无法为这些错误提供更多的上下文信息。我曾尝试使用die
处理程序,但至少无法找出一种合理的方法来包含被调用子例程的参数:
try {
x( 'y' );
} catch {
say "CATCHED error: $_";
};
sub _die_handler {
my @caller = caller(1);
say "@caller"; # how to access @_ for this frame
die @_;
}
sub x {
local $SIG{__DIE__} = \&_die_handler;
die;
}
在上面的示例中,我怎么看到x
是用'y'作为参数调用的?
如果我尚未在子例程中修改@_
,是否可以从_die_handler
访问它?还是可以事先向处理程序提供一些数据?
我现在看到两个选项:
更好的方法呢?
答案 0 :(得分:6)
我不确定您想要什么“上下文”,但是Carp是我们的朋友。
其中之一,通过包含use Carp::Always;
,我们获得了在所有错误上打印的完整堆栈回溯记录。
如果您愿意选择,最简单的方法是直接使用合适的Carp
例程
use warnings;
use strict;
use feature 'say';
use Carp;
eval { my $y = 10; x($y) };
if ($@) {
print "eval: $@";
# print/log else as wanted, recover and proceed or exit
}
say "done";
sub x {
local $SIG{__DIE__} = \&Carp::confess;
# ...
my $bad_math = $_[0] / 0;
}
Carp::confess
死,具有完整的堆栈回溯,但是在您的示例中,die
被eval
捕获。通过confess
消亡并捕获异常,您可以从confess
获得“调用上下文”,而且可以保留控制权,可以按需在eval
“捕获”中进行操作。
此打印
eval: Illegal division by zero at error_context.pl line 18. at error_context.pl line 18. main::x(10) called at error_context.pl line 7 eval {...} called at error_context.pl line 7 done
在没有eval
的情况下,程序将终止(除非堆栈中进一步有eval
),但我们仍将获得调用的完整回溯。 Carp
中有一些例程不会消失,其中cluck
还会显示回溯。
要进行更多自定义处理,请使用$SIG{__DIE__}
挂钩。 Carp
的一个小缺点是,具有回溯功能的例程不会死,cluck
只会打印到STDERR
流中;我们无法轻易获得该消息以进一步构建它。跟踪曾经在longmess
中,但现在已经不存在了,我认为没有Carp
就能从die
获取堆栈跟踪。
然后使用confess
,它返回跟踪,并用eval
包裹呼叫
sub _die_handler {
my $other_info = '...';
Carp::confess($other_info . "\n" . $_[0]);
}
...
sub x {
local $SIG{__DIE__} = \&_die_handler;
...
}
...
eval { x() };
if ($@) { ... } #--> confess's trace with $other_info prepended
因此,当您处理其die
时,整个消息就会放在$@
中的eval
中。为此,您仍然需要eval
。
如果您希望能够完全处理异常,请参见Devel::StackTrace
use Devel::StackTrace;
sub _die_handler {
my $trace = Devel::StackTrace->new;
# Add other info to `$trace` object, or build error object/structure
# Print/log/etc $trace (or your new error structure/object), or
#die $trace;
}
如果您想重新抛出Devel::StackTrace
,当然也很有用,在这种情况下,您可以将其对象传递给die
。请参阅文档,尤其是构造函数选项。
一般警告:小心$SIG{__DIE__}
;这可能很棘手。我想说的是,最好使用Carp
。
最后,如果通过“ 上下文”表示从调用堆栈中获取更多详细信息,则可以使用caller
手动遍历堆栈,并通过{{1}从每个帧中检索词法}。 this post中的一个示例。