アプリケーションログをワンライナーでフィルタリング

ログのフィルタだったら grep でいいじゃん?となるかもしれないけど、例外スタックトレースなどの改行が含まれるログの場合 grep だと肝心な部分が抜け落ちてしまう。

ということで、改行が含まれたログもワンライナーでフィルタリングしてみる。

フィルタに使用するコマンド

前に書いたイメージで awk を利用する。
たとえば " ERROR " をキーワードにフィルタリングする場合…

awk '/ ERROR /{f=1;print;next;} !/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/{if(f==1){print;next;}} {f=0}'
/ ERROR /
表示したいログのキーワードを見つけたら
{f=1;print;next;}
フラグ立てて、出力して、次の行を読む
!/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/
先頭が yyyy-mm-dd 形式(次のログを表すフォーマット)でなければ*1
{if(f==1)
フラグが立っていれば(表示したいログの続きだったら)
print;next;}}
行頭の空白を削除して、出力して、次の行を読む
{f=0}
いずれにもマッチしなかったらフラグを倒しておく

あとは、awk に直接ファイルを渡す

awk '/ ERROR /{f=1;print;next;} !/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/{if(f==1){print;next;}} {f=0}'  foo.log > foo_error.log

なり、tail の結果をパイプで渡す

tail -f foo.log | awk '/ ERROR /{f=1;print;next;} !/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/{if(f==1){print;next;}} {f=0}'

なりする。

もっと手軽に利用する

手軽に利用できるようにするのであれば、awk の部分を alias として登録しておく。
bash であれば、

function logfilter_func() {
    awk "/$1/{f=1;print;next;} !/^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/{if(f==1){print;next;}} {f=0}"
}
alias logfilter='logfilter_func'

を、~/.bashrc あたりに書いて*2

logfilter " ERROR " < foo.log > foo_error.log
tail -f foo.log | logfilter " ERROR "

といった感じ。

*1:行内にこのパターンを見つけたら次のログ」ということが判断できるパターンを書く

*2:bash の場合、alias 内での引数展開が上手くできないので function を利用する必要がある