ログは1行1イベントに

何を言っとるんだお前はという感じであるが、つまりこういう事である。

通常、ログは1行が1イベントである。
たとえばapacheのログやらなにやら、みんなそうである。
少なくともUNIX系のシステムであれば、これは常識である。
しかし、この世の中、UNIX系の常識が通用しないログだって山ほどあるのである。

1イベントが複数行にわたるログがなぜいけないか。

たとえば以下の様な擬似ログを考えてみよう。

まったく関係のない話だが、以下のデータは手元にあるLIFEのノートから適当にでっち上げた。
冒頭にタイムスタンプがあり、イベントの内容が記される、典型的なログである。

2013/12/16 17:55:05.6582 N28 [499016804864] Noble note section B6 5m/m

しかしこれが、以下のように複数行にわたって記録(あるいは表示)される、こういうログを相手にすることだってあるのだ。

2013/12/16 17:55:05.6582 N28
4990 1680
4864
Noble note section
B6 5m/m

これは主に可読性を意識したせいであろうが、こういったログをHadoopで扱うのは難しい。

Hadoopのデータの扱い方

というのも、Hadoopは分散処理のためにログを分割するからだ(デフォルトでは64MBごと)。

しかもその分割は、単純にサイズのみで判断され、文脈は考慮されない。
上のログで言えば、Nobleの手前で切られてしまってログとして意味がなくなってしまうことだってある。

これを避けるには、Hadoopにファイルそのものではなくて、ファイルリストを与える手があるけれども、それではHadoopの長所を活かせない。
ファイルリストは綺麗に分割されるけど、ファイルの大きさはまちまちだから。

ログの整形

というわけで、こういったログを扱う前に、下準備として1行1イベントにまとめてしまおう。

まとめ自体もHadoopで処理してしまえば楽である。
Hadoopの象本Appendix Cに良い例があるのでこれを使う。
ここでfiles.txtは処理するログファイルをリストしたものとする。
また、concat.shは1行1イベントにまとめるスクリプトとする。

$ hadoop jar $HADOOP_INSTALL/contrib/streaming/hadoop-*-streaming.jar \
-D mapred.reduce.tasks=0 \
-D mapred.map.tasks.speculative.execution=false \
-D mapred.task.timeout=12000000 \
-input files.txt \
-inputformat org.apache.hadoop.mapred.lib.NLineInputFormat \
-output output \
-mapper concat.sh \
-file concat.sh

reduceは必要ないのでmapred.reduce.tasks=0。
重複して書き込んでほしくないのでmapred.map.tasks.speculative.execution=false。
タイムアウトは長めに。
mapが一回に処理するファイルはひとつにしたいので、-inputformat org.apache.hadoop.mapred.lib.NLineInputFormat 。

以上