]> jfr.im git - munin-plugins.git/commitdiff
make work with journald + add datapoints
authorJohn Runyon <redacted>
Thu, 28 Sep 2023 21:53:49 +0000 (16:53 -0500)
committerJohn Runyon <redacted>
Thu, 28 Sep 2023 21:53:49 +0000 (16:53 -0500)
postfix_mailstats

index 518c9c9bd36028dd48e8b6589312963b0458734f..24776348302a40ea8891f6e9508ae423e88d7a42 100755 (executable)
@@ -57,151 +57,140 @@ use Munin::Plugin;
 
 my $statefile = $ENV{'MUNIN_PLUGSTATE'} . "/munin-plugin-postfix_mailstats.state";
 my $pos;
-my $delivered = 0;
+my %descriptions = (
+       messages => 'Number of messages',
+       recipients => 'Number of recipients',
+       local => 'Outgoing local messages',
+       smtp => 'Outgoing SMTP messages',
+       pickup => 'Incoming local messages',
+       smtpd => 'Incoming SMTP messages',
+       bounce => 'Bounces handled',
+);
+my %stats = map { $_ => 0 } keys %descriptions;
 my %rejects = ();
-my $LOGDIR  = $ENV{'logdir'}  || '/var/log';
-my $LOGFILE = $ENV{'logfile'} || 'mail.log';
-
-my $logfile = "$LOGDIR/$LOGFILE";
-
-if ( $ARGV[0] and $ARGV[0] eq "autoconf" )
-{
-    if (-d $LOGDIR)
-    {
-        if (-f $logfile)
-        {
-            if (-r $logfile)
-            {
-                print "yes\n";
-                exit 0;
-            }
-            else
-            {
-                print "no (logfile '$logfile' not readable)\n";
-            }
-        }
-        else
-        {
-            print "no (logfile '$logfile' not found)\n";
-        }
-    }
-    else
-    {
-        print "no (could not find logdir '$LOGDIR')\n";
-    }
-
-    exit 0;
+
+if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) {
+       my $logfile;
+       `which postconf >/dev/null 2>/dev/null`;
+       if (!$?) { # if postconf returns success
+               `journalctl --facility=mail --show-cursor -n 0`;
+               if (!$?) { # if journalctl returns success
+                       print "yes\n";
+               } else {
+                       print "no (journalctl returned error)\n";
+               }
+       } else {
+               print "no (postfix not found)\n";
+       }
+
+       exit 0;
 }
 
 
-if ( -f $statefile)
-{
-    open (IN, '<', $statefile) or die "Unable to open state-file: $!\n";
-    if (<IN> =~ /^(\d+):(\d+)/)
-    {
-        ($pos, $delivered) = ($1, $2);
-    }
-    while (<IN>)
-    {
-        if (/^([0-9a-z.\-]+):(\d+)$/)
-        {
-            $rejects{$1} = $2;
-        }
-    }
-    close IN;
+if ( -f $statefile) {
+       open (IN, '<', $statefile) or die "Unable to open state-file: $!\n";
+       chomp($pos = <IN>);
+       chomp(my $stat_line = <IN>);
+       for my $stat (split /:/, $stat_line) {
+               my ($k, $v) = split /=/, $stat, 2;
+               $stats{$k} = $v;
+       }
+       while (<IN>) {
+               if (/^([0-9a-z.\-]+):(\d+)$/) {
+                       $rejects{$1} = $2;
+               }
+       }
+       close IN;
 }
 
-if (! -f $logfile)
-{
-    print "delivered.value U\n";
-    foreach my $reason (sort keys %rejects)
-    {
-        my $fieldname = clean_fieldname("r$reason");
-        print "$fieldname.value U\n";
-    }
-    exit 0;
+if (!defined $pos) { # Initial run.
+       my $cursor = `journalctl --facility=mail --show-cursor -n 0 | tail -n 1`;
+       $pos = ($cursor =~ s/^-- cursor: //r);
 }
 
-
-my $startsize = (stat $logfile)[7];
-
-if (!defined $pos)
-{
-    # Initial run.
-    $pos = $startsize;
+$pos = parseLogfile($pos);
+
+if ( $ARGV[0] and $ARGV[0] eq "config" ) {
+       print "graph_title Postfix message throughput\n";
+       print "graph_args --base 1000 -l 0\n";
+       print "graph_vlabel mails / \${graph_period}\n";
+       print "graph_scale  no\n";
+       print "graph_total  Total\n";
+       print "graph_category postfix\n";
+       print "graph_period minute\n";
+       print "messages.label messages\n";
+       print "messages.type DERIVE\n";
+       print "messages.min 0\n";
+
+       foreach my $key (sort keys %descriptions) {
+               print "$key.label $key\n";
+               print "$key.info $descriptions{$key}\n";
+               print "$key.type DERIVE\n";
+               print "$key.min 0\n";
+       }
+
+       foreach my $reason (sort keys %rejects) {
+               my $fieldname = clean_fieldname("r$reason");
+               print "$fieldname.label reject $reason\n";
+               print "$fieldname.type DERIVE\n";
+               print "$fieldname.min 0\n";
+       }
+       exit 0;
 }
 
-parseLogfile($logfile, $pos, $startsize);
-$pos = $startsize;
-
-if ( $ARGV[0] and $ARGV[0] eq "config" )
-{
-    print "graph_title Postfix message throughput\n";
-    print "graph_args --base 1000 -l 0\n";
-    print "graph_vlabel mails / \${graph_period}\n";
-    print "graph_scale  no\n";
-    print "graph_total  Total\n";
-    print "graph_category postfix\n";
-    print "graph_period minute\n";
-    print "delivered.label delivered\n";
-    print "delivered.type DERIVE\n";
-    print "delivered.draw AREA\n";
-    print "delivered.min 0\n";
-    foreach my $reason (sort keys %rejects)
-    {
-        my $fieldname = clean_fieldname("r$reason");
-        print "$fieldname.label reject $reason\n";
-        print "$fieldname.type DERIVE\n";
-        print "$fieldname.draw STACK\n";
-        print "$fieldname.min 0\n";
-    }
-    exit 0;
+foreach my $type (sort keys %stats) {
+       print "$type.value " . $stats{$type} . "\n";
 }
-
-print "delivered.value $delivered\n";
-foreach my $reason (sort keys %rejects)
-{
-    my $fieldname = clean_fieldname("r$reason");
-    print "$fieldname.value ", $rejects{$reason}, "\n";
+foreach my $reason (sort keys %rejects) {
+       my $fieldname = clean_fieldname("r$reason");
+       print "$fieldname.value ", $rejects{$reason}, "\n";
 }
 
 if(-l $statefile) {
-    die("$statefile is a symbolic link, refusing to touch it.");
+       die("$statefile is a symbolic link, refusing to touch it.");
 }
 open (OUT, '>', $statefile) or die "Unable to open statefile: $!\n";
-print OUT "$pos:$delivered\n";
-foreach my $i (sort keys %rejects)
-{
-    print OUT "$i:", $rejects{$i}, "\n";
+print OUT "$pos\n";
+print OUT join(':', map { "$_=$stats{$_}" } keys %stats) . "\n";
+foreach my $i (sort keys %rejects) {
+       print OUT "$i:", $rejects{$i}, "\n";
 }
 close OUT;
 
-sub parseLogfile
-{
-    my ($fname, $start, $stop) = @_;
-    open (LOGFILE, $fname)
-        or die "Unable to open logfile $fname for reading: $!\n";
-    seek (LOGFILE, $start, 0)
-        or die "Unable to seek to $start in $fname: $!\n";
-
-    while (tell (LOGFILE) < $stop)
-    {
-        my $line = <LOGFILE>;
-        chomp ($line);
-
-        if ($line =~ / to=.*, status=sent /)
-        {
-            $delivered++;
-        }
-        elsif ($line =~ /postfix\/smtpd.*proxy-reject: \S+ (\S+)/ ||
-               $line =~ /postfix\/smtpd.*reject: \S+ \S+ \S+ (\S+)/ ||
-               $line =~ /postfix\/cleanup.* reject: (\S+)/ ||
-               $line =~ /postfix\/cleanup.* milter-reject: \S+ \S+ \S+ (\S+)/)
-        {
-            $rejects{$1}++;
-        }
-    }
-    close(LOGFILE) or warn "Error closing $fname: $!\n";
+sub parseLogfile {
+       my ($start) = @_;
+       open(my $LOGFILE, '-|', "journalctl --facility=mail --after-cursor='$start' --show-cursor")
+               or die "Unable to open journalctl for reading: $? $!\n";
+
+       while (my $line = <$LOGFILE>) {
+               chomp $line;
+
+               if ($line =~ /qmgr\[[^]]+\]: [0-9A-Za-z]+: from=.*, size=\d+, nrcpt=(\d+)/) {
+                       $stats{messages}++;
+                       $stats{recipients} += $1;
+               } elsif ($line =~ /local\[[^]]+\]: [0-9A-Za-z]+: to=/) {
+                       $stats{local}++;
+               } elsif ($line =~ /smtp\[[^]]+\]: [0-9A-Za-z]+: to=/) {
+                       $stats{smtp}++;
+               } elsif ($line =~ /pickup\[[^]]+\]: [0-9A-Za-z]+: uid=/) {
+                       $stats{pickup}++;
+               } elsif ($line =~ /smtpd\[[^]]+\]: [0-9A-Za-z]+: client=/) {
+                       $stats{smtpd}++;
+               } elsif ($line =~ /bounce\[[^]]+\]: [0-9A-Za-z]+: /) {
+                       $stats{bounce}++;
+               } elsif (
+                       $line =~ /postfix\/smtpd.*proxy-reject: \S+ (\S+)/ ||
+                       $line =~ /postfix\/smtpd.*reject: \S+ \S+ \S+ (\S+)/ ||
+                       $line =~ /postfix\/cleanup.* reject: (\S+)/ ||
+                       $line =~ /postfix\/cleanup.* milter-reject: \S+ \S+ \S+ (\S+)/
+               ) {
+                       $rejects{$1}++;
+               } elsif ($line =~ /^-- cursor: (.+)$/) {
+                       $start = $1;
+               }
+       }
+       close($LOGFILE);
+       return $start;
 }
 
 # vim:syntax=perl