Обработка log-файла почтового сервера Postfix


У многих системных администраторов, работающих с почтовыми серверами,
рано или поздно возникают различные задачи, связанные с обработкой
журнальных файлов.

Например:

1) Нужно узнать было ли когда-то в прошлом письмо с определенного почтового адреса?
2) Или нужно посмотреть статистику работы с почтой уволившегося сотрудника?
3) Получить точный размер почтовых сообщений, прошедших через SMTP-сервер.
4) Да мало ли что еще

Основные проблемы при таких задачах следующие:

1) При больших объемах почтового трафика журналы растут достаточно быстро;
2) Журналы подвергаются ежедневной ротации;
3) В журналах масса ненужной информации.

Конечно, можно подстраховаться, и архивировать все ежедневные
журналытолько вот зачем? Если возникнет задача снять статистику за год,
то нужно будет выделять нужную информацию из 365 файлов (в случае
ежедневной ротации), а нужной информации может быть всего несколько
строк.

Мною предлагается следующее решение этой проблемы: Ежедневно перед
ротацией журнального файла обрабатывать файл Perl-скриптом и помещать
нужную нам информацию в СУБД MySQL.

Система, на которой решалась эта задача:
FreeBSD 5.4, MySQL 4.1.16, Perl 5.8.6, Postfix 2.2.8, также установлен модуль: p5-DBI-1.5.

1) Создаем в СУБД MySQL базу данных maillogs с таблицей mails:

Mysql> create database maillogs;
Query OK, 1 row affected (0.11 sec)

Mysql> create table mails( month varchar(10), day char(2), time time,
ip varchar(255) not null default '', mailfrom varchar(255) not null default '',
rcptto varchar(255) not null default '', size int, primary key(month,day,time));

Query OK, 0 rows affected (0.04 sec)


Наша созданная таблица содержит 7 столбцов:

month - Месяц,
day - День,
time - Время,
ip - IP-адрес SMTP-клиента,
mailfrom - почтовый адрес отправителя,
tcptto - почтовый адрес получателя,
size - размер сообщения.


Естественно количество полей может меняться в зависимости от того, какие
задачи ставит перед собой системный администратор. Для моих задач этих
данных более чем достаточно.

2) Создаем пользователя для доступа к созданной базе:

Mysql> grant select,insert,update on maillogs.* to mailuser@localhost identified by 'mailuser';
Query OK, 0 rows affected (0.06 sec)

Mysql> flush privileges;
Query OK, 0 rows affected (0.02 sec)


3) Самое главное - создаем скрипт на Perl, который будет обрабатывать
наш журнальный файл, и помещать нужную информацию в СУБД MySQL:


#!/usr/local/bin/perl

use DBI;

$dbh = DBI->connect("DBI:mysql:host=localhost;database=maillogs","mailuser","mailuser")
or die "Нет доступа к СУБД!";
$insert = "INSERT INTO mails (month,day,time,ip,mailfrom,rcptto,size) VALUES(?,?,?,?,?,?,?)";
$sth = $dbh->prepare("$insert");

my %rec;

open(MAIL, "/var/log/maillog");
while ($line = <MAIL>)
{
my ($month, $day, $time, $hostname, $servicename, $id, $message) = split /s+/, $line, 7;
if ($id =~ /([a-z0-9]+):/i)
{
$id = $1;
$rec{$id} = {}
unless ($rec{$id});

if ($message =~ 'removed')
{
$rec{$id}->{'removed'}++;
}
else
{
while ($message =~ /(client|size|from|to)=(S+?)(s|,)/g)
{
if ($1 eq 'client') {
$rec{$id}->{'month'} = sprintf "%s", $month;
$rec{$id}->{'day'} = sprintf "%d", $day;
$rec{$id}->{'time'} = sprintf "%s", $time;
}
$rec{$id}->{$1} = $2;
}
}
}
}
close(MAIL);

foreach my $id (sort { $rec{$a}->{'time'} cmp $rec{$b}->{'time'} } keys %rec)
{
$rec{$id}->{'client'} =~ s/(.+)[(d+.d+.d+.d+)]/$2/;
$rec{$id}->{'from'} =~ s/<(.+)>/$1/;
$rec{$id}->{'to'} =~ s/<(.+)>/$1/;
if (
$rec{$id}->{'removed'}
&&
$rec{$id}->{'client'} ne '127.0.0.1'
)
{
if ($rec{$id}->{'from'} ne 'root@domen.com')
{
$sth->execute($rec{$id}->{'month'},$rec{$id}->{'day'},$rec{$id}->{'time'},$rec{$id}->{'client'},$rec{$id}->{'from'},$rec{$id}->{'to'},$rec{$id}->{'size'});
}
}
}

$sth->finish;
$dbh->disconnect;




Вот собственно и вся программа.

Принцип обработки такой:

1) Открывается журнальный файл maillog, находящийся в каталоге /var/log/;
2) В скалярную переменную $line последовательно в цикле помещается каждая строка журнала;
3) С помощью функции split строка дробится на составляющие элементы;
4) Выделение нужной информации основано на поиске Message-ID, который уникален для
каждого обрабатываемого письма;
5) Попутно исключается учет писем, обрабатываемых различными фильтрами (не ведется учет
писем, проходящих через localhost);
6) Также исключается учет писем от различных служб на ящик root@domen.com.

Программу легко переписать для собственных нужд, если Вам потребуется
дополнительная информация о письмах.

После того, как скрипт написан, необходимо в Cron внести запуск этого
скрипта за несколько минут до полуночи (т.е. до ротации).

Выполняем crontab -e, вносим следующую строчку:

58 23 * * * /root/Scripts/maillog.pl

Обновлено: 13.03.2015