最近RustでCLIを作るのに突然ハマってきたのと、ISUCON が近づいていることもあって、以下のCLIを作ってみました。
使い方
現状はnginxのlog_formatをJSONとした上で特定のキーを持っていることを想定しています。 (こういう感じの設定をしておく👇)
log_format json escape=json '{' '"time":"$time_iso8601",' '"host":"$remote_addr",' '"method":"$request_method",' '"uri":"$request_uri",' '"status":"$status",' '"body_bytes":"$body_bytes_sent",' '"referer":"$http_referer",' '"ua":"$http_user_agent",' '"request_time":"$request_time",' '"response_time":"$upstream_response_time"' '}';
この状態で吐かれたアクセスログを、 seki
にパイプして食わせるのみです。以下のような形のJSONが返ってきます。*1
$ cat access.log | seki [ { "method": "GET", "uri": "/hello", "count": 1, "status_code": { "status_1xx": 0, "status_2xx": 0, "status_3xx": 1, "status_4xx": 0, "status_5xx": 0 }, "response_time": { "min": 0.0, "max": 0.113, "avg": 0.113, "sum": 0.113, "p50": 0.113, "p75": 0.113, "p90": 0.113, "p95": 0.113, "p99": 0.113 } } ]
このJSONをどう使うかはユーザに委ねる、という感じです。
思想
tkuchiki/alp や matsuu/kataribe など便利ツールが世の中にあるので新たに作る必要は特にないですが*2、思想としてはJSONとして返しておけば色々と加工したり独自のスクリプトに組み込んであれこれしやすそうではないか?と思って作ってみています。
そのため、 seki
自体が人間にみやすいフォーマットへの出力オプションを持つ予定はないです。
private-isu でちょっと使ってみた様子
実際に catatsuy/private-isu でも使ってみました。AMIを利用してEC2サーバを建てた上で実施しています。
まず seki
をインストールします。今回は Release v0.1.2 · stefafafan/seki · GitHub に載っている curl を使う手順を使います。
$ curl --proto '=https' --tlsv1.2 -LsSf https://github.com/stefafafan/seki/releases/download/v0.1.2/seki-installer.sh | sh
PATHを通したかったら source
してねと言われるのでします。
$ source $HOME/.cargo/env
nginx.conf の log_format
を書き換えた上でベンチマークを回します。終わったら早速 seki
を使ってみます。
$ sudo cat /var/log/nginx/access.log | seki
これを何気なく実行すると結果が長すぎることがわかります。 /posts/12667
みたいなエンドポイントが沢山個別にならんでいるのでグルーピングします。
一旦エンドポイントの一覧をざっとみてみましょう。 jq
コマンドで .uri
のみ抽出します。
$ sudo cat /var/log/nginx/access.log | seki | jq '.[] | .uri' "/posts/12667" "/posts/2005" "/image/6908.jpg" "/image/9785.jpg" "/image/9228.jpg" "/posts/556" "/posts/4366" "/image/1149.jpg" "/posts/7523" "/posts/5208" "/posts/1797" "/posts/3376" "/@lindsay" "/@violet" "/image/1855.jpg" "/image/6059.jpg" "/posts/7627" ...
/posts/\d+
と /images/.+
と /@[a-zA-Z0-9]+
みたいなのが沢山ありそうですね。 config.toml
にグルーピングの設定を書きましょう。 リポジトリにもサンプルがあります。*3
github.com
$ vim config.toml $ cat config.toml [[grouping]] regexp = '^/posts/\d+$' [[grouping]] regexp = '^/@[a-zA-Z0-9]+$' name = '/@:username' [[grouping]] regexp = '^/image/.+'
regexp
に正規表現を書きます。 name
の指定はオプショナルですが、名前をつけたいときに書いておくとURLが並んだ時に見やすくなります。
今一度先ほどのコマンドを実行してみます。 seki
は同じ階層にある config.toml
をデフォルトで読み込みますが、 --config
で明示的にパスを指定することもできます。
$ sudo cat /var/log/nginx/access.log | seki | jq '.[] | .uri' "/login" "/posts" "/" "/@:username" "/admin/banned" "/image/.+" "/comment" "/initialize" "/js/main.js" "/register" "/" "/css/style.css" "^/posts/\\d+$" "/favicon.ico" "/login" "/js/timeago.min.js" "/logout"
いい感じにグルーピングができたので、あとは好きな形に加工してベンチの結果を確認してみましょう。今回は response_time.sum
順にしつつ、 column
コマンドで整形して出力してみます。
$ sudo cat /var/log/nginx/access.log | seki | \ jq -r "sort_by(-.response_time.sum) | \ .[] | \ [.method, \ .uri, \ .status_code.status_2xx, \ .status_code.status_3xx, \ .status_code.status_4xx, \ .status_code.status_5xx, \ .response_time.sum] | \ @tsv" | column -t GET / 3939 0 0 0 198.79800000000128 GET /posts 1590 0 0 0 95.20300000000013 GET ^/posts/\\d+$ 2829 0 0 0 93.72100000000103 POST /login 0 1872 0 0 78.4420000000002 GET /@:username 482 0 0 0 40.025 GET /login 694 0 0 0 19.445999999999984 POST / 0 162 162 0 17.529000000000003 GET /image/.+ 2977 90691 0 0 16.31000000000001 POST /register 0 263 0 0 12.548999999999992 GET /logout 0 347 0 0 9.674999999999988 POST /comment 0 244 0 0 7.659999999999998 GET /admin/banned 0 0 263 0 7.20899999999999 GET /initialize 1 0 0 0 0.369 GET /css/style.css 5504 0 0 0 0.0 GET /js/timeago.min.js 5504 0 0 0 0.0 GET /favicon.ico 5504 0 0 0 0.0 GET /js/main.js 5504 0 0 0 0.0
GET /
が遅そうなので、ここの改善をしていきましょう。
作ってみての感想
当初思っていたよりもサクッとCLIが作れてよかった、 閉包テーブルによる階層構造をPlantUMLやMermaid形式で可視化するツールを作った - stefafafan の fa は3つです の時と同様に clap-rs/clap がやっぱり便利なのと、それ以外にも各種バイナリの用意やリリースのためのツール群 (crate-ci/cargo-release と axodotdev/cargo-dist) もとても助かりました。
そして実装の際に alp のこと改めて調べていましたが、めちゃくちゃ機能充実していてすごいなと思ったりしました。実装にあたってalpとkataribeのインターフェースはとても参考にしました、ありがとうございます🙏 複雑なjqのクエリを書くのは大変なのでこれら既存のツールを使うのが一番手っ取り早いなと改めて思いました(が自分でも作るのは楽しかったです)。
次は pt-query-digest 代わりのCLIも作りたいです (?)。