package Silki::Plugin::ErrorHandling; { $Silki::Plugin::ErrorHandling::VERSION = '0.29'; } use strict; use warnings; use Data::Dump qw/dump/; use HTML::Entities qw( encode_entities ); use HTTP::Status qw( RC_NOT_FOUND RC_INTERNAL_SERVER_ERROR ); use Silki::JSON; # I'd really rather _not_ copy this whole thing in here, but it's the # only way to override how errors are logged. I have to monkey-patch # rather than subclassing or else NEXT::finalize() ends up calling the # finalize in Catalyst itself before calling finalize() for other # plugins (a mess!). { package Catalyst; no warnings 'redefine'; sub finalize { my $self = shift; for my $error ( @{ $self->error } ) { $self->_log_error($error); } # Allow engine to handle finalize flow (for POE) if ( $self->engine->can('finalize') ) { $self->engine->finalize($self); } else { $self->finalize_uploads; # Error if ( $#{ $self->error } >= 0 ) { $self->finalize_error; } $self->finalize_headers; # HEAD request if ( $self->request->method eq 'HEAD' ) { $self->response->body(''); } $self->finalize_body; } if ( $self->use_stats ) { my $elapsed = sprintf '%f', $self->stats->elapsed; my $av = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed; $self->log->info( "Request took ${elapsed}s ($av/s)\n" . $self->stats->report . "\n" ); } return $self->response->status; } } sub _log_error { my $self = shift; my $error = shift; # XXX - change this later to log to the apache log? # if ( $error =~ /unknown resource/ ) my %error = ( uri => $self->request()->uri() . '' ); if ( my $user = $self->user() ) { $error{user} = $user->username(); $error{user} .= q{ - } . $user->user_id() if $user->user_id(); } $error{error} = $error . ''; $self->log()->error( Silki::JSON->Encode( \%error ) ); } sub finalize_error { my $self = shift; my @errors = @{ $self->error() || [] }; my $status = ( grep {/unknown resource|no default/i} @errors ) ? RC_NOT_FOUND : RC_INTERNAL_SERVER_ERROR; $self->response()->status($status); if ( $self->debug() ) { $self->_finalize_error_with_debug( $self, @_ ); return; } $self->error( [] ); $self->response()->content_type('text/html; charset=utf-8'); $self->response()->body( $self->subreq("/error/$status") ); } # copied from Catalyst::Engine->finalize_error just so we can set the # fucking status. GRRRR! sub _finalize_error_with_debug { my $self = shift; my $c = shift; $c->res->content_type('text/html; charset=utf-8'); my $name = $c->config->{name} || join( ' ', split( '::', ref $c ) ); my ( $title, $error, $infos ); if ( $c->debug ) { # For pretty dumps $error = join '', map { '

' . encode_entities($_) . '

' } @{ $c->error }; $error ||= 'No output'; $error = qq{
$error
}; $title = $name = "$name on Catalyst $Catalyst::VERSION"; $name = "

$name

"; # Don't show context in the dump delete $c->req->{_context}; delete $c->res->{_context}; # Don't show body parser in the dump delete $c->req->{_body}; # Don't show response header state in dump delete $c->res->{_finalized_headers}; my @infos; my $i = 0; for my $dump ( $c->dump_these ) { my $name = $dump->[0]; # stored in there for classes with an anon metaclass. delete $dump->[1]{__MOP__} if ref $dump->[1]; my $value = encode_entities( dump( $dump->[1] ) ); push @infos, sprintf <<"EOF", $name, $value;

%s

%s
EOF $i++; } $infos = join "\n", @infos; } else { $title = $name; $error = ''; $infos = <<"";
(en) Please come back later
(fr) SVP veuillez revenir plus tard
(de) Bitte versuchen sie es spaeter nocheinmal
(at) Konnten's bitt'schoen spaeter nochmal reinschauen
(no) Vennligst prov igjen senere
(dk) Venligst prov igen senere
(pl) Prosze sprobowac pozniej
(pt) Por favor volte mais tarde
$name = ''; } $c->res->body( <<"" ); $title
$error
$infos
$name
# Trick IE $c->res->{body} .= ( ' ' x 512 ); $c->res->status(500) unless $c->res->status; } 1; # ABSTRACT: An uber-hack plugin to override Catalyst's error output and add better error logging __END__ =pod =head1 NAME Silki::Plugin::ErrorHandling - An uber-hack plugin to override Catalyst's error output and add better error logging =head1 VERSION version 0.29 =head1 AUTHOR Dave Rolsky =head1 COPYRIGHT AND LICENSE This software is Copyright (c) 2011 by Dave Rolsky. This is free software, licensed under: The GNU Affero General Public License, Version 3, November 2007 =cut