package LIMS::Local::ErrorHandler; # http://www.stonehenge.com/merlyn/PerlJournal/col05.html use strict; $|++; use Carp; use Data::Dumper; use POSIX qw(strftime); use LIMS::Model::Email; use LIMS::Local::ScriptHelpers; use Sub::Exporter -setup => { exports => [ qw(set_errorhandler_args) ] }; BEGIN { use vars qw($cfg); use FindBin qw($Bin); # warn 'BIN:'.$Bin; exit; use LIMS::Local::Config; $cfg = LIMS::Local::Config->instance; use CGI::Application; =begin use Devel::Events::Objects; use Devel::Cycle; use Devel::Events::Handler::ObjectTracker; use Devel::Events::Filter::RemoveFields; use Devel::Events::Generator::Objects; my $tracker = Devel::Events::Handler::ObjectTracker->new(); my $gen = Devel::Events::Generator::Objects->new( handler => Devel::Events::Filter::RemoveFields->new( fields => [qw/generator/], # don't need to have a ref to $gen in each event handler => $tracker, ), ); =cut no warnings 'redefine'; *CGI::Application::__get_body = sub { my $self = shift; my $rm = shift; my ($rmeth, $is_autoload) = $self->__get_runmeth($rm); my $body; # $gen->enable(); eval { $body = $is_autoload ? $self->$rmeth($rm) : $self->$rmeth(); }; # $gen->disable(); =begin my @leaked_objects = keys %{ $tracker->live_objects }; print "leaked ", scalar(@leaked_objects), " objects\n"; foreach my $object ( @leaked_objects ) { print "Leaked object: $object\n"; # the event that generated it # print Dumper( $object, $tracker->live_objects->{$object} ); print '$object: ' . $object . "\n"; print '$tracker->live_objects->{$object}' . $tracker->live_objects->{$object}. "\n"; find_cycle( $object ); } =cut if ($@) { my $error = $@; # warn Dumper [$error, $self->error_mode]; $self->call_hook('error', $error); if (my $em = $self->error_mode) { $body = $self->$em( $error ); } else { my $error_string = "Error executing run mode '$rm': $error"; # warn $error_string; $body = LIMS::Local::ErrorHandler::fatal_error($error_string); } } # Make sure that $body is not undefined (suppress 'uninitialized value' # warnings) return defined $body ? $body : ''; } } ################################################################################ # need 'to', 'from' & 'smtp' vars supplying via $args to initiate email to admin ################################################################################ use vars qw($args); my $tools = LIMS::Local::ScriptHelpers->new(); $tools->use_path("$Bin/.."); # for ScriptHelpers to find correct path for templates sub set_errorhandler_args { $args = shift; # warn Dumper $args; } sub fatal_error { my $message = shift; # my ($package, $file, $line) = caller; warn Dumper ($package, $file, $line); # don't need it my $incident = unpack "H*", pack "NS", time, $$; my $time = strftime "%d-%b-%Y %H:%M:%S", localtime; my $user = uc $args->{user}; my %report = ( settings => $cfg->{settings}, incident => $incident, message => $message, user => $user, ); # record reason for death into the error log warn join "", map { sprintf "crash %s [%s %s]: %s\n", $incident, $user, $time, $_; } split /\n+/, $message; # maybe alert admin: if ( $ENV{ROSEDB_DEVINIT} =~ /prod/ ) { my $response = _send_email(\%report); $report{mail_response} = $response->string; # if $response->type ne 'success'; # for tt } # now give the user a place to record their experience my $output = $tools->process_template('error/errorhandler.tt', \%report); # warn Dumper $output; return \$output; } sub _send_email { my $report = shift; my $settings = $cfg->{settings}; # need 'to' & 'from' attr from settings: my $from = $settings->{email_from} || return 0; my $smtp = $settings->{smtp} || return 0; my $to = $settings->{sysadmin_email} || return 0; my $subject = sprintf '%s [%s]', $report->{incident}, $report->{user}; my $message = $report->{message}; my %mail = ( recipient => $to, from => $from, config => $settings, message => $message, subject => $subject, ); # DEBUG(\%mail); my $res = LIMS::Model::Email->send_message(\%mail); # Return::Value object return $res; } 1;