Reworked error handling to propogate errors downward, especially when webgui.debug is set
This commit is contained in:
parent
b815228a1b
commit
e945a94c63
12 changed files with 132 additions and 110 deletions
|
|
@ -198,12 +198,11 @@ sub callMethod {
|
|||
}
|
||||
|
||||
#Try to call the method
|
||||
my $output = eval { $self->$method(@{$args}) };
|
||||
|
||||
#Croak on error
|
||||
if($@) {
|
||||
croak "Unable to run $method on $module: $@";
|
||||
return undef;
|
||||
my $output = eval { $self->$method(@{$args}); };
|
||||
if( $@ ) {
|
||||
my $e = WebGUI::Error->caught;
|
||||
$e->{message} = "Unable to run $method on $module: $e->{message}";
|
||||
$e->rethrow;
|
||||
}
|
||||
|
||||
#Return the output from the method call
|
||||
|
|
|
|||
|
|
@ -746,41 +746,51 @@ Any leftover part of the requested URL.
|
|||
sub dispatch {
|
||||
my ($self, $fragment) = @_;
|
||||
return undef if $fragment;
|
||||
|
||||
my $session = $self->session;
|
||||
my $state = $self->get('state');
|
||||
|
||||
##Only allow interaction with assets in certain states
|
||||
return if $state ne 'published' && $state ne 'archived' && !$session->isAdminOn;
|
||||
my $func = $session->form->param('func') || 'view';
|
||||
my $viewing = $func eq 'view' ? 1 : 0;
|
||||
my $sub = $self->can('www_'.$func);
|
||||
if (!$sub && $func ne 'view') {
|
||||
$sub = $self->can('www_view');
|
||||
$viewing = 1;
|
||||
|
||||
# needed for tests that call straight here but otherwise redundant with same in WebGUI.pm
|
||||
local $SIG{__DIE__} = sub { WebGUI::Error::RunTime->throw( message => $_[0] ); };
|
||||
|
||||
|
||||
for my $func ( $session->form->param('func'), 'view' ) {
|
||||
|
||||
# if there's no output from the user specified func, try view next
|
||||
|
||||
my $viewing = $func eq 'view' ? 1 : 0;
|
||||
my $sub = $self->can('www_'.$func);
|
||||
|
||||
if (!$sub && $func ne 'view') {
|
||||
$sub = $self->can('www_view');
|
||||
$viewing = 1;
|
||||
}
|
||||
|
||||
return undef unless $sub;
|
||||
|
||||
my $output = eval { $self->$sub(); };
|
||||
|
||||
if ( $@ ) {
|
||||
my $e = Exception::Class->caught();
|
||||
# previously, this only handled WebGUI::Error::ObjectNotFound::Template
|
||||
my $errstr = sprintf(
|
||||
"Couldn't call method ``%s'' on asset for url ``%s'': Error: ``%s''",
|
||||
"www_$func", $session->url->getRequestedUrl, $e->error,
|
||||
);
|
||||
$errstr .= " templateId: " . $e->templateId if $e->can('templateId') and $e->templateId;
|
||||
$errstr .= " assetId: " . $e->assetId if $e->can('assetId') and $e->assetId;
|
||||
$session->log->error($errstr);
|
||||
$e->rethrow if $session->request->env->{'webgui.debug'};
|
||||
}
|
||||
|
||||
return $output if $output || $viewing;
|
||||
|
||||
}
|
||||
return undef unless $sub;
|
||||
my $output = eval { $self->$sub(); };
|
||||
if (my $e = Exception::Class->caught('WebGUI::Error::ObjectNotFound::Template')) {
|
||||
#WebGUI::Error::ObjectNotFound::Template
|
||||
$session->log->error(sprintf "%s templateId: %s assetId: %s", $e->error, $e->templateId, $e->assetId);
|
||||
}
|
||||
elsif ($@) {
|
||||
my $message = $@;
|
||||
$session->log->error("Couldn't call method www_".$func." on asset for url: ".$session->url->getRequestedUrl." Root cause: ".$message);
|
||||
}
|
||||
return $output if $output || $viewing;
|
||||
##No output, try the view method instead
|
||||
$output = eval { $self->www_view };
|
||||
if (my $e = Exception::Class->caught('WebGUI::Error::ObjectNotFound::Template')) {
|
||||
$session->log->error(sprintf "%s templateId: %s assetId: %s", $e->error, $e->templateId, $e->assetId);
|
||||
return "chunked";
|
||||
}
|
||||
elsif ($@) {
|
||||
warn "logged another warn: $@";
|
||||
my $message = $@;
|
||||
$session->log->warn("Couldn't call method www_view on asset for url: ".$session->url->getRequestedUrl." Root cause: ".$@);
|
||||
return "chunked";
|
||||
}
|
||||
return $output;
|
||||
|
||||
return ''; # not reached
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -470,7 +470,7 @@ sub graph {
|
|||
|
||||
my $session = $self->session;
|
||||
|
||||
eval { local $SIG{'__DIE__'}; require GraphViz };
|
||||
eval { require GraphViz };
|
||||
if ($@) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -710,7 +710,7 @@ sub www_graph {
|
|||
|
||||
my $i18n = WebGUI::International->new($session, "Asset_Survey");
|
||||
|
||||
eval { local $SIG{'__DIE__'}; require GraphViz };
|
||||
eval { require GraphViz };
|
||||
if ($@) {
|
||||
return '<h1>' . $i18n->get('survey visualization') . '</h1>Survey Visualization requires the GraphViz module';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,15 +79,21 @@ sub dispatch {
|
|||
$fragment =~ s/$url//;
|
||||
$session->asset($asset);
|
||||
my $output = eval { $asset->dispatch($fragment); };
|
||||
if ( $@ ) {
|
||||
$session->log->error( "Problem with dispatching $url: " . $@ );
|
||||
if( $@ ) {
|
||||
my $e = WebGUI::Error->caught('WebGUI::Error');
|
||||
if( $session->request->env->{'webgui.debug'} ) {
|
||||
$e->rethrow;
|
||||
} else
|
||||
{
|
||||
$session->log->error( "Problem with dispatching $url: " . $e, $e );
|
||||
}
|
||||
}
|
||||
return $output if defined $output;
|
||||
}
|
||||
}
|
||||
$session->clearAsset;
|
||||
if ($session->isAdminOn) {
|
||||
my $asset = WebGUI::Asset->newByUrl($session, $session->url->getRefererUrl) || WebGUI::Asset->getDefault($session);
|
||||
my $asset = eval { WebGUI::Asset->newByUrl($session, $session->url->getRefererUrl) } || WebGUI::Asset->getDefault($session);
|
||||
return $asset->addMissing($assetUrl);
|
||||
}
|
||||
return undef;
|
||||
|
|
|
|||
|
|
@ -205,75 +205,75 @@ use Exception::Class (
|
|||
|
||||
'WebGUI::Error' => {
|
||||
description => "A general error occured.",
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::OverrideMe' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => 'This method should be overridden by subclasses.',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::MethodNotFound' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => q|Called a method that doesn't exist.|,
|
||||
fields => 'method'
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::InvalidObject' => {
|
||||
isa => 'WebGUI::Error::InvalidParam',
|
||||
description => "Expected to get a reference to an object type that wasn't gotten.",
|
||||
fields => ["expected","got"],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::InvalidParam' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => "Expected to get a param we didn't get.",
|
||||
fields => ["param"],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::Compile' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => "Unable to compile the requested class",
|
||||
fields => ["class", "cause"],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::ObjectNotFound' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => "The object you were trying to retrieve does not exist.",
|
||||
fields => ["id"],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::ObjectNotFound::Template' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => "The template an asset was trying to retrieve does not exist.",
|
||||
fields => [qw/templateId assetId/],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::InvalidFile' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => "The file you have provided has errors.",
|
||||
fields => [qw{ brokenFile brokenLine }],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::Template' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => "A template has errors that prevent it from being processed.",
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
'WebGUI::Error::Connection' => {
|
||||
'WebGUI::Error::Connection' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => "Couldn't establish a connection.",
|
||||
fields => [qw{ resource }],
|
||||
},
|
||||
},
|
||||
|
||||
'WebGUI::Error::Fatal' => {
|
||||
isa => 'WebGUI::Error',
|
||||
|
|
@ -290,6 +290,11 @@ use Exception::Class (
|
|||
description => 'A module was requested that does not exist in the configuration file.',
|
||||
fields => [qw{ module configKey }],
|
||||
},
|
||||
|
||||
'WebGUI::Error::RunTime' => {
|
||||
isa => 'WebGUI::Error',
|
||||
description => 'Perl runtime error.',
|
||||
},
|
||||
);
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use strict;
|
|||
use WebGUI::BestPractices;
|
||||
use Moose;
|
||||
use MooseX::Storage;
|
||||
use WebGUI::Exception;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package WebGUI::FormBuilder::Role::HasFields;
|
|||
|
||||
use strict;
|
||||
use Moose::Role;
|
||||
use Try::Tiny;
|
||||
use WebGUI::Exception;
|
||||
use Carp qw(confess);
|
||||
|
||||
requires 'session', 'pack', 'unpack';
|
||||
|
|
@ -56,11 +56,13 @@ sub addField {
|
|||
# Load the class
|
||||
# Try to load the WebGUI Field first in case we conveniently overlap with a common name
|
||||
# (like Readonly)
|
||||
if ( $INC{'WebGUI/Form/'. ucfirst $file} || try { local $SIG{'__DIE__'}; require 'WebGUI/Form/' . ucfirst $file } ) {
|
||||
if ( $INC{'WebGUI/Form/'. ucfirst $file} || eval { require 'WebGUI/Form/' . ucfirst $file } ) {
|
||||
$type = 'WebGUI::Form::' . ucfirst $type;
|
||||
}
|
||||
elsif ( !$INC{$file} && !try { require $file; } ) {
|
||||
confess sprintf "Could not load form control class %s", $type;
|
||||
elsif ( !$INC{$file} && ! eval { require $file; } ) {
|
||||
my $e = WebGUI::Error->caught;
|
||||
$e->{message} = "Could not load form control class $type";
|
||||
$e->rethrow;
|
||||
}
|
||||
$field = $type->new( $self->session, { @properties } );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,13 +47,8 @@ sub call {
|
|||
$app = Plack::Middleware::SimpleLogger->wrap( $app );
|
||||
}
|
||||
|
||||
my $session = try {
|
||||
$env->{'webgui.session'} = WebGUI::Session->open( $config, $env );
|
||||
} catch {
|
||||
# We don't have a logger object, so for now just warn() the error
|
||||
warn "Unable to instantiate WebGUI::Session - $_";
|
||||
return; # make sure $session assignment is undef
|
||||
};
|
||||
my $session = $env->{'webgui.session'} = WebGUI::Session->open( $config, $env ) or
|
||||
die "Unable to instantiate WebGUI::Session - $_";
|
||||
|
||||
if ( !$session ) {
|
||||
|
||||
|
|
|
|||
|
|
@ -14,49 +14,37 @@ use WebGUI::Session::Log;
|
|||
|
||||
BEGIN {
|
||||
|
||||
our $StackTraceClass = "Devel::StackTrace";
|
||||
if (try { require Devel::StackTrace::WithLexicals; 1 }) {
|
||||
# Optional since it needs PadWalker
|
||||
$StackTraceClass = "Devel::StackTrace::WithLexicals";
|
||||
}
|
||||
|
||||
no warnings 'redefine';
|
||||
|
||||
my $old_fatal = *WebGUI::Session::Log::fatal{CODE} || sub { };
|
||||
if (eval { require Devel::StackTrace::WithLexicals; 1 }) {
|
||||
# Optional since it needs PadWalker
|
||||
|
||||
*WebGUI::Session::Log::fatal = sub {
|
||||
my $self = shift;
|
||||
my $message = shift;
|
||||
$self->{_stacktrace} ||= $StackTraceClass->new; # favor the first stack trace
|
||||
$self->{_message} ||= $message;
|
||||
$old_fatal->($self, $message, @_);
|
||||
};
|
||||
|
||||
my $old_error = *WebGUI::Session::Log::error{CODE};
|
||||
|
||||
*WebGUI::Session::Log::error = sub {
|
||||
my $self = shift;
|
||||
my $message = shift;
|
||||
$self->{_stacktrace} ||= $StackTraceClass->new;
|
||||
$self->{_message} ||= $message;
|
||||
$old_error->($self, $message, @_);
|
||||
};
|
||||
my $old_new = Devel::StackTrace->can('new');
|
||||
*Devel::StackTrace::new = sub {
|
||||
my $self = $old_new ? $old_new->(@_) : { };
|
||||
bless $self, 'Devel::StackTrace::WithLexicals'; # rebless
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub call {
|
||||
my($self, $env) = @_;
|
||||
|
||||
my $res = try { $self->app->($env) };
|
||||
# this won't be Middleware called by the .psgi in the default config unless $env->{'webgui.debug'} is true
|
||||
|
||||
if( my $trace = $env->{'webgui.session'}->log->{_stacktrace} ) {
|
||||
local $SIG{__DIE__} = sub {
|
||||
WebGUI::Error::RunTime->throw(error => $@);
|
||||
};
|
||||
|
||||
undef $env->{'webgui.session'}->log->{_stacktrace}; # the stack trace modules do create circular references; this is necessary
|
||||
# this should also keep us from doing this work twice if we get stacked twice
|
||||
my $res = try { $self->app->($env) }; # XXX this try is useless; plack doesn't let errors cross middlewares
|
||||
|
||||
if( my $e = delete $env->{'webgui.error'} ) {
|
||||
|
||||
my $trace = $e->trace;
|
||||
my $message = $e->error;
|
||||
|
||||
my $text = trace_as_string($trace);
|
||||
my $message = $env->{'webgui.session'}->log->{_message};
|
||||
delete $env->{'webgui.session'}->log->{_message};
|
||||
|
||||
my @previous_html = $res && $res->[2] ? (map ref $_ ? @{ $_ } : $_, $res->[2]) : ();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use strict;
|
|||
use WebGUI::Paths;
|
||||
use WebGUI::Exception;
|
||||
use Sub::Uplevel;
|
||||
use Scalar::Util qw(weaken);
|
||||
use Scalar::Util qw(weaken blessed);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
@ -147,8 +147,15 @@ The message to use.
|
|||
sub fatal {
|
||||
my $self = shift;
|
||||
my $message = shift;
|
||||
my $error_obj = shift;
|
||||
Sub::Uplevel::uplevel( 1, $self->getLogger, { level => 'fatal', message => $message});
|
||||
WebGUI::Error::Fatal->throw( error => $message );
|
||||
if( blessed $error_obj and $error_obj->can('rethrow') ) {
|
||||
# Exception::Class objects have valuable stack traces built in to them; rethrow the existing error to preserve that if possible
|
||||
$error_obj->rethrow;
|
||||
} else
|
||||
{
|
||||
WebGUI::Error::Fatal->throw( error => $message );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue