问题
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