ログをLTSVやJSONで保存した場合のサイズ比較

追記(2/17)

変換スクリプトを見せてほしい、という要望があったので、 https://gist.github.com/stanaka/4967403 に上げておきました。ltsvを読み込んでオプションで指定したフォーマット(デフォルト JSON)に変更します。

追記ここまで

LTSVの盛り上りも収束してきていますが、サイズに関する懸念があがっていたので、確認してみました。

手近にあったアクセスログ 186万件ほどを対象に、

  • ssv .. Combined Log Formatの拡張で、ラベルなし (レスポンス時間とか10個ぐらいのフィールドを拡張しています)
  • json .. ラベルあり
  • ltsv .. ラベルあり

の3パターンで試してみました。

まずは行数を確認しておきます。

% wc -l access_log.json
1861706 access_log.json

未圧縮だと、

access_log.ssv    818,729,505 (781M)
access_log.json 1,282,452,709 (1.2G)
access_log.ltsv 1,111,017,347 (1.1G)

でした。1G前後でそれなりのサイズのログですね。ssvとltsvの差は300M程度。jsonにするとさらに150Mぐらいでかくなってます。

最初にgz圧縮してみると、

access_log.ssv.gz  136,835,984 (131M)
access_log.json.gz 152,432,366 (146M)
access_log.ltsv.gz 146,072,000 (140M)

131M〜146Mでだいぶ差が縮まりました。1割程度の差を問題視するかどうかはポリシー次第ですが、最近では1割程度は誤差なんじゃないかと思います。

さらにbz2圧縮してみると、

access_log.ssv.bz2   93,621,212 (90M)
access_log.json.bz2 100,358,979 (96M)
access_log.ltsv.bz2  98,269,135 (94M)

と差は5%とさらに縮まりました。スペースを気にしつつ、bz2の圧縮時間に耐えられるなら、これもアリだと思います。

ついでなので、7z圧縮してみました。manの例にも載ってた推奨(?)のオプションでやってみました。

7z a -t7z -m0=lzma -mx=9 -mfb=64 -md=32m -ms=on access_log.ltsv.7z access_log.ltsv

結果は、

access_log.ssv.7z  71,518,534 (69M)
access_log.json.7z 79,827,214 (77M)
access_log.ltsv.7z 77,032,835 (74M)

という感じで、さらに小さくなりました。差はbzip2に比べると、むしろ広がってますね。

まとめ

伝統的なCombine Log Formatの拡張、JSON, LTSVで比べてみましたが、JSON, LTSVともに圧縮すれば1割程度の増加で済むので、JSONにせよLTSVにせよ、気にせずにラベルを付けて、使い勝手重視で行くのがよいんじゃないでしょうか。

Labeled Tab Separated Values (LTSV) ノススメ

追記(2/8 11:30)

追記ここまで

Labeled Tab Separated Values (LTSV) というのは、はてなで使っているログフォーマットのことで、広く使われているTSV(Tab Separated Value)フォーマットにラベルを付けて扱い易くしたものです。はてなでは、もう3年以上、このフォーマットでログを残していて、one-linerからfluentd、Apache Hiveまで幅広く便利に使えています。

ログフォーマットに期待されることは、

  • フォーマットが統一されている → 共通のツールで集計し易い
  • 新しいフィールドの追加が容易 → サービス固有のデータをログに残したいことはよくある。かつ、複数サービスに渡って、別々のエンジニアが触っても混乱しない
  • one linerで操作しやすい → grepした後でsortなり、uniqなりで集計するのはよくあること

の3点だと考えています。

これまでにも、ログフォーマットを扱い易くする取り組みとして、SyslogでもRFC5425で構造化の仕様が入ったり*1していますが、いまいち普及しているとは言えません。Apache標準のCombined Log Formatを独自拡張して頑張る、というのがよくある光景なんじゃないかと思います。ただ、サービスごとの要求に応じて、どんどん独自拡張していると個別にParserの定義を書かないといけなくなったり、fluentdで扱う際にも頑張ってサービスごとの定義を正規表現で書かないといけない羽目になって、どんどん辛くなります。

Labeled Tab Separated Values (LTSV) とは

そこではてなでは、3年ほど前*2からLabeled Tab Separated Values (LTSV) によるログフォーマットを全面的に採用しています。

Labeled Tab Separated Valueは、

time:[28/Feb/2013:12:00:00 +0900]	host:192.168.0.1	req:GET /list HTTP/1.1	status:200	size:5316	referer:- ua:Mozilla/5.0	taken:9789	isrobot:1	dos:-	harddos:-	cache:-

というもので、各フィールドがタブで区切られ、フィールドの頭にラベルを付けています。

ApacheのLogFormat定義は

LogFormat "host:%h\ttime:%t\treq:%r\tstatus:%>s\tsize:%b\treferer:%{Referer}i\tua:%{User-Agent}i\ttaken:%D\tisbot:%{Isbot}e\tdos:%{SuspectDoS}e\tharddos:%{SuspectHardDoS}ecache:%{X-Cache}o" ltsv

となり、nginxでは

log_format tsv "time:$time_local\thost:$remote_addr\treq:$request\tstatus:$status\tsize:$body_bytes_sent\treferer:$http_referer\tua:$http_user_agent\treqtime:$request_time\tcache:$upstream_http_x_cache";

となります。

このLabeled Tab Separeted Valueフォーマットのメリットは、まずログを定義する時に各フィールドの順序を意識しなくてよく、各プロダクトで容易に独自拡張できる、ということにあります。上の例では、X-Cacheヘッダやmod_dosdetectorの判定結果を残したりしています。はてなでの運用では、ラベルをWikiで管理しており、新しいラベルを作る際には記録してもらうようにしています。

Labeled Tab Separated Valueを扱う

LTSVはTSVの拡張ですので、cutコマンドで簡単に切り分けられます。例えば、ステータスコードごとに数えるとすると以下のようになります。

% tail -n 100 /var/log/httpd/access_log.ltsv | cut -f 5 | sort | uniq -c
     97 status:200
      2 status:301
      1 status:404

ただ、これだけだと、ラベルが出て、ちょっと分かり易いぐらいですかね。

では、次にfluentd pluginを見てみます。コードは、https://github.com/stanaka/fluent-plugin-tail-labeled-tsvにあります(近々Gem化します)。fluentdでの定義は、

<source>
  type tail_labeled_tsv
  path /var/log/nginx/access_log.ltsv
  tag access_log
  pos_file /tmp/fluent.log.pos
</source>

のようになります。in_tail pluginのように非標準のフォーマットでも頑張って定義を正規表現で書く必要がありません。Webサーバー側での定義を変更してもfluentd側の設定を触らずにすむのも便利です。

さらにApache HadoopのHive用のSerDe(シリアライザ・デシリアライザ)も用意しています。コードは、 https://github.com/hatena/KeyValuePairsDeserializer にあります。

これにより、Amazon EMR上のHiveで簡単にテーブルを定義することができます。

ADD JAR s3://sample/_lib/hadoop/hive/serde/hatena-serde.jar;

CREATE EXTERNAL TABLE logs (
  time string, host string,
  req string, status string, size string,
  referer string, ua string
) 
PARTITIONED BY (dt string)
ROW FORMAT SERDE 'jp.ne.hatena.hadoop.hive.serde.KeyValuePairsDeserializer';

ALTER TABLE sample ADD PARTITION (dt='2013-01-01') LOCATION 's3://sample/sample_log.ltsv.gz';

その後、Hiveにより、MapReduceでログ解析を行うことができます。例えば、

select count(distinct referer) from logs

のようなHiveQLを流すことで、1年分のログでも、がりっと処理することができます。

まとめ

はてなでは、Labeled Tab Separated Valueによるログフォーマットで統一していて、one-linerからfluentd、Amazon EMR上のHiveまで使えて便利!という話でした。

*1:ところで、Parser書いている人もいますね。 http://blog.kentarok.org/entry/20101217/1292606627

*2:確認すると2009年10月頃からでした

Kindle Paperwhite向けに自炊pdfを最適化する

id:halfrackKindle Paperwhiteのタワーを作っていたので、Kindle Paperwhite向け自炊pdfの最適化をしてみました。
f:id:halfrack:20121011205048j:image:w200

Kindle Paperwhite向けの自炊pdf最適化は、

  • 余白を適切に削除すること
  • 画像を最適なサイズである横658ドット x 縦905ドットにすること

の2つが大事です。特に後者が重要で、デバイスごとに最適なサイズにすることでpixel by pixelで表示することができます。ちなみに、これまでのKindleでは横560ドット x 縦735ドットが最適でした*1

調査には1ピクセルごとに線を入れたpdfを作成して行いました。このpdfでは、ページごとに画像サイズを1ピクセルずつ変えていて、最適な解像度ではない場合、綺麗に一様な模様にならず、すぐに分かります。これでちょっとずつ探りながら最適なサイズを探すわけです。地道ですね。実際に試したい場合は、使ったpdfをkindle_paperwhite_resolution.pdfに置きましたのでどうぞ。作成スクリプトは https://gist.github.com/3892964 にあります。

Kindle Paperwhite向けに最適化されたのがどれぐらい綺麗かというと、
f:id:stanaka:20121016002842j:image:w200 f:id:stanaka:20121016002841j:image:w200
ぐらい違います。いずれも元pdfは同じで、左がKindle4(最適化済)、右がKindle Paperwhite(最適化済)です。「聖職」に注目すると、文字の細かい線がKindle Paperwhiteのほうがはっきりしていますね。

次は最適化するためのツールが欲しい、ということで、Kindle向けのpdf最適化を行うツールの中から Kindlizer( https://github.com/tdtds/kindlizer )からforkして、https://github.com/stanaka/kindlizer で手を入れてKindle Paperwhite向けにできるようにしました。ただただしさん、ありがとうございます!

Kindlizerの解説や詳しい使い方は、 http://d.hatena.ne.jp/kamosawa/20111116 にあります。
Macでは依存ツールはbrewでざくざく入ります。

brew install sam2p
brew install imagemagick
brew install pdftk
brew install poppler --with-glib

popplerはオプション無しではビルドに失敗した(OSX 10.7.4, XCode 4.5.1)のですが、"--with-glib"をつけたら成功しました。rakeは既に入っていますよね。当然です。

fork版はオリジナルからは以下の変更を加えています。

  • Rakefileをハードコードされていた元pdf(SRC)やマージン(TOP, BOTTOM, LEFT, RIGHT)、サイズ(SIZE)、階調(LEVEL)を引数で指定可能にした
  • 元pdfのファイル名が空白などを含んでいても処理可能にした
  • sample.pdf → sample.out.pdf と変更されていたファイル名のout部分を変更可能にした(POSTFIX)
  • i文庫向けに"[著者名] タイトル.pdf"というルールのファイル名を"タイトル.out.pdf"にしつつ、AUTHORのメタデータを書き換えるようなオプション(UPDATE_METADATA)を新設した

これを使って、Kindle Paperwhite向けの最適化を実行するには、元ファイルをsample.pdfとして用意して、

rake SIZE="658x905"

と一発でいけます。

複数ファイルを対象としたい( & 美白化したい & マージンの調整をしたい & 著者名をメタデータに入れたい)時も

for i in ~/book/*;do rake SRC=$i LEVEL='0%,93%,0.7' TOP=100 UPDATE_METADATA=1 POSTFIX=kindle; rake clean SRC=$i; done

ワンライナーで一発でいけます。

では、快適なKindle Paperwhiteライフを!

Kindle Paperwhite

Kindle Paperwhite


Kindle Paperwhite 3G

Kindle Paperwhite 3G

追記(10/16 9:52)

コメントでjpegをzipしたものなら、リサイズもまともですよ!ということなので、Kindle4で試してみましたが、コントラストの調整ができなくて、ちょっと厳しそう。ページ数が出ないのもナビゲーション的に厳しいですね。あと、Paperwhiteではjpegのzipファイルは認識してくれなかったので、サポート外になったのかもしれません。(同じzipでKindle4では見れました)
f:id:stanaka:20121016095141j:image:w200

追記 2(10/16 10:17)

Kindle Paperwhiteでのスクリーンショットの撮り方は、

All you have to do to take a screenshot is tap the upper left corner and lower right corner at the same time. Or you can tap the upper right corner and lower left corner at the same time.

http://blog.the-ebook-reader.com/2012/10/08/kindle-paperwhite-tips-how-to-take-screenshots-and-remove-recommended-ebooks/

ということで、右上と左下を同時にタップ、もしくは左上と右下を同時にタップで、ルートディレクトリにPNGファイルが生成されます。

*1:参考: http://non-non-chan.blogspot.jp/2010/10/kindle-3-2.html

データベースのモダンな使い方を知りたい人のための「Webエンジニアのためのデータベース技術[実践]入門」

「Webエンジニアのためのデータベース技術実践入門」を献本いただきました。著者の松信さんはMySQLの専門家で、これまでも数々の良書を執筆されています。ここ1,2年でデータベースの設計や運用の考え方は大きく進化したのですが、そのあたりが分かり易く解説されており、期待を裏切らない良本でした。

Webエンジニアのための データベース技術[実践]入門 (Software Design plus)

Webエンジニアのための データベース技術[実践]入門 (Software Design plus)

目次は松信さんのブログエントリにありますが、データベース技術の背景にある考え方から、具体的な概念、MySQLでの実践的な運用、データベースに適したハードウェアの特性まで記述されており、Webアプリケーションエンジニアはこの一冊を読んでおけば、必要な知識の相当部分が得られます。

データベースの設計や運用の考え方は、ここ1、2年でのハード性能の大幅な向上やクラウドの普及など外部環境の変化により、前提条件が大幅に変化しています。また、MySQLのバージョンアップにより、パーティションテーブルが実用的になり、スキーマ設計の考え方も進歩しています。実際、現場で新人に教える時にもこれまでのMySQL関連本では、内容が古くなっていると感じるところが多々あります。

松信さんの「Webエンジニアのためのデータベース技術実践入門」で特に良いなと思ったのは、

  • SQLやインデックスなどのデータベース設計に必要な知識
  • SSDによるIO性能の向上による効果や、その性能の引出し方
  • ウェブサービスの現場で使われている運用の考え方
  • リレーショナルデータベース以外のデータベースの概略や利点

あたりについて分かり易く書かれているところです。ウェブアプリケーションエンジニアがモダンなデータベースについて深く知りたくなった時に、スムーズに全体を把握することができそうです。ちなみに、中でもMySQLの内部構造やソースコードの読み方まで書かているのは、松信さんの真骨頂でなかなか他では読めないので、非常に面白かったです。

今週金曜(3/9)発売とのことですので、データベースについてもう一歩知りたいエンジニアの方にお勧めします。