ApacheのアクセスログをElasticsearchに放り投げたいなどの時に、普通はFluentdを使うものだと思いますが中には既存のログを取り込みたい時などもあると思います。
そんなわけでRubyでApacheのアクセスログをJSONに変換するスクリプトを作ってみました。
想定するログのフォーマットはCombined形式の先頭にX-Forwarded-Forを追加した、以下のような形式です。
LogFormat “%{X-Forwarded-For}i %h %l %u %t “%r” %>s %b “%{Referer}i” “%{User-agent}i”” combined
ruby access_log_to_json.rb -i some.log -o some.log.json
のように使います。
123.45.67.89 10.20.30.40 - - [20/Apr/2014:03:15:35 +0900] "GET /index.html HTTP/1.1" 200 159 "https://www.google.co.jp/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36"
123.45.67.89 10.20.30.40 - - [20/Apr/2014:03:15:40 +0900] "GET /best_picture_ever.jpg HTTP/1.1" 200 534000 "http://www.exemple.com/index.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36"
123.45.67.89 10.20.30.40 - - [20/Apr/2014:03:15:45 +0900] "GET /greatest_music_ever.mp3 HTTP/1.1" 200 19478980 "http://www.exemple.com/index.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36"
のようなログが
{"xforward_for":"123.45.67.89","ip":"10.20.30.40","remotelog":"-","remoteuser":"-","time":"2014-04-20 03:15:35 +0900","request":"GET /index.html HTTP/1.1","status":"200","bytes":159,"referer":"https://www.google.co.jp/","ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36"}
{"xforward_for":"123.45.67.89","ip":"10.20.30.40","remotelog":"-","remoteuser":"-","time":"2014-04-20 03:15:40 +0900","request":"GET /best_picture_ever.jpg HTTP/1.1","status":"200","bytes":534000,"referer":"http://www.exemple.com/index.html","ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36"}
{"xforward_for":"123.45.67.89","ip":"10.20.30.40","remotelog":"-","remoteuser":"-","time":"2014-04-20 03:15:45 +0900","request":"GET /greatest_music_ever.mp3 HTTP/1.1","status":"200","bytes":19478980,"referer":"http://www.exemple.com/index.html","ua":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36"}
のように変換されます。ポイントを何点か。
X-Forwarded-Forはプロキシ経由だと123.45.67.89, 234.56.78.90のようにカンマ空白区切りで2つのIPアドレスが記録されることがあります。その場合の判定処理を追加してます。
Apacheの時刻形式([20/Apr/2014:03:13:24 +0900]などのような)はTime.parseでもDateTime.parseでもパースできず面倒くさいなあ…と思ったのですが日付の後の:を空白に換えたらあっさりパースできました。
また今回はJSONに変換していますが、ApacheLogクラスを使って他にも何か応用できるかもですね。
それはそうとStructちょう便利ですよね!!
SomeClass = Struct.new :some_attr, :another_attr do
def greeting
"Hello!"
end
end
foobar = SomeClass.new "foo", "bar"
foobar.some_attr # => "foo"
foobar.another_attr # => "bar"
foobar.greeting # => "Hello!"