Perl: Force stacktrace for “can't call method on undefined”

ぃ、小莉子 提交于 2019-12-10 21:35:33

问题


I'm using Perl and Catalyst as Web-Framework.

How can I globally force a stacktrace if an exception like Can't call method "XXX" on an undefined value... is thrown?

Suppose the following code in your Controller/Root.pm

use DateTime;

sub test  :Local :Args(0) {
    my ( $self, $c ) = @_;
    my $now = DateTime->now(time_zone=>'local');
    my $tmp = undef;

    my $throwing = $tmp - $now; #this will throw an exception!
    $c->res->body("OK");
}

Opening http://localhost:3000/test throws of course the exception:

Caught exception in MyApp::Controller::Root->test "Can't call method "subtract_datetime" on an undefined value at /usr/local/lib/perl/5.10.1/DateTime.pm line 1619, line 1003."

The stacktrace is missing!

How can I force in such a case a stacktrace?

I.e. I would like to get (not a real output, only composed by hand):

MyApp::Controller::Root::__ANON__('Can\'t call method "subtract_datetime" on an undefined value at /usr/local/lib/perl/5.10.1/DateTime.pm line 1619, <DATA> line 1003.^J') called at /home/user/MyApp/script/../lib/MyApp/Controller/Root.pm line 114
MyApp::Controller::Root::test('MyApp::Controller::Root=HASH(0x20583b60)', 'MyApp=HASH(0x206e9140)') called at /usr/local/share/perl/5.10.1/Catalyst/Action.pm line 65
Catalyst::Action::execute('Catalyst::Action=HASH(0x20668e88)', 'MyApp::Controller::Root=HASH(0x20583b60)', 'MyApp=HASH(0x206e9140)') called at /usr/local/share/perl/5.10.1/Catalyst.pm line 1687
eval {...} at /usr/local/share/perl/5.10.1/Catalyst.pm line 1687
Catalyst::execute('MyApp=HASH(0x206e9140)', 'MyApp::Controller::Root', 'Catalyst::Action=HASH(0x20668e88)') called at /usr/local/share/perl/5.10.1/Catalyst/Action.pm line 60
[ and the rest of the exception ...]

With this I know exactly that the exception was caused by the line 114 in Root.pm


回答1:


I am not sure if it works within Catalyst, but usually you can force a stacktrace by including the CPAN module Carp::Always, e.g. by writing somewhere in your program use Carp::Always; or starting the perl script with the -MCarp::Always switch.




回答2:


One solution is to replace the default signal handlers for __DIE__ and __WARN__:

use Carp;
$SIG{__DIE__} = sub { die Carp::longmess @_ };
$SIG{__WARN__} = sub { warn Carp::longmess @_ };

This is essentially what Carp::Always does for you (but it is more thorough and safer).

If you only care about this in one section of your code, then use dynamic scoping to limit the effect:

#!/usr/bin/perl

use strict;
use warnings;

use Carp;

warn "not affected 1";

foo();
bar("outside");

warn "not affected 2";

sub foo {
    local $SIG{__WARN__} = sub { warn Carp::longmess @_ };
    warn "in foo";
    bar("inside");
}

sub bar {
    warn @_;
}

output:

not affected 1 at t.pl line 8.
in foo at t.pl line 17.
 at t.pl line 17
    main::foo() called at t.pl line 10
inside at t.pl line 22.
 at t.pl line 22
    main::bar('inside') called at t.pl line 18
    main::foo() called at t.pl line 10
outside at t.pl line 22.
not affected 2 at t.pl line 13.


来源:https://stackoverflow.com/questions/17659663/perl-force-stacktrace-for-cant-call-method-on-undefined

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!