アクセスログから指定範囲のIPアドレスを含む行を抜き出す

アクセスログから、指定範囲のIPアドレスを含む行を抜き出す。ログはまあまあのファイルサイズがあり、処理はできるだけ速く完了したい。そこで、幾つかの方法を試してみた。

例として、Webサーバのhttpdアクセスログを使う。ログのサイズは約19MBで、72733行ある。IPアドレスの範囲はCIDRで記述する。ここでは、抜き出すIPアドレスの範囲として、東京大学IPアドレスを用いる。https://www.nc.u-tokyo.ac.jp/riyou/192.51.htmlによれば、IPアドレス範囲は130.69.0.0/16133.11.0.0/16157.82.0.0/16である。

Perl

Net::IP::Match::Regexpが、速さをアピールしていて良さそうだ。


Python

PyPIを検索して見つけた、ipacalcnetaddrを使うことにした。


Haskell

Data.IPが使える。このモジュールはiprouteというパッケージに含まれているので、cabalでインストールした。


比較

$ wc -l /var/log/lighttpd/access.log*
    4141 /var/log/lighttpd/access.log
    4688 /var/log/lighttpd/access.log.1
    5019 /var/log/lighttpd/access.log.10
    6265 /var/log/lighttpd/access.log.11
    8291 /var/log/lighttpd/access.log.12
    5657 /var/log/lighttpd/access.log.2
    4850 /var/log/lighttpd/access.log.3
    4626 /var/log/lighttpd/access.log.4
    4963 /var/log/lighttpd/access.log.5
    6795 /var/log/lighttpd/access.log.6
    4580 /var/log/lighttpd/access.log.7
    5804 /var/log/lighttpd/access.log.8
    7054 /var/log/lighttpd/access.log.9
   72733 total
$ time cat /var/log/lighttpd/access.log* | perl ipfilter.pl | wc -l
     612

real    0m2.355s
user    0m2.306s
sys     0m0.036s

$ time cat /var/log/lighttpd/access.log* | python ipfilter_ipcalc.py | wc -l
     612

real    0m6.868s
user    0m6.782s
sys     0m0.077s

$ time cat /var/log/lighttpd/access.log* | python ipfilter_netaddr.py | wc -l
     612

real    0m3.932s
user    0m3.861s
sys     0m0.062s

$ time cat /var/log/lighttpd/access.log* | runhaskell ipfilter.hs | wc -l
     612

real    0m3.706s
user    0m3.599s
sys     0m0.101s

ipcalc/Pythonが最も遅い。どうやら、IPアドレス範囲に含まれているかのチェック:ipcalc.Network.in_network(other)で、long(address)を何度も呼び出しているせいのようだ。

iproute/HaskellIPアドレス群の管理にTRIEを使っており、そこそこ早い。コンパイルしたらもっと速くなるかもしれない。

それにしても、Net::IP::Match::Regexp/Perlの速さには驚いた。さすが、テキスト処理のプロ。