ログをもう少し詳しくしたいなぁと思って調べていて、Error.pmのソースで気がついたんけど、
eval {
die Foo->new;
}
warn ref $@; #Foo
が使えるんですね。
Sledge::Plugin::DebugScreenとか、CGI::Applicationだと$self->{__stacktrace}で情報を保存しているけど、$@を利用してstacktrace情報を受け渡すことができるよな、と考えたので書いてみた
package MyApp::Exception;
use strict;
use warnings;
use base qw/Class::Accessor::Fast Exporter/;
use Devel::StackTrace;
use overload
'""' => 'as_string';
__PACKAGE__->mk_accessors(qw/frames message debug/);
our @EXPORT_OK = qw/throw/;
our $IGNOREPKG = [];
sub throw {
my $exception = __PACKAGE__->new(@_);
die $exception;
}
sub new {
my $class = shift;
my $message = join '', @_;
$message =~ s/[\n\r]*$//gi;
my $self = bless {
frames => [],
message => $message,
}, $class;
my $stack = Devel::StackTrace->new(
ignore_package => $IGNOREPKG,
no_refs => 1,
respect_overload => 1,
);
$self->frames([$stack->frames]);
$self;
}
sub as_string {
my $self = shift;
unless ( grep { $_->package eq 'MyApp::Framework' } @{$self->frames} ) {#frameworkとの連携
return $self->stacktrace;
}
my $caller = $self->frames->[0];
sprintf("%s at %s line %s\n", $self->message, $caller->filename, $caller->line);
}
sub stacktrace {
my $self = shift;
my @trace;
foreach my $frame ( @{$self->frames} ) {
push @trace,
sprintf( "%s line %s",
$frame->filename, $frame->line );
}
sprintf( "%s at %s\n", $self->message, join( ", ", @trace ) );
}
1;
基本、dieの代わりにthrowを使うようにする。そうすっとSTDERRにstacktrace付きのメッセージがでる。
#!/usr/bin/perl
use MyApp::Exception qw/throw/;
DBI->connect(...) or throw 'connection failed';#stacktraceが付く
フレームワークを離礁しているところでは、フレームワークで、
package MyApp::Framework;
sub run {
....
eval {
local $SIG{__DIE__} = MyApp::Exeption->can('throw');
$self->prepare;
$self->execute;
$self->finilize;
};
if ( $@ && ref $@ && $self->debug) {
$@->framesをtemplateに渡してdebugscreenなど
}
else {
die $@;
}
}
と書いてあるので、普通にdieした時もtraceするし、throwを使っていてもいい具合に出力を調整することができる。
ただ、車輪の再発明をした気がすごくするなぁ。。