How to create RRDtools statistics
Here’s a quick how-to on making RRDtool graphs for qpsmtpd like these.
Create the RRDtools database
rrdtool create hamspam.rrd \
--start 1156420800 \
--step 3600 \
DS:queued:GAUGE:3600:U:U \
DS:spamassassin:GAUGE:3600:U:U \
DS:geoip:GAUGE:3600:U:U \
DS:limitsenders:GAUGE:3600:U:U \
DS:check_badmailfrom:GAUGE:3600:U:U \
DS:rhsbl:GAUGE:3600:U:U \
DS:check_earlytalker:GAUGE:3600:U:U \
DS:check_badrcptto:GAUGE:3600:U:U \
DS:dnsbl:GAUGE:3600:U:U \
DS:require_res_fhost:GAUGE:3600:U:U \
DS:check_spamhelo:GAUGE:3600:U:U \
DS:check_badr_pat:GAUGE:3600:U:U \
DS:others:GAUGE:3600:U:U \
RRA:AVERAGE:0.5:1:744 RRA:MAX:0.5:1:744 \
RRA:AVERAGE:0.5:24:366 RRA:MAX:0.5:24:366
Analyse the logs (I have cron call this every hour)
#!/usr/bin/perl
use Time::Local;
use RRDs;
#print timelocal(0,0,14,24,07,106);
#print "\n";
my @date = (localtime(time));
$timestamp = timelocal(0,0,$date[2],$date[3],$date[4],$date[5]);
@date = (localtime(time - (60*60))); # one hour back
$date[5] += 1900;
$date[4]++;
$date[4] = (sprintf(”%02d”, $date[4]));
$date[3] = (sprintf(”%02d”, $date[3]));
$date[2] = (sprintf(”%02d”, $date[2]));
$date = “$date[5]-$date[4]-$date[3] $date[2]“;
print “$date\n”;
open LOG, “/bin/cat /var/log/qmail/qpsmtpd/* | /usr/local/bin/tai64nlocal |” ||
die “Couldn’t open the log file”;
my $queued = 0;
my %denied;
my @plugins = (”spamassassin”, “geoip”, “limitsenders”, “check_badmailfrom”,
“rhsbl”, “check_earlytalker”, “check_badrcptto”, “dnsbl”,
“require_resolvable_fromhost”, “check_spamhelo”, “check_badrcptto_patterns”);
my $concur = 0;
my $conns = 0;
my $denys = 0;
my %hosts;
while (
unless (/^$date/) {next;}
if (/250 Queued!/) {
$queued++;
}
elsif (/Plugin (\w+), hook \w+ returned DENY/) {
$denied{$1}++;
}
elsif (/tcpserver: pid \d+ from (.+)$/) {
$conns++;
$hosts{$1}++;
}
elsif (/tcpserver: deny/) {
$denys++;
}
elsif (/tcpserver: status: (\d+)\/\d+/) {
if ($1 > $concur) {
$concur = $1;
}
}
}
close LOG;
$unique = scalar keys %hosts;
print “Concur: $concur
Conns: $conns
Denys: $denys
Uniq: $unique”;
my @data;
push @data, $queued;
#print “Queued: $queued\n”;
foreach (@plugins) {
#print “DENY ($_): ” . ($denied{$_}?$denied{$_}:0) .”\n”;
push @data, ($denied{$_}?$denied{$_}:0);
$denied{$_} = 0;
}
my $others = 0;
foreach (keys %denied) {
if ($denied{$_} != 0) {
$others += $denied{$_};
print “Other: “.$_.”: “.$denied{$_}.”\n”;
}
}
#print “DENY (others): $others\n”;
push @data, $others;
RRDs::update (”/home/johan/rrdtool/hamspam.rrd”, “–template”,
“queued:spamassassin:geoip:limitsenders:check_badmailfrom:rhsbl:”.
“check_earlytalker:check_badrcptto:dnsbl:require_res_fhost:”.
“check_spamhelo:check_badr_pat:others”,
$timestamp . “:” . (join “:”, @data));
my $ERR=RRDs::error;
die “ERROR while updating mydemo.rrd: $ERR\n” if $ERR;
And finally, make a pretty graph:
#!/usr/local/bin/bash
/usr/local/bin/rrdtool graph /home/johan/rrdtool/hamspam.png \
--title "Ham and Spam" --height 400 --lower-limit -10 \
--end now --start end-60h \
DEF:ds0=/home/johan/rrdtool/hamspam.rrd:queued:AVERAGE:step=86400 \
DEF:ds1=/home/johan/rrdtool/hamspam.rrd:check_earlytalker:AVERAGE:step=86400 \
DEF:ds2=/home/johan/rrdtool/hamspam.rrd:spamassassin:AVERAGE:step=86400 \
DEF:ds3=/home/johan/rrdtool/hamspam.rrd:geoip:AVERAGE:step=86400 \
DEF:ds4=/home/johan/rrdtool/hamspam.rrd:check_badr_pat:AVERAGE:step=86400 \
DEF:ds5=/home/johan/rrdtool/hamspam.rrd:limitsenders:AVERAGE:step=86400 \
DEF:ds6=/home/johan/rrdtool/hamspam.rrd:check_badmailfrom:AVERAGE:step=86400 \
DEF:ds7=/home/johan/rrdtool/hamspam.rrd:rhsbl:AVERAGE:step=86400 \
DEF:ds8=/home/johan/rrdtool/hamspam.rrd:check_badrcptto:AVERAGE:step=86400 \
DEF:ds9=/home/johan/rrdtool/hamspam.rrd:dnsbl:AVERAGE:step=86400 \
DEF:ds10=/home/johan/rrdtool/hamspam.rrd:require_res_fhost:AVERAGE:step=86400 \
DEF:ds11=/home/johan/rrdtool/hamspam.rrd:check_spamhelo:AVERAGE:step=86400 \
DEF:ds12=/home/johan/rrdtool/hamspam.rrd:others:AVERAGE:step=86400 \
\
CDEF:queued=ds0,-1,* \
\
AREA:queued#000000:"Queued messages (below)" \
AREA:ds1#00ff00:Earlytalker \
STACK:ds3#ff0000:GeoIP \
STACK:ds4#ff00ff:"badrcpto pattern" \
STACK:ds2#0000ff:SpamAssassin \
STACK:ds5#00ffff:"Limit sender by host" \
STACK:ds6#ffff00:"badmailfrom" \
STACK:ds7#00bb00:"Right-hand-side blacklist" \
STACK:ds8#bb00bb:"badrcptto" \
STACK:ds9#bbbb00:"DNS blacklist" \
STACK:ds10#0000bb:"Unresolvable FROM host" \
STACK:ds11#888888:"HELO known spammer" \
STACK:ds12#44aa44:"Other reason" \
> /dev/null