Skip to content

Instantly share code, notes, and snippets.

@tsuchm
Last active March 12, 2020 14:15
Show Gist options
  • Save tsuchm/b2835e61415b2085c097e58c5f98fc47 to your computer and use it in GitHub Desktop.
Save tsuchm/b2835e61415b2085c097e58c5f98fc47 to your computer and use it in GitHub Desktop.
Munin plugins to observe incoming throughput and outgoing throughput
#!/usr/bin/perl
=head1 NAME
postfix_incoming_mailstats - Plugin to monitor the number of mails
received by postfix
=head1 DEFAULT CONFIGURATION
[postfix_incoming_mailstats]
env.logdir /var/log
env.logfile mail.log
env.confdir /etc/postfix
env.conffile master.cf
=head1 Target Log Messages
=head2 # of Connections
Mar 7 00:38:08 mail postfix/postscreen[123]: CONNECT from [192.0.2.51]:12345 to [203.0.113.10]:25
Mar 7 00:38:23 mail postfix/smtpd[124]: connect from mail.example.net[192.0.2.52]
=head2 # of Rejected Messages
Mar 7 00:38:23 mail postfix/smtpd[125]: NOQUEUE: reject: RCPT from mail.example.net[192.0.2.42]:
450 4.1.8 <[email protected]>: Sender address rejected: Domain not found; from=<[email protected]>
to=<[email protected]> proto=ESMTP helo=<mail.example.net>
=head2 # of Normal Received Messages
Mar 6 05:43:34 mail postfix/smtpd[126]: disconnect from unknown[192.0.2.42] ehlo=1 mail=1 rcpt=1
data=1 commands=4
=head1 AUTHOR
TSUCHIYA Masatoshi <[email protected]>
=head1 LICENSE
GPLv3
=head1 MAGIC MARKERS
=begin comment
These magic markers are used by munin-node-configure when installing
munin-node.
=end comment
#%# family=manual
#%# capabilities=autoconf
=cut
use File::Spec::Functions qw/ catfile /;
use Munin::Plugin;
use strict;
our $LOGDIR = $ENV{'logdir'} || '/var/log';
our $LOGFILE = $ENV{'logfile'} || 'mail.log';
our $CONFDIR = $ENV{'confdir'} || '/etc/postfix';
our $CONFFILE = $ENV{'conffile'} || 'master.cf';
our $USE_POSTSCREEN = &checkconf( &catfile( $CONFDIR, $CONFFILE ) );
our @REASON = qw/ received
rejected_4xx
rejected_5xx
hangup /;
&main();
sub count {
my( $file, $pos ) = @_;
my( $fh, $rotated ) = &tail_open( $file, $pos );
my %table;
while( <$fh> ){
next unless index( $_, ' postfix/' ) >= 0;
if( $USE_POSTSCREEN ){
$table{hangup}++ if index( $_, ': CONNECT from' ) >= 0;
} else {
$table{hangup}++ if index( $_, ': connect from' ) >= 0;
}
next unless index( $_, ' postfix/smtpd[' ) >= 0;
if( my( $errorcode ) = m!: reject: \S+ \S+ \S+ ([45])\d{2} ! ){
$table{sprintf('rejected_%dxx', $errorcode)}++;
} elsif( my( $received ) = m!: disconnect from .*? rcpt=(\d+)[/ ]! ){
$table{hangup}--;
$table{received} += $received;
}
}
for my $reason ( @REASON ){
printf( "%s.value %d\n", $reason, $table{$reason} || 0 );
}
return &tail_close( $fh );
}
sub checkconf {
my( $file ) = @_;
open( my $fh, $file ) or die "Cannot open $file to read: $!\n";
while( <$fh> ){
next unless m/\Asmtp\s/;
if( /\s+postscreen\Z/ ){
return 1;
}
}
0;
}
sub main {
if( $ARGV[0] eq 'autoconf' ){
print "yes\n";
} elsif( $ARGV[0] eq 'config' ){
print <<EOF;
graph_title Postfix incoming messages throughput
graph_args --base 1000 -l 0
graph_vlabel mails / \${graph_period}
graph_scale no
graph_total Total
graph_category postfix
graph_period minute
EOF
for my $reason ( @REASON ){
my $label = ucfirst($reason);
$label =~ s/_/ /g;
printf( <<'TEMPLATE', $reason, $label, $reason, $reason, $reason );
%s.label %s
%s.type DERIVE
%s.draw AREASTACK
%s.min 0
TEMPLATE
}
} else {
my( $pos ) = &restore_state();
$pos = &count( &catfile( $LOGDIR, $LOGFILE ), $pos || 0 );
&save_state( $pos );
}
}
#!/usr/bin/perl
=head1 NAME
postfix_outgoing_mailstats - Plugin to monitor the number of mails
delivered by postfix
=head1 DEFAULT CONFIGURATION
[postfix_outgoing_mailstats]
env.logdir /var/log
env.logfile mail.log
=head1 Target Log Messages
=head2 # of Locally Delivered Messages
Mar 6 00:55:21 mail postfix/local[123]: XXXXXXXX: to=<[email protected]>,
orig_to=<[email protected]>, relay=local, delay=1.7, delays=1.7/0.01/0/0.01,
dsn=2.0.0, status=sent (forwarded as YYYYYYYY)
=head2 # of Remotely Delivered Messages
Mar 6 06:25:09 mail postfix/smtp[124]: XXXXXXXX: to=<[email protected]>,
orig_to=<[email protected]>, relay=mail.example.com[192.0.2.51]:25, delay=5,
delays=0/0/4.4/0.62, dsn=2.0.0, status=sent (250 ok)
=head2 # of Deferred Messages
Mar 7 14:45:15 mail postfix/smtp[125]: XXXXXXXX: to=<[email protected]>,
relay=none, delay=30, delays=0/0/30/0, dsn=4.4.1, status=deferred (connect to
mail.example.com[192.0.2.51]:25: Connection timed out)
=head2 # of Bounced Messages
Mar 7 14:58:08 mail postfix/smtp[126]: XXXXXXXX: to=<[email protected]>,
relay=mail.example.com[192.0.2.51]:25, delay=1.7, delays=0.01/0/0.83/0.84,
dsn=5.7.26, status=bounced (host relay=mail.example.com[192.0.2.51] said: 550)
=head2 # of Expired Messages
Mar 7 15:49:45 mail postfix/qmgr[127]: DDBB34008C: from=<>, status=expired,
returned to sender
=head1 AUTHOR
TSUCHIYA Masatoshi <[email protected]>
=head1 LICENSE
GPLv3
=head1 MAGIC MARKERS
=begin comment
These magic markers are used by munin-node-configure when installing
munin-node.
=end comment
#%# family=manual
#%# capabilities=autoconf
=cut
use File::Spec::Functions qw/ catfile /;
use Munin::Plugin;
use strict;
my $LOGDIR = $ENV{'logdir'} || '/var/log';
my $LOGFILE = $ENV{'logfile'} || 'mail.log';
our @REASON = qw/ sent_local
sent_remote
bounced
deferred
expired
other /;
&main();
sub count {
my( $file, $pos ) = @_;
my( $fh, $rotated ) = &tail_open( $file, $pos );
my %table;
while( <$fh> ){
next unless index( $_, ' postfix/' ) >= 0;
next unless index( $_, 'status=' ) >= 0;
if( my( $status ) = m/\s+status=(sent|bounced|deferred|expired)[,\s]/ ){
if( $status eq 'sent' ){
if( m/\s+relay=local,/ ){
$table{'sent_local'}++;
} else {
$table{'sent_remote'}++;
}
} else {
$table{$status}++;
}
} else {
$table{'other'}++;
}
}
for my $reason ( @REASON ){
printf( "%s.value %d\n", $reason, $table{$reason} || 0 );
}
return &tail_close( $fh );
}
sub main {
if( $ARGV[0] eq 'autoconf' ){
print "yes\n";
} elsif( $ARGV[0] eq 'config' ){
print <<EOF;
graph_title Postfix outgoing messages throughput
graph_args --base 1000 -l 0
graph_vlabel mails / \${graph_period}
graph_scale no
graph_total Total
graph_category postfix
graph_period minute
EOF
for my $reason ( @REASON ){
my $label = ucfirst($reason);
$label =~ s/_/ /g;
printf( <<'EOF', $reason, $label, $reason, $reason, $reason );
%s.label %s
%s.type DERIVE
%s.draw AREASTACK
%s.min 0
EOF
}
} else {
my( $pos ) = &restore_state();
$pos = &count( &catfile( $LOGDIR, $LOGFILE ), $pos || 0 );
&save_state( $pos );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment