如何为模处理人员提供更多上下文?

时间:2019-11-17 23:23:16

标签: perl exception

在某些功能必须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访问它?还是可以事先向处理程序提供一些数据?

我现在看到两个选项:

  • 通过一些全局变量,如果有可能出现一些错误,我总是事先填充所需的数据
  • 使用带有die_handler方法的对象

更好的方法呢?

1 个答案:

答案 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死,具有完整的堆栈回溯,但是在您的示例中,dieeval捕获。通过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中的一个示例。