Tripletail
view release on metacpan or search on metacpan
lib/Tripletail/Error.pm view on Meta::CPAN
# -----------------------------------------------------------------------------
# Tripletail::Error - å
é¨ã¯ã©ã¹
# -----------------------------------------------------------------------------
package Tripletail::Error;
use strict;
use warnings;
use Data::Dumper;
#use Smart::Comments;
use Tripletail;
use overload
'""' => \&_stringify,
fallback => 1;
sub _POST_REQUEST_HOOK_PRIORITY() { 2_000_000_000 } # Debug ãããå¾
my $PADWALKER_AVAILABLE; # PadWalker ãå©ç¨å¯è½ã§ãããã©ãããundef / 1 / 0
my $VARIABLE_LENGTH_LIMIT = 32 * 1024; # 1夿°ãããã®è¡¨ç¤ºããæå¤§é· (ãã¤ã)
my $DEFAULT_ERROR_TEMPLATE = &__load_default_error_template();
my $TRACE_ALLOWANCE_OF_CURRENT_REQUEST;
# æå¾ã«çºçãã DB ã®ã¨ã©ã¼ãå
容ã¯ä»»æã®ããã·ã¥ã
our $LAST_DB_ERROR;
1;
# -----------------------------------------------------------------------------
# $TL->newError($type, $msg);
# $TL->newError($type, $msg, $title);
#
sub _new {
# ã¹ã¿ãã¯ãã¬ã¼ã¹ãæã£ãä¾å¤ãªãã¸ã§ã¯ããçæããã
# è¿ãããã¤ã³ã¹ã¿ã³ã¹ã¯ "" æ¼ç®åã«ãã£ã¦æåååãå¯è½ã§ããã
my $class = shift;
my $type = shift; # 'error' / 'warn' / 'file-update' / 'memory-leak'
my $msg = shift; # $@
my $title = shift; # ä»»æã®æåå
my $this = bless {} => $class;
$this->{message} = $msg;
$this->{type} = $type;
$this->{title} = $title || "Error: $msg";
$this->{frames} = []; # Tripletail::Error::Frame
$this->{source} = {}; # ãã¡ã¤ã«ãã¹ => ä¸èº«
$this->{show_trace} = undef;
$this->{show_vars} = undef;
$this->{show_src} = undef;
$this->{suppress_internal} = 1;
$this->{appear} = 'sudden'; # sudden/usertrap
$this->{on_require} = undef; # undef/1.
$this->{http_status_code} = undef;
$this->{http_status_line} = undef;
$this->{db_error} = undef;
if( $msg =~ /: we are getting too large (file|request) which exceeds the limit. |: Post Error: request size was too big to accept. / )
{
$this->{http_status_code} = 413;
$this->{http_status_line} = "413 Request Entity Too Large";
}else
{
$this->{http_status_code} = 500;
$this->{http_status_line} = "500 Internal Server Error";
}
my $switch = $TL->INI->get(TL => 'stacktrace', 'onlystack');
if ($switch eq 'none') {
# skip
}
elsif ($switch eq 'onlystack') {
$this->{show_trace} = 1;
}
elsif ($switch eq 'full') {
$this->{show_trace} = 1;
$this->{show_vars} = 1;
$this->{show_src} = 1;
}
else {
die "Unknown stacktrace type: $switch (stacktraceã®æå®ã䏿£ã§ã)";
}
if ($this->{show_trace} and not $this->is_trace_allowed) {
$this->{show_trace} = undef;
}
if ($this->{show_trace}) {
# TLã®dieãã³ãã©ããå¼ã°ããããç¥ããªãã®ã§ãç¡éå帰ãé²ãã
local $SIG{__DIE__} = 'DEFAULT';
local($@);
eval {
$this->_fetch_frames;
};
if ($@) {
print STDERR $@;
exit 1;
}
}
if (our $LAST_DB_ERROR) {
$this->{db_error} = $LAST_DB_ERROR->force;
}
$TL->setHook(
'postRequest',
_POST_REQUEST_HOOK_PRIORITY,
sub {
$TRACE_ALLOWANCE_OF_CURRENT_REQUEST = undef;
});
$this;
}
sub type {
shift->{type};
}
sub title {
shift->{title};
}
sub message {
my $this = shift;
my $new = shift;
if ($new) {
$this->{message} = $new;
}
$this->{message};
}
sub _fetch_frames {
my $this = shift;
if (not defined $PADWALKER_AVAILABLE) {
eval {
require PadWalker;
};
$PADWALKER_AVAILABLE = ($@ ? 0 : 1);
}
my $found_die_handler;
my $level = 0;
my $pad_level = 0;
$this->{appear} = 'sudden'; # sudden/usertrap
for (my $i = 0; my @c = caller $i; $i++) {
my ($package, $filename, $line, $sub, $hasargs,
$wantarray, $evaltext, $is_require, $hints, $bitmask) = @c;
if ($sub =~ /^Tripletail::__die_handler_for_(localeval|startup)$/) {
$sub = 'Tripletail::((die handler))';
$found_die_handler = 1;
}
elsif ($sub eq '(eval)') {
if ($is_require) {
$sub = "((require/use $package))";
if( $this->{appear} eq 'sudden' )
{
$this->{on_require} = 1;
}
}
else {
if( $this->{appear} eq 'sudden' && $package!~/^Tripletail\b/ )
{
$this->{appear} = 'usertrap';
}
if (defined $evaltext) {
$evaltext =~ s!\s*|\s*!!g;
if (length($evaltext) > 30) {
substr($evaltext, 27) = '...';
}
$sub = sprintf '((eval "%s"))', $evaltext;
}
else {
$sub = '((eval))';
}
}
$sub = $package . '::' . $sub;
}
if ($hasargs) {
$pad_level++;
}
else {
next; # 颿°å¼åºãã®ã¿èæ
®ãevalã§ä½ããããã¬ã¼ã ã¯é£ã°ãã
# peek_my/peek_our ã§ã eval ã®ãã¬ã¼ã ã¯é£ã°ãããã
# (pod ã«ã¯åã弿°ã caller ã¨åãã ã¨æ¸ãã¦ãããã©åâ¦)
}
$this->{suppress_internal} and not $found_die_handler
and next; # ã¾ã die ãã³ãã©ãè¦ãã¦ããªã
my $frame = Tripletail::Error::Frame->new(
$level++, $filename, $line, $sub);
if ($this->{show_vars} and $PADWALKER_AVAILABLE) {
# ãã¼ã«ã«å¤æ°ãåå¾
my $mines = PadWalker::peek_my($pad_level);
my $ours = PadWalker::peek_our($pad_level);
while (my ($name, $ref) = each %$mines) {
$frame->set_variable("my $name", $ref);
}
while (my ($name, $ref) = each %$ours) {
$frame->set_variable("our $name", $ref);
}
#my @args;
#do {
# package DB;
# @c = caller $i + 1;
# @args = @DB::args;
#};
#$frame->set_variable('@_', \@args);
}
if ($this->{show_src}) {
# ã½ã¼ã¹ã³ã¼ããåå¾
if (not exists $this->{source}{$filename}) {
my $src;
if (-r $filename) {
$src = $TL->readTextFile($filename);
}
$this->{source}{$filename} = $src;
}
}
push @{$this->{frames}}, $frame;
}
}
sub is_trace_allowed {
my $this = shift;
if (defined(my $ret = $TRACE_ALLOWANCE_OF_CURRENT_REQUEST)) {
$ret;
}
else {
my $ret;
my $masks = $TL->INI->get(TL => stackallow => '');
if (my $remote = $ENV{REMOTE_ADDR}) {
if($TL->newValue->set($remote)->isIpAddress($masks)) {
# ããããã
$TL->log(__PACKAGE__,
"[$remote] matched to [$masks]. stack trace is allowed");
$ret = 1;
}
else {
# ã©ãã«ããããããªãã£ãã
$TL->log(
__PACKAGE__, sprintf(
"[%s] didn't match to any of [%s]. stack trace is not allowed",
$remote, $masks));
$ret = 0;
lib/Tripletail/Error.pm view on Meta::CPAN
);
}
# ã½ã¼ã¹
foreach my $fpath (keys %{$this->{source}}) {
$this->_foreach_source_line(
$fpath, sub {
my ($linenum, $src) = @_;
$src = $TL->escapeJs($src);
$src =~ s!</script>!</sc"+"ript>!i;
$t->node('scripts')->node('js-src')->node('line')->setAttr(
LINE => 'raw',
);
$t->node('scripts')->node('js-src')->node('line')->add(
LINE => $src,
);
});
$t->node('scripts')->node('js-src')->setAttr(
FILE => 'js',
);
$t->node('scripts')->node('js-src')->add(
FILE => $fpath,
);
}
my $frame = $this->{frames}[$default_level];
# ããã©ã«ãã§è¡¨ç¤ºããã夿°ã¯ Lv. 0 ã®å¤æ°ã§ããã表示ãããã½ã¼ã¹
# 㯠Lv. 0 ã®ã½ã¼ã¹ã§ãããããã¯å¾ã§ JavaScript ã«ãã£ã¦æ¸ãæãã
# ããå¯è½æ§ãããã
if (not $this->{show_vars}) {
$t->node('detail')->node('vars-unavail')->add(
REASON => 'iniãã¡ã¤ã«ã[TL]ã°ã«ã¼ãã® "stacktrace" ã®è¨å®å¤ã'.
' "full" ã«ãªã£ã¦ãã¾ããã');
}
elsif (not $frame) {
$t->node('detail')->node('vars-unavail')->setAttr(
REASON => 'raw',
);
$t->node('detail')->node('vars-unavail')->add(
REASON =>
q{ã¹ã¿ãã¯ãã¬ã¼ã¹ãåå¾ã§ãã¾ããã§ããã} .
q{$SIG{__DIE__} ãã³ãã©ãç½®ãæããããç¶æ
ã§ã¨ã©ã¼ãçºçããå¯è½æ§ãããã¾ãã<br />} .
q{ã¨ã©ã¼å
容ãåæã«æ¸ãæããããã®ãé²ããªã©ã®çç±ã§ä¸æçã« $SIG{__DIE__} } .
q{ãã³ãã©ãç½®ãæããéã«ã¯ã次ã®ããã«ãã¦ãçºçããã¨ã©ã¼ãå度 die ãã¦ä¸ããã<br /><br />} .
q[<pre>eval {] . "\n" .
q[ $SIG{__DIE__} = 'DEFAULT';] . "\n" .
q[ # ã¨ã©ã¼ãçºçããå¦ç] . "\n" .
q[};] . "\n" .
q[if ($@) {] . "\n" .
q[ die $@; # å度ã¨ã©ã¼ãçºçããã] . "\n" .
q[}</pre>],
);
}
elsif (not $PADWALKER_AVAILABLE) {
$t->node('detail')->node('vars-unavail')->setAttr(
REASON => 'raw',
);
$t->node('detail')->node('vars-unavail')->add(
REASON => '<a href="http://search.cpan.org/~robin/PadWalker/">PadWalker</a> ãå©ç¨ä¸å¯è½ã§ãã');
}
else {
foreach my $name (sort {$a cmp $b} keys %{$frame->vars_shallow}) {
$t->node('detail')->node('vars-avail')->node('var')->add(
NAME => $name,
VALUE => $frame->vars_shallow->{$name},
);
}
$t->node('detail')->node('vars-avail')->add;
}
if (not $this->{show_src}) {
$t->node('detail')->node('src-unavail')->add(
REASON => 'iniãã¡ã¤ã«ã[TL]ã°ã«ã¼ãã® "stacktrace" ã®è¨å®å¤ã'.
' "full" ã«ãªã£ã¦ãã¾ããã');
}
elsif (not $frame) {
$t->node('detail')->node('src-unavail')->add(
REASON =>
q{ã¹ã¿ãã¯ãã¬ã¼ã¹ãåå¾ã§ãã¾ããã§ããã}
);
}
elsif (not defined $this->{source}{$frame->fpath}) {
$t->node('detail')->node('src-unavail')->add(
REASON => 'ã½ã¼ã¹ãã¡ã¤ã« "%s" ãèªã¿è¾¼ãäºãåºæ¥ã¾ããã');
}
else {
$this->_foreach_source_line(
$frame, sub {
my ($linenum, $src) = @_;
$t->node('detail')->node('src-avail')->node('line')->node(
$frame->line == $linenum ? 'caller-line' : 'other-line')->add(
SOURCE => $src,
LINE_NUM => $linenum,
);
$t->node('detail')->node('src-avail')->node('line')->add;
});
$t->node('detail')->node('src-avail')->add;
}
if ($frame) {
$t->node('scripts')->add({
SELECTED_LV => $frame->level,
LAST_HILITED => $frame->line,
});
$t->expand(
SELECTED_LV => $frame->level,
LAST_HILITED => $frame->line,
);
}
else {
$t->node('scripts')->add({
SELECTED_LV => 0,
LAST_HILITED => 0,
});
$t->expand(
SELECTED_LV => 0,
LAST_HILITED => 0,
);
( run in 1.475 second using v1.01-cache-2.11-cpan-39bf76dae61 )