]>
Commit | Line | Data |
---|---|---|
3decc5d1 JR |
1 | #!/usr/bin/perl -w |
2 | # -*- perl -*- | |
3 | ||
4 | =head1 NAME | |
5 | ||
6 | postfix_mailvolume - Plugin to monitor the volume of mails delivered | |
7 | by postfix. | |
8 | ||
9 | =head1 APPLICABLE SYSTEMS | |
10 | ||
11 | Any postfix. | |
12 | ||
13 | =head1 CONFIGURATION | |
14 | ||
15 | The following shows the default configuration. | |
16 | ||
17 | [postfix*] | |
2a1c8afa JR |
18 | env.logdir /var/log |
19 | env.logfile syslog | |
3decc5d1 JR |
20 | |
21 | =head1 INTERPRETATION | |
22 | ||
23 | The plugin shows the number of bytes of mail that has passed through | |
24 | the postfix installation. | |
25 | ||
26 | =head1 MAGIC MARKERS | |
27 | ||
28 | #%# family=auto | |
29 | #%# capabilities=autoconf | |
30 | ||
31 | =head1 BUGS | |
32 | ||
33 | None known | |
34 | ||
35 | =head1 VERSION | |
36 | ||
37 | v1.1 2018-03-24 | |
38 | * calculate extra field for mail volume that is actually delivered ("volume_delivered") | |
39 | ||
40 | =head1 AUTHOR | |
41 | ||
42 | Copyright (C) 2002-2008. | |
43 | ||
44 | No author is documented. | |
45 | ||
46 | =head1 LICENSE | |
47 | ||
48 | GPLv2 | |
49 | ||
50 | =cut | |
51 | ||
52 | use strict; | |
53 | use warnings; | |
54 | use Munin::Plugin; | |
55 | ||
56 | my $pos = undef; | |
57 | # the volume that was actually delivered | |
58 | my $volume_delivered = 0; | |
59 | my %volumes_per_queue_id = (); | |
60 | my $serialized_volumes_queue; | |
61 | my %expired_queue_ids = (); | |
62 | # Discard old queue IDs after a while (otherwise the state storage grows infinitely). We need to | |
63 | # store the IDs long enough for the gap between two delivery attempts. Thus multiple hours are | |
64 | # recommended. | |
65 | use constant queue_id_expiry => 6 * 3600; | |
66 | ||
3decc5d1 | 67 | sub parseLogfile { |
2a1c8afa JR |
68 | my ($start) = @_; |
69 | ||
70 | open(my $LOGFILE, '-|', "journalctl --facility=mail --after-cursor='$start' --show-cursor"); | |
71 | ||
72 | while (my $line = <$LOGFILE>) { | |
73 | chomp $line; | |
74 | ||
75 | if ($line =~ /qmgr.*: ([0-9A-Za-z]+): from=.*, size=([0-9]+)/) { | |
76 | # The line with queue ID and size may pass along multiple times (every time the mail | |
77 | # is moved into the active queue for another delivery attempt). The size should always | |
78 | # be the same. | |
79 | if (not exists($volumes_per_queue_id{$1})) { | |
80 | $volumes_per_queue_id{$1} = {timestamp => time}; | |
81 | } | |
82 | # probably it is the same value as before | |
83 | $volumes_per_queue_id{$1}->{size} = $2; | |
84 | } elsif ($line =~ / ([0-9A-Za-z]+): to=.*, status=sent /) { | |
85 | # The "sent" line is repeated for every successful delivery for each recipient. | |
86 | if (exists($volumes_per_queue_id{$1})) { | |
87 | $volume_delivered += $volumes_per_queue_id{$1}->{size}; | |
88 | $volumes_per_queue_id{$1}->{timestamp} = time; | |
89 | } | |
90 | } elsif ($line =~ /^-- cursor: (.+)$/) { | |
91 | $start = $1; | |
92 | } | |
93 | } | |
94 | close($LOGFILE); | |
95 | # remove all expired queue IDs | |
96 | my @expired_queue_ids; | |
97 | for my $key (keys %volumes_per_queue_id) { | |
98 | if (time > $volumes_per_queue_id{$key}->{timestamp} + queue_id_expiry) { | |
99 | push @expired_queue_ids, $key; | |
100 | } | |
101 | } | |
102 | delete(@volumes_per_queue_id{@expired_queue_ids}); | |
103 | return $start; | |
3decc5d1 JR |
104 | } |
105 | ||
106 | if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) { | |
2a1c8afa JR |
107 | my $logfile; |
108 | `which postconf >/dev/null 2>/dev/null`; | |
109 | if (!$?) { # if postconf returns success | |
110 | `journalctl --facility=mail --show-cursor -n 0`; | |
111 | if (!$?) { # if journalctl returns success | |
112 | print "yes\n"; | |
113 | } else { | |
114 | print "no (journalctl returned error)\n"; | |
115 | } | |
116 | } else { | |
117 | print "no (postfix not found)\n"; | |
118 | } | |
119 | ||
120 | exit 0; | |
3decc5d1 JR |
121 | } |
122 | ||
123 | ||
124 | if ( $ARGV[0] and $ARGV[0] eq "config" ) { | |
2a1c8afa JR |
125 | print "graph_title Postfix bytes throughput\n"; |
126 | print "graph_args --base 1000 -l 0\n"; | |
2a1c8afa JR |
127 | print "graph_scale yes\n"; |
128 | print "graph_category postfix\n"; | |
27030d31 JR |
129 | print "graph_vlabel bytes / \${graph_period}\n"; |
130 | print "graph_period minute\n"; | |
2a1c8afa JR |
131 | print "volume.label delivered volume\n"; |
132 | print "volume.type DERIVE\n"; | |
133 | print "volume.min 0\n"; | |
134 | exit 0; | |
3decc5d1 JR |
135 | } |
136 | ||
137 | ||
3decc5d1 JR |
138 | # load the stored data |
139 | ($pos, $volume_delivered, $serialized_volumes_queue) = restore_state(); | |
140 | ||
141 | ||
0d8dd5f2 | 142 | if (!defined($volume_delivered) || !defined($pos) || !$pos) { # they could be defined but 0 if the old plugin was run |
3decc5d1 | 143 | |
2a1c8afa JR |
144 | # No state file present. Avoid startup spike: Do not read log |
145 | # file up to now, but remember how large it is now, and next | |
146 | # time read from there. | |
3decc5d1 | 147 | |
2a1c8afa JR |
148 | my $cursor = `journalctl --facility=mail --show-cursor -n 0 | tail -n 1`; |
149 | $pos = ($cursor =~ s/^-- cursor: //r); | |
3decc5d1 | 150 | |
2a1c8afa JR |
151 | $volume_delivered = 0; |
152 | %volumes_per_queue_id = (); | |
3decc5d1 | 153 | } else { |
2a1c8afa JR |
154 | # decode the serialized hash |
155 | # source format: "$id1=$size1:$timestamp1 $id2=$size2:$timestamp2 ..." | |
156 | # The "serialized" value may be undefined, in case we just upgraded from the version before | |
157 | # 2018, since that old version stored only two fields in the state file. Tolerate this. | |
158 | for my $queue_item_descriptor (split(/ /, $serialized_volumes_queue || "")) { | |
159 | (my $queue_item_id, my $queue_item_content) = split(/=/, $queue_item_descriptor); | |
160 | (my $size, my $timestamp) = split(/:/, $queue_item_content); | |
161 | $volumes_per_queue_id{$queue_item_id} = { size => int($size), timestamp => int($timestamp) }; | |
162 | } | |
163 | $pos = parseLogfile($pos); | |
3decc5d1 JR |
164 | } |
165 | ||
166 | print "volume.value $volume_delivered\n"; | |
167 | ||
168 | # serialize the hash to a string (see "source format" above) | |
169 | $serialized_volumes_queue = join(" ", map { sprintf("%s=%s", $_, sprintf("%d:%d", $volumes_per_queue_id{$_}->{size}, $volumes_per_queue_id{$_}->{timestamp})) } keys %volumes_per_queue_id); | |
170 | save_state($pos, $volume_delivered, $serialized_volumes_queue); | |
171 | ||
172 | # vim:syntax=perl |