Created
March 9, 2018 15:24
-
-
Save Code-Hex/90301253e3fe91ec71b2d706e8499e66 to your computer and use it in GitHub Desktop.
slacklog is ported from https://github.com/kazuho/kaztools/blob/master/cronlog
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #! /usr/bin/perl | |
| use strict; | |
| use warnings; | |
| use Fcntl qw(SEEK_SET); | |
| use File::Temp qw(tempfile); | |
| use Getopt::Long; | |
| use Pod::Usage; | |
| use Furl; | |
| use JSON qw/encode_json/; | |
| our $VERSION = '0.01'; | |
| my ($logfile, $opt_timestamp, $opt_print_always, $opt_help, $opt_version, $opt_channel); | |
| GetOptions( | |
| 'logfile=s' => \$logfile, | |
| 'timestamp' => \$opt_timestamp, | |
| 'print-always' => \$opt_print_always, | |
| 'channel' => \$opt_channel, | |
| 'help' => \$opt_help, | |
| 'version' => \$opt_version, | |
| ) or pod2usage(1); | |
| if ($opt_help) { | |
| pod2usage(0); | |
| } elsif ($opt_version) { | |
| print "$VERSION\n"; | |
| exit 0; | |
| } | |
| die "no args" | |
| unless @ARGV; | |
| # open logfile | |
| my $logfh; | |
| if ($logfile) { | |
| open $logfh, '>>', $logfile | |
| or die "failed to open file:$logfile:$!"; | |
| } else { | |
| ($logfh, $logfile) = tempfile(UNLINK => 1); | |
| } | |
| autoflush $logfh 1; | |
| print $logfh '-'x78, "\n"; | |
| my $logpos = tell $logfh; | |
| die "failed to obtain position of logfile:$!" | |
| if $logpos == -1; | |
| seek $logfh, $logpos, SEEK_SET | |
| or die "cannot seek within logfile:$!"; | |
| pipe my $logrh, my $logwh | |
| or die "failed to create pipe:$!"; | |
| # exec | |
| _log( | |
| do { my $h = `hostname 2> /dev/null`; chomp $h; $h } | |
| . ' starting: `' . join(' ', @ARGV) . "`\n", | |
| ); | |
| my $exit_code = -1; | |
| unless (my $pid = fork) { | |
| if (defined $pid) { | |
| # child process | |
| close $logrh; | |
| close $logfh; | |
| open STDERR, '>&', $logwh | |
| or die "failed to redirect STDERR to logfile"; | |
| open STDOUT, '>&', $logwh | |
| or die "failed to redirect STDOUT to logfile"; | |
| close $logwh; | |
| exec @ARGV; | |
| die "exec(2) failed:$!:@ARGV"; | |
| } else { | |
| close $logrh; | |
| close $logwh; | |
| print $logfh, "fork(2) failed:$!\n" | |
| unless defined $pid; | |
| } | |
| } else { | |
| close $logwh; | |
| _log("Result:\n" . "`" x 3 . "\n"); | |
| _log($_) while <$logrh>; | |
| _log("`" x 3 . "\n"); | |
| close $logrh; | |
| while (wait == -1) {} | |
| $exit_code = $?; | |
| } | |
| # end | |
| if ($exit_code == -1) { | |
| _log("failed to execute command:$!\n"); | |
| } elsif ($exit_code & 127) { | |
| _log("command died with signal:" . ($exit_code & 127) . "\n"); | |
| } else { | |
| _log("command exited with code:" . ($exit_code >> 8) ."\n"); | |
| } | |
| # print log to stdout | |
| if ($exit_code != 0 || $opt_print_always) { | |
| open my $fh, '<', $logfile | |
| or die "failed to open $logfile:$!"; | |
| seek $fh, $logpos, SEEK_SET | |
| or die "failed to seek to the appropriate position in logfile:$!"; | |
| my $str = do { local $/; <$fh>; }; | |
| if (my $url = $ENV{SLACK_WEBHOOK_URL}) { | |
| my $furl = Furl->new; | |
| my $data = +{ | |
| text => $str, | |
| $opt_channel ? (channel => $opt_channel) : () | |
| }; | |
| my $res = $furl->post($url, ['Content-type' => 'application/json'], encode_json($data)); | |
| exit($exit_code >> 8) if $res->is_success; | |
| } | |
| print $str; | |
| } | |
| exit($exit_code >> 8); | |
| sub _log { | |
| my ($line, $timestamp) = @_; | |
| print $logfh ( | |
| ($timestamp || $opt_timestamp ? '[' . scalar(localtime) . '] ' : ''), | |
| $line, | |
| ); | |
| } | |
| __END__ | |
| =head1 NAME | |
| slacklog - log output of a cron job, and send slack on error | |
| =head1 SYNOPSIS | |
| slacklog [--channel=slack_channel] [--logfile=logfile] [--timestamp] [--print-always] -- cmd args... | |
| =head1 DESCRIPTION | |
| slacklog is a wrapper script for running cron tasks. It does the following: | |
| =over 4 | |
| =item logs the output of the command | |
| If logfile is specified, stdout and stderr of the command will be logged to the file so that it could be used for later inspection. If not specified, the outputs will not be logged. | |
| =item prints the output on error | |
| slacklog prints the outputs the command if and only if the command execution failed (i.e. fails to start, or returns a non-zero exit code, or killed by a signal). In other words, this behaviour causes L<cron> to send mail when and only when an error occurs. | |
| =back | |
| =head1 AUTHOR | |
| codehex | |
| =cut |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
環境変数
SLACK_WEBHOOK_URLに incoming Webhook の URL を指定してあげる。Furl と JSON モジュールに依存している。