File: postfix_mailvolume.in

package info (click to toggle)
munin 2.0.76-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 7,064 kB
  • sloc: perl: 11,684; java: 1,924; sh: 1,632; makefile: 636; javascript: 365; python: 267
file content (184 lines) | stat: -rw-r--r-- 5,213 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!@@PERL@@ -w
# -*- perl -*-

=head1 NAME

postfix_mailvolume - Plugin to monitor the volume of mails delivered
  by postfix.

=head1 APPLICABLE SYSTEMS

Any postfix.

=head1 CONFIGURATION

The following shows the default configuration.

  [postfix*]
    env.logdir /var/log
    env.logfile syslog

=head1 INTERPRETATION

The plugin shows the number of bytes of mail that has passed through
the postfix installation.

=head1 MAGIC MARKERS

  #%# family=auto
  #%# capabilities=autoconf

=head1 BUGS

None known

=head1 VERSION

v1.1 2018-03-24
* calculate extra field for mail volume that is actually delivered ("volume_delivered")

=head1 AUTHOR

Copyright (C) 2002-2008.

No author is documented.

=head1 LICENSE

GPLv2

=cut

use strict;
use warnings;
use Munin::Plugin;

my $pos = undef;
# the volume that was actually delivered
my $volume_delivered = 0;
my %volumes_per_queue_id = ();
my $serialized_volumes_queue;
my %expired_queue_ids = ();
# Discard old queue IDs after a while (otherwise the state storage grows infinitely). We need to
# store the IDs long enough for the gap between two delivery attempts. Thus multiple hours are
# recommended.
use constant queue_id_expiry => 6 * 3600;

my $LOGDIR  = $ENV{'logdir'}  || '/var/log';
my $LOGFILE = $ENV{'logfile'} || 'syslog';


sub parseLogfile {
    my ($fname, $start) = @_;

    my ($LOGFILE, $rotated) = tail_open($fname, $start || 0);

    while (my $line = <$LOGFILE>) {
        chomp ($line);

        if ($line =~ /qmgr.*: ([0-9A-Za-z]+): from=.*, size=([0-9]+)/) {
            # The line with queue ID and size may pass along multiple times (every time the mail
            # is moved into the active queue for another delivery attempt). The size should always
            # be the same.
            if (not exists($volumes_per_queue_id{$1})) {
                $volumes_per_queue_id{$1} = {timestamp => time};
            }
            # probably it is the same value as before
            $volumes_per_queue_id{$1}->{size} = $2;
        } elsif ($line =~ / ([0-9A-Za-z]+): to=.*, status=sent /) {
            # The "sent" line is repeated for every successful delivery for each recipient.
            if (exists($volumes_per_queue_id{$1})) {
                $volume_delivered += $volumes_per_queue_id{$1}->{size};
                $volumes_per_queue_id{$1}->{timestamp} = time;
            }
        }
    }
    # remove all expired queue IDs
    my @expired_queue_ids;
    for my $key (keys %volumes_per_queue_id) {
        if (time > $volumes_per_queue_id{$key}->{timestamp} + queue_id_expiry) {
            push @expired_queue_ids, $key;
        }
    }
    delete(@volumes_per_queue_id{@expired_queue_ids});
    return tail_close($LOGFILE);
}

if ( $ARGV[0] and $ARGV[0] eq "autoconf" ) {
    my $logfile;
    `sh -c 'command -v postconf' >/dev/null`;
    if (!$?) {
        $logfile = "$LOGDIR/$LOGFILE";

        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 (postfix not found)\n";
    }

    exit 0;
}


if ( $ARGV[0] and $ARGV[0] eq "config" ) {
    print "graph_title Postfix bytes throughput\n";
    print "graph_args --base 1000 -l 0\n";
    print "graph_vlabel bytes / \${graph_period}\n";
    print "graph_scale yes\n";
    print "graph_category postfix\n";
    print "volume.label delivered volume\n";
    print "volume.type DERIVE\n";
    print "volume.min 0\n";
    exit 0;
}


my $logfile = "$LOGDIR/$LOGFILE";

if (! -f $logfile) {
    print "volume.value U\n";
    exit 0;
}

# load the stored data
($pos, $volume_delivered, $serialized_volumes_queue) = restore_state();


if (!defined($volume_delivered)) {

    # No state file present.  Avoid startup spike: Do not read log
    # file up to now, but remember how large it is now, and next
    # time read from there.

    $pos = (stat $logfile)[7]; # File size

    $volume_delivered = 0;
    %volumes_per_queue_id = ();
} else {
    # decode the serialized hash
    # source format: "$id1=$size1:$timestamp1 $id2=$size2:$timestamp2 ..."
    # The "serialized" value may be undefined, in case we just upgraded from the version before
    # 2018, since that old version stored only two fields in the state file.  Tolerate this.
    for my $queue_item_descriptor (split(/ /, $serialized_volumes_queue || "")) {
        (my $queue_item_id, my $queue_item_content) = split(/=/, $queue_item_descriptor);
        (my $size, my $timestamp) = split(/:/, $queue_item_content);
        $volumes_per_queue_id{$queue_item_id} = { size => int($size), timestamp => int($timestamp) };
    }
    $pos = parseLogfile ($logfile, $pos);
}

print "volume.value $volume_delivered\n";

# serialize the hash to a string (see "source format" above)
$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);
save_state($pos, $volume_delivered, $serialized_volumes_queue);

# vim:syntax=perl