メールログを解析する簡易 Perl スクリプト

あなたの作ったメール配信システムはエラーメール処理をしていますか?」 という記事が結構よく読まれています。最近は本業の方でもメール未達について調べて欲しいとかいろいろ頼まれた経緯もあり、そのときにでっちあげたスクリプトを晒しておきます。誰かの役にたちそうだなぁ〜と思いまして。

ちなみにメールログの形式は qmail のものでしか試したことがないので各メールサーバにあわせて若干の改変は必要かもしれませんがあしからず・・・。

- スポンサーリンク -

メールログの中から特定のメールアドレスの配信状況を調べるスクリプト

使い方

perl  maillogcheck.pl  メールログファイル  メールアドレス(正規表現使えます)

実行結果例(※解析結果の変数がダンプされます)

OK:$VAR1 = {
  '36224868' => {
    'drk@****.jp' => '2008-03-03 19:30:20.173540500 delivery 36224868: success: 127.0.0.1_accepted_message./Remote_host_said:_250_ok_dirdel/'
  }
},
NG:$VAR1 = {
  '36160144' => {
    'drk@****.jp' => '2008-02-29 19:36:15.990414500 delivery 36160144: failure: 127.0.0.1_failed_after_I_sent_the_message./Remote_host_said:_554_delivery_error:_dd_This_user_doesn\'t_have_a_****.jp_account_(drk@****.jp)_[-5]_-_mta130.mail.****.jp/'
  }
};

ソースコード(※短時間で作ったでっちあげなので綺麗なソースではありませんが・・・。)

use strict;
use warnings;
use Data::Dumper;

my $infile = $ARGV[0] or die qq{usage: perl maillogcheck.pl maillogfile mailaddr\n};
my $inmail = $ARGV[1] or die qq{usage: perl maillogcheck.pl maillogfile mailaddr\n};

## メールログファイルの解析
my %err_maillist;    ## エラーメールリストにする変数
my %ok_maillist;     ## 配信成功メールリストにする変数
my %all_maillist;    ## ログファイルにあるすべてのメールアドレスのリストにする変数
open my $fh, '<', $infile or die "open err: $infile";

## deliveryid の解析
while (<$fh>) {
    chomp;
    if (m!starting delivery (\d+): msg \d+ to remote (.+)$!) {
        my ( $deliveryid, $mail ) = ( $1, $2 );
        $all_maillist{$deliveryid} = $mail if ( $mail =~ $inmail );
    }
}
## 配信状況の解析
seek( $fh, 0, 0 );
while (<$fh>) {
    chomp;
    ## 成功
    if (m!delivery (\d+):.+(said:_250)!) {
        my $deliveryid = $1;
        $ok_maillist{$deliveryid}->{ $all_maillist{$deliveryid} } = $_ if ( $all_maillist{$deliveryid} );
    }
    ## 失敗
    elsif (m!delivery (\d+)!) {
        my $deliveryid = $1;
        $err_maillist{$deliveryid}->{ $all_maillist{$deliveryid} } = $_ if ( $all_maillist{$deliveryid} );
    }
}
close $fh;

## 再送で配信できてた人はNGリストから削除する
delete $err_maillist{$_} for ( keys %ok_maillist );

print "OK:", Dumper( \%ok_maillist );
print "NG:", Dumper( \%err_maillist );


メールログから配信状況のサマリーを作成するスクリプト

使い方

perl  maillogstat.pl  メールログファイル

実行結果例(※ステータス別に件数とメールアドレス一覧が出力されます)

250_ok:208021
  aaaa@****.jp
  ....
said:_550:2317
  bbbb@****.jp
  ....
said:_554:2218
  cccc@****.jp
  ....
said:_host_not_found:1385
  dddd@****.jp

ソースコード(※短時間で作ったでっちあげなので綺麗なソースではありませんが・・・。)

use strict;
use warnings;

my $infile = $ARGV[0] or die qq{usage: perl maillogstat.pl maillogfile\n};

## メールログファイルの解析
my %err_maillist;    ## エラーメールリストにする変数
my %ok_maillist;     ## 配信成功メールリストにする変数
my %all_maillist;    ## ログファイルにあるすべてのメールアドレスのリストにする変数

## エラーコードと絞込み条件ハッシュ変数
my %errors = (
    'said:_421' => qq{.*},
    'said:_450' => qq{.*},
    'said:_451' => qq{.*},
    'said:_452' => qq{.*},
    'said:_500' => qq{.*},
    'said:_501' => qq{.*},
    'said:_502' => qq{.*},
    'said:_503' => qq{.*},
    'said:_504' => qq{.*},
    'said:_511' => qq{.*},
    'said:_512' => qq{.*},
    'said:_530' => qq{.*},
    'said:_550' => qq{unknown|such|invalid|inactive|unavailable},
    'said:_551' => qq{.*},
    'said:_553' => qq{unknown|such|invalid|inactive|unavailable},
    'said:_554' => qq{.*},
);
open my $fh, '<', $infile or die "open err: $infile";

## deliveryid の解析
while (<$fh>) {
    chomp;
    if (m!starting delivery (\d+): msg \d+ to remote (.+)$!) {
        my ( $deliveryid, $mail ) = ( $1, $2 );
        $all_maillist{$deliveryid} = $mail;
    }
}
## 配信状況の解析
seek( $fh, 0, 0 );
while (<$fh>) {
    chomp;
    ## 成功
    if (m!delivery (\d+):.+(said:_250)!) {
        my $deliveryid = $1;
        $ok_maillist{ $all_maillist{$deliveryid} } = "said:_250_ok" if ( $all_maillist{$deliveryid} );
    }
    ## 不明ドメインエラーレコードの解析
    elsif (m!delivery (\d+):.+_find_any_host_named_!) {
        my $deliveryid = $1;
        $err_maillist{ $all_maillist{$deliveryid} } = 'said:_host_not_found' if ( $all_maillist{$deliveryid} );
    }
    ## 550番のsmtpエラーレコードの解析
    elsif (m!delivery (\d+):.+(said:_5\d{2})!) {
        my ( $deliveryid, $errcode ) = ( $1, $2 );
        if ( defined $errors{$errcode} && m!$errors{$errcode}!i ) {
            $err_maillist{ $all_maillist{$deliveryid} } = $errcode if ( $all_maillist{$deliveryid} );
        }
    }
}
close $fh;

## 結局配信できてた人はNGリストから削除する
delete $err_maillist{$_} for ( keys %ok_maillist );

## OKアドレスの一覧
my $num = scalar( keys %ok_maillist );
print qq{250_ok:$num\n};
print qq{  $_\n} for ( keys %ok_maillist );

## NGアドレスの一覧
my %result;
push @{ $result{ $err_maillist{$_} } }, $_ for ( keys %err_maillist );
for ( keys %result ) {
    my $num = scalar @{ $result{$_} };
    print qq{$_:$num\n};
    print qq{  $_\n} for ( @{ $result{$_} } );
}


いずれも自前の処理にあわせてスクリプトを加工して使うと使いやすいかと思います。

- スポンサーリンク -