SREチームの@cubicdaiyaです。
今回はlltsvというツールを利用してLTSV形式のデータを処理する術について解説します。
LTSV
LTSVはLabeled Tab-separated Valuesの略で、コロンで区切られたラベルと値の組み合わせ(key:value)をタブ区切りで表現したフォーマットです。
主にログデータのフォーマットとしての利用が想定されています。
uri:/upload status:400 size:13599 reqtime:0.280 apptime:0.150 uri:/downalod status:200 size:12812 reqtime:0.330 apptime:0.210 uri:/item/new status:200 size:29830 reqtime:0.050 apptime:0.050 uri:/item/fav status:200 size:33123 reqtime:0.100 apptime:0.099 uri:/top status:301 size:1256 reqtime:0.020 apptime:0.020
このようにLTSVは非常にシンプルなフォーマットなのでデータの定義や拡張が容易にできます。
例えば、nginxでログデータをLTSV形式で出力するには以下のようにログフォーマットを定義します。
log_format ltsv 'uri:$uri\t' 'status:$status\t' 'size:$body_bytes_sent\t' 'reqtime:$request_time\t' 'apptime:$upstream_response_time';
メルカリでは先述にもあるようにデータの定義や拡張が容易で、メンテナンスもしやすいことからnginxやApache等のHTTPサーバのログフォーマットにLTSVを利用しています。
また、ログデータの転送に利用しているFluentdやfluent-agent-hydraがサポートしているのも理由の一つです。メルカリでのログデータの処理についてはWEB+DB PRESS vol.97でもう少し詳しく解説しているので興味がある方はご覧下さい。
lltsv
lltsvはLTSV形式のデータの加工やフィルタリングができるツールです。普段からよく利用しているのですが、
何度かパッチを書いて送っていたらコラボレータに追加してもらえたので今でも時々機能を追加したりしています。
それでは、さきほど紹介した5行のLTSVフォーマットのデータをlltsvで処理してみましょう。(ファイル名をsample.ltsvにして保存します)
uri:/upload status:400 size:13599 reqtime:0.280 apptime:0.150 uri:/downalod status:200 size:12812 reqtime:0.330 apptime:0.210 uri:/item/new status:200 size:29830 reqtime:0.050 apptime:0.050 uri:/item/fav status:200 size:33123 reqtime:0.100 apptime:0.099 uri:/top status:301 size:1256 reqtime:0.020 apptime:0.020
特定のラベルにマッチするレコードだけを抜き出したり、
$ lltsv -k uri,reqtime,apptime sample.ltsv uri:/upload reqtime:0.280 apptime:0.150 uri:/download reqtime:0.330 apptime:0.210 uri:/item/new reqtime:0.050 apptime:0.050 uri:/item/fav reqtime:0.100 apptime:0.099 uri:/top reqtime:0.020 apptime:0.020
あるいは省くこともできます。
$ lltsv -i reqtime,apptime sample.ltsv uri:/upload status:400 size:13599 uri:/download status:200 size:12812 uri:/item/new status:200 size:29830 uri:/item/fav status:200 size:33123 uri:/top status:301 size:1256
デフォルトの動作だとラベルと値を一緒に出力しますが、集計処理をするような場合だと特定のラベルの値だけを出力したいこともあるでしょう。
そういう場合は-Kを指定します。
$ lltsv -k reqtime -K sample.ltsv 0.280 0.330 0.050 0.100 0.020
また、tail等のコマンドの出力をパイプで渡すこともできます。
$ tail -f /var/log/nginx/access.log | lltsv
フィルタ機能
次にlltsvのフィルタ機能を利用して特定のラベルの値にマッチしたレコードだけを抜き出してみましょう。
以下ではHTTPステータスコードが301のレコードだけを抜き出しています。
$ lltsv -f 'status == 301' sample.ltsv uri:/top status:301 size:1256 reqtime:0.020 apptime:0.020
続いてアップストリームサーバのレスポンスタイムが大きいレコード(100ms以上)を抜き出します。
$ lltsv -f 'apptime > 0.100' sample.ltsv uri:/upload status:400 size:13599 reqtime:0.280 apptime:0.150 uri:/download status:200 size:12812 reqtime:0.330 apptime:0.210
ちなみに、reqtimeとapptimeというラベル名はそれぞれ以下の意味を持っており、
- reqtime: サーバがリクエストを処理するのにかかった時間
- apptime: アップストリーム(プロキシ先)のサーバがレスポンスを返すのにかかった時間
これらはltsv.orgに掲載されている、WebサーバがWebサーバのログフォーマットにLTSVを利用する際に推奨されるラベル名のリストを元にしています。
また、正規表現も利用可能です。(Goのregexpパッケージを利用しています)
$ lltsv -f 'uri =~ ^/item/' sample.ltsv uri:/item/new status:200 size:29830 reqtime:0.050 apptime:0.050 uri:/item/fav status:200 size:33123 reqtime:0.100 apptime:0.099
lltsvで利用可能な比較演算子は以下になります。(末尾が*の演算子は大文字と小文字を区別しない(Case-insensitive)ことを表しています)
>= > == < <= 算術演算による比較 == ==* != !=* 文字列による比較 =~ !~ =~* !~* 正規表現による比較
フィルタ機能の制限
lltsvでは-fでフィルタを追加する際、ラベル名と演算子と値はスペースで区切られている必要がある点に注意しましょう。
例えば、以下のフィルタは正しく動作しますが、
-f 'status == 200'
スペースを削除して実行するとfilter expression is invalid: status==200と出力されてエラーになります。
-f 'status==200'
また、同一ラベルに対して複数のフィルタを指定した場合、最後に指定したフィルタのみが適用されます。
$ lltsv -f 'status == 200' -f 'status == 301' sample.ltsv uri:/top status:301 size:1256 reqtime:0.020 apptime:0.020
同一ラベルに対して複数のフィルタを適用する方法は今のところ提供されていません。
式評価機能
lltsvはさらに式評価機能を備えており、これを利用して新しいラベルと値を動的に生成したり、既存ラベルの値を上書きすることができます。
例えば、reqtimeとapptimeの差分を表すdiffというラベルを追加し、さらにdiffの値が100ms以上のレコードだけ抜き出す、といったことが可能です。
$ lltsv -k uri,status,size,reqtime,apptime,diff -e 'diff=reqtime-apptime' -f 'diff > 0.100' sample.ltsv uri:/upload status:400 size:13599 reqtime:0.280 apptime:0.150 diff:0.13 uri:/download status:200 size:12812 reqtime:0.330 apptime:0.210 diff:0.12
あるいはreqtimeとapptimeに1000をかけて単位を秒からミリ秒に変換することもできます。
$ lltsv -e 'reqtime=reqtime*1000' -e 'apptime=apptime*1000' sample.ltsv uri:/upload status:400 size:13599 reqtime:280 apptime:150 uri:/download status:200 size:12812 reqtime:330 apptime:210 uri:/item/new status:200 size:29830 reqtime:50 apptime:50 uri:/item/fav status:200 size:33123 reqtime:100 apptime:99 uri:/top status:301 size:1256 reqtime:20 apptime:20
lltsvの式評価機能は与えられた式からGoのAST(抽象構文木)を生成した後、生成した構文木をトラバースする形で与えられた式を評価します。
そのため、f = (a + b) * (c - d)
のような若干複雑な式を記述することができるほか、
フィルタ機能と違ってラベルや値と演算子の間にスペースがなくてもうまい具合に解釈してくれます。
ただし、1行毎にevalを実行するようなものなので処理速度は遅い点に注意しましょう。
GoでASTを扱う方法については弊社の@tenntennが詳しいので興味のある人は彼のQiitaエントリを覗いてみると良いでしょう。
実際のところ、私もこの機能を実装するにあたって大いに参考させていただきました。
まとめ
lltsvを利用してLTSV形式のデータを処理するさまざまな方法について解説しました。
LTSVはシンプルなフォーマットなのでgrepやawk、perlのワンライナーでもある程度簡単にデータの加工やフィルタリングが可能ですが、
lltsvがあるとLTSVの加工やフィルタリングがさらに捗ります。
それでは、良いLTSVライフを。