diff --git a/Build.PL b/Build.PL index 4c7c31a97f..a92de0cffb 100644 --- a/Build.PL +++ b/Build.PL @@ -32,9 +32,12 @@ my $builder = Local::Module::Build->new( 'Catalyst::View::JSON' => 0, 'Catalyst::View::TT' => 0, 'Config::General' => 0, + 'Convert::Ascii85' => 0, + 'Data::Dumper' => 0, 'Data::Validate::IP' => 0, 'DateTime' => 0, 'DateTime::Format::ISO8601' => 0, + 'DateTime::Format::RFC3339' => 0, 'DBIx::Class::ResultSet::RecursiveUpdate' => '0.30', 'Email::Valid' => 0, 'File::ShareDir' => 0, @@ -49,6 +52,7 @@ my $builder = Local::Module::Build->new( 'HTML::FormHandler::Moose' => 0, 'HTML::FormHandler::Widget::Block::Bootstrap' => 0, 'HTTP::Status' => 0, + 'IO::Compress::Xz' => 0, 'IPC::System::Simple' => 0, 'Log::Log4perl::Catalyst' => 0, 'Module::Runtime' => 0, @@ -65,6 +69,7 @@ my $builder = Local::Module::Build->new( 'strict' => 0, 'Template' => 0, 'Text::CSV_XS' => 0, + 'Time::HiRes' => 0, 'URI::Encode' => 0, 'URI::Escape' => 0, 'UUID' => 0, @@ -95,6 +100,9 @@ my $builder = Local::Module::Build->new( # for the testcover target 'Devel::Cover' => 0, 'sigtrap' => 0, + # for development + 'Convert::Ascii85' => 0, + 'IO::Uncompress::UnXz' => 0, }, add_to_cleanup => [ 'NGCP-Panel-*' ], ); diff --git a/lib/NGCP/Panel/Controller/Root.pm b/lib/NGCP/Panel/Controller/Root.pm index 4e54139888..7b00f826a6 100644 --- a/lib/NGCP/Panel/Controller/Root.pm +++ b/lib/NGCP/Panel/Controller/Root.pm @@ -1,11 +1,15 @@ package NGCP::Panel::Controller::Root; use Sipwise::Base; - - BEGIN { extends 'Catalyst::Controller' } - +use Carp qw(longmess); +use Convert::Ascii85 qw(); +use Data::Dumper qw(Dumper); +use DateTime qw(); +use DateTime::Format::RFC3339 qw(); +use IO::Compress::Xz qw(xz); use NGCP::Panel::Widget; use Scalar::Util qw(blessed); +use Time::HiRes qw(); # # Sets the actions in this controller to be registered with no prefix @@ -111,7 +115,41 @@ sub default :Path { $c->detach( '/error_page' ); } -sub end : ActionClass('RenderView') {} +sub render :ActionClass('RenderView') { } + +sub end :Private { + my ($self, $c) = @_; + $c->forward('render'); + if (@{ $c->error }) { + my $incident = DateTime->from_epoch(epoch => Time::HiRes::time); + my $incident_id = sprintf '%X', $incident->strftime('%s%N'); + my $incident_timestamp = DateTime::Format::RFC3339->new->format_datetime($incident); + my $crash_state; + if ($c->config->{log_crash_state}) { + local $Data::Dumper::Indent = 1; + local $Data::Dumper::Useqq = 1; + local $Data::Dumper::Deparse = 1; + local $Data::Dumper::Quotekeys = 0; + local $Data::Dumper::Sortkeys = 1; + my $buffer; + xz(\(join(q(), @{ $c->error }) . longmess . Dumper($c) . Dumper($c->config)), \$buffer); + $crash_state = Convert::Ascii85::encode($buffer); + $crash_state =~ s/(.{80})/$1\n/g; + } + $c->log->error( + "Exception id $incident_id at $incident_timestamp crash_state:" . + ($crash_state ? ("\n" . $crash_state) : ' disabled') + ); + $c->clear_errors; + $c->stash( + exception_incident => $incident_id, + exception_timestamp => $incident_timestamp, + template => 'error_page.tt' + ); + $c->response->status(500); + $c->detach($c->view); + } +} sub _prune_row { my ($columns, %row) = @_; @@ -129,8 +167,7 @@ sub error_page :Private { my ($self,$c) = @_; $c->log->error( 'Failed to find path ' . $c->request->path ); - $c->stash(template => 'error_page.tt'); - #$c->response->body( 'Page not found' ); + $c->stash(template => 'notfound_page.tt'); $c->response->status(404); } diff --git a/ngcp_panel.conf b/ngcp_panel.conf index 6975164a04..7b86c9f0b7 100644 --- a/ngcp_panel.conf +++ b/ngcp_panel.conf @@ -10,6 +10,9 @@ log4perl.appender.Default.layout=PatternLayout log4perl.appender.Default.layout.ConversionPattern=%d{ISO8601} [%p] [%F +%L] %m{chomp}%n # perhaps also add: host=%H pid=%P +# each crash state is <50 KiB +log_crash_state 1 + schema_class NGCP::Schema diff --git a/script/ngcp_panel_decode_crash b/script/ngcp_panel_decode_crash new file mode 100755 index 0000000000..3440644722 --- /dev/null +++ b/script/ngcp_panel_decode_crash @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +use Convert::Ascii85 qw(); +use IO::Uncompress::UnXz qw(unxz $UnXzError); + +local $/ = undef; +my $buf = Convert::Ascii85::decode(<>); +my $out; +unxz \$buf, \$out or die $UnXzError; +print $out; + +__END__ + +=encoding UTF-8 + +=head1 NAME + +ngcp_panel_decode_crash - decode a crash state from the Web server error log + +=head1 USAGE + + ngcp_panel_decode_crash < crashfile diff --git a/share/templates/error_page.tt b/share/templates/error_page.tt index 5556f10866..7bf908312a 100644 --- a/share/templates/error_page.tt +++ b/share/templates/error_page.tt @@ -6,11 +6,16 @@

Oops!

-

[% c.response.status %] Not found

+

[% c.response.status %] Internal server error

- Sorry, an error has occured, Requested page not found! - +

Sorry, an exceptional error has occured:

+ +

Details have been logged on the server. If you want to report the error, + describe what you were doing or attempting to do just before.

diff --git a/share/templates/notfound_page.tt b/share/templates/notfound_page.tt new file mode 100644 index 0000000000..5556f10866 --- /dev/null +++ b/share/templates/notfound_page.tt @@ -0,0 +1,37 @@ +
+ +
+ +
+ +

Oops!

+ +

[% c.response.status %] Not found

+ +
+ Sorry, an error has occured, Requested page not found! + +
+ + + +
+ + +
+ +
+