Skip to content

Instantly share code, notes, and snippets.

@neolitec
Created June 22, 2012 15:33
Show Gist options
  • Save neolitec/2973510 to your computer and use it in GitHub Desktop.
Save neolitec/2973510 to your computer and use it in GitHub Desktop.
Error Catcher in Perl

Perl Batch Error Catcher for Nagios

Catcher - Perl extension for error handling

=head1 SYNOPSIS

use Catcher; #...some piece of code END_PROGRAM;

Declare errors with custom message

use Catcher; ERROR "Custom error !";

the program ends here

END_PROGRAM;

Warning handling for Nagios can be disabled for ugly scripts

use Catcher; warningsOff; END_PROGRAM;

Warnings can be temporarly disabled

use Catcher; warningsOff;

ugly instructions

warningsOn;

supposed safe instructions

END_PROGRAM;

For critical scripts, a warning can trigger an error

use Catcher; dieOnWarn(1);

critical instructions

END_PROGRAM;

Reporting variable can be defined

use Catcher; use Catcher qw(ReportVars); reportVar('user_creation', 0); incVar('user_creation'); incVar('user_creation'); incVar('user_creation'); reportVar('user_modification', 0); END_PROGRAM;

Reporting variables can be defined in a foreign hash

use Catcher; use Catcher qw(:ReportVars); %vars = ( user_creation => 4, user_modification => 2 ); setReportVars(%vars); END_PROGRAM;

Customise the success message

use Catcher; END_PROGRAM("Custom message");

Customise the success message (other method)

use Catcher;

foo...

setSuccessMessage("foo ok");

bar...

setSuccessMessage("bar ok"); END_PROGRAM;

package Catcher;
use strict;
use warnings;
use Carp;
use Carp qw (cluck);
use File::Basename;
use POSIX qw(strftime);
# Nagios error levels
use constant {
NG_OK => 0,
NG_WARNING => 1,
NG_CRITICAL => 2,
NG_UNKNOWN => 3
};
our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $NG_REPORT_DIRECTORY);
my ($NG_REPORT_FILENAME);
# Private (internals)
# Recommended settings
my $_SUCCESS = 0;
my $_ERROR = 0;
my $_LEVEL = NG_OK;
my $_DISABLE_WARNINGS = 0;
my $_STOP_ON_WARNING = 0;
# Internal
sub _warn {
if(!$_DISABLE_WARNINGS) {
$_ERROR = $_[0];
$_LEVEL = NG_WARNING;
$_SUCCESS = 0;
if($_STOP_ON_WARNING) {
die $_[0];
} else {
warn $_[0];
}
}
}
# Exported
sub ERROR {
$_ERROR = shift;
$_LEVEL = NG_CRITICAL;
$_SUCCESS = 0;
die $_ERROR;
}
BEGIN {
require Exporter;
our $VERSION = "1.0.0";
our @ISA = qw(Exporter);
# Exported by default
our @EXPORT = qw(ERROR END_PROGRAM warningsOn warningsOff dieOnWarn setNagiosFilename setSuccessMessage);
# Can be optionally exported
our @EXPORT_OK = qw(trace $NG_REPORT_DIRECTORY);
# Tags
our %EXPORT_TAGS = (
ReportVars => [qw(reportVar incVar setReportVars)]
);
# Resulting @EXPORT_OK is @EXPORT_OK = qw(trace $NG_REPORT_DIRECTORY reportVar incVar setReportVars);
Exporter::export_ok_tags('ReportVars');
# Guess the default Nagios report filename
$NG_REPORT_FILENAME = basename($0, '.pl');
# Destination folder for the .nagios file
# Can be accessed and modified (for tests) although it is discouraged to it.
$NG_REPORT_DIRECTORY = "/var/log/apps";
# Catch DIE signals
$SIG{__DIE__} = sub { ERROR $_[0] };
# Catch WARN signals
$SIG{__WARN__} = sub { _warn $_[0] };
}
# Nagios reporting variables
my %report_vars = ();
my $success_message = "OK Execution normale";
# Exported
# Setter for the nagios report filename
sub setNagiosFilename {
($NG_REPORT_FILENAME) = shift;
}
# Exported
# A program must finish with this instruction.
sub END_PROGRAM {
($_) = @_;
if(defined $_) {
setSuccessMessage($_);
}
if(!$_ERROR && !$_LEVEL) {
$_SUCCESS = 1;
}
}
# Exported
# When warnings are enabled, the last warning will be reported (if there is no error).
sub warningsOn {
$_DISABLE_WARNINGS = 0;
}
# Exported
# When warnings are disabled, the errors of "warn" level are totally ignored, but still printed.
sub warningsOff {
$_DISABLE_WARNINGS = 1;
}
# Exported
# Trigger die when __WARN__ is encountered.
# It transforms the first warn instruction into a die instruction.
sub dieOnWarn {
($_STOP_ON_WARNING) = shift;
}
### Variables management
# Variable can be defined in order to be reported in Nagios
# Add a variable
sub reportVar {
my ($key, $value) = @_;
$report_vars{$key} = $value;
}
sub getReportVar {
my ($key) = @_;
return $report_vars{$key};
}
sub incVar {
my ($key) = @_;
$report_vars{$key}++;
}
sub setReportVars {
%report_vars = @_;
}
sub setSuccessMessage {
($success_message) = @_;
}
# Exportable
sub trace {
my (@args) = shift();
foreach my $line (@args) {
print "$line\n";
}
}
# Private
my $sanitizePath = sub {
($_) = @_;
# hack for not removing last directory
$_ = "$_/.";
# Is the destination directory exist ?
unless(-e) { croak "Directory $_ does not exist !" }
return dirname($_);
};
my $writeReport = sub {
my $dest = &$sanitizePath($NG_REPORT_DIRECTORY)."/".$NG_REPORT_FILENAME.".nagios";
my $safeError;
if($_SUCCESS) {
$safeError = $success_message;
} else {
$safeError = $_ERROR;
}
# Delete special chars
$safeError =~ s/[\|,]+/\-/g;
# Keep the first line
my @safeErrorArray = split("\n", $safeError);
$safeError = $safeErrorArray[0];
my $content = (strftime "%F-%T", localtime).",$_LEVEL,$safeError";
# Construct vars restitution
my @reportVars = ();
while (my($k, $v) = each(%report_vars)) {
push @reportVars, "$k=$v";
}
my $reportVarsString = join ' ; ', @reportVars;
if(length($reportVarsString)) {
$content .= " | ".$reportVarsString;
}
open REPORT, ">".$dest;
print REPORT $content;
close REPORT;
};
END {
&$writeReport();
if(!$_SUCCESS) {
if($_LEVEL == NG_WARNING) {
trace "WARNING : $_ERROR";
} elsif($_LEVEL == NG_CRITICAL) {
trace "CRITICAL : $_ERROR";
} elsif($_LEVEL == NG_UNKNOWN) {
trace "UNKNOWN : $_ERROR";
}
exit(1);
} else {
trace "SUCCESS";
exit(0);
}
};
1;
__END__
=head1 NAME
Catcher - Perl extension for error handling
=head1 SYNOPSIS
use Catcher;
#...some piece of code
END_PROGRAM;
# Declare errors with custom message
use Catcher;
ERROR "Custom error !";
# the program ends here
END_PROGRAM;
# Warning handling for Nagios can be disabled for ugly scripts
use Catcher;
warningsOff;
END_PROGRAM;
# Warnings can be temporarly disabled
use Catcher;
warningsOff;
# ugly instructions
warningsOn;
# supposed safe instructions
END_PROGRAM;
# For critical scripts, a warning can trigger an error
use Catcher;
dieOnWarn(1);
# critical instructions
END_PROGRAM;
# Reporting variable can be defined
use Catcher;
use Catcher qw(ReportVars);
reportVar('user_creation', 0);
incVar('user_creation');
incVar('user_creation');
incVar('user_creation');
reportVar('user_modification', 0);
END_PROGRAM;
# Reporting variables can be defined in a foreign hash
use Catcher;
use Catcher qw(:ReportVars);
%vars = (
user_creation => 4,
user_modification => 2
);
setReportVars(%vars);
END_PROGRAM;
# Customise the success message
use Catcher;
END_PROGRAM("Custom message");
# Customise the success message (other method)
use Catcher;
# foo...
setSuccessMessage("foo ok");
# bar...
setSuccessMessage("bar ok");
END_PROGRAM;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment