APIリクエストごとにカウント取ってみた
「ログ見てるけど情報量多すぎて嫌になる」
このような経験、ありませんか?
そんな苦い経験とは金輪際、おさらばしましょう。
本記事では、コマンドを駆使してログを見やすく加工したり、カウントする方法を紹介します。
使用するコマンド
## ファイルを標準出力
cat
## 行数を指定して出力
head
## テキスト処理コマンド
awk
## 繰り返し処理
for
## 特定文字列を除外
sed
## 文字列出力
printf
## 出力のソート
sort
## 出力を一意の結果にする
uniq
まずは普通に出力
cat
コマンド使います
cat app.log
[2018-06-07 19:58:15] lumen.DEBUG: -- Startup {"method":"GET","uri":"/api/API/apipipipi/1"} []
[2018-06-07 19:58:15] lumen.DEBUG: -- Shutdown {"time":"234.990[ms]","memory":"10[mb]"} []
~~~100行くらい~~~
[2018-06-07 20:03:56] lumen.DEBUG: -- Shutdown {"time":"67.988[ms]","memory":"2048[kb]"} []
100行か。。。まだ大丈夫な範囲だけど本番のログだと篦棒にある気がする。
そんなの見てたら目が痛くなりますよね。
大量のログを凝視している方を見ていると僕は思わずニヤニャ...ゲフン。胸が痛くなります。
さて次は作業しやすいように数行だけ出力してみましょう。
先頭から数行だけ出力してみる
先頭から3行だけ出力してみましょうか。
cat
コマンドにパイプとhead
コマンドを使いますが、オプションで-n
をつけて行数をそのあとに指定しましょう。
$ cat app.log | head -n3
[2018-06-07 19:58:15] lumen.DEBUG: -- Startup {"method":"GET","uri":"/api/API/apipipipi/1"} []
[2018-06-07 19:58:15] lumen.DEBUG: -- Shutdown {"time":"234.990[ms]","memory":"10[mb]"} []
[2018-06-07 19:58:16] lumen.DEBUG: -- Startup {"method":"GET","uri":"/api/API/apipipipi/2"} []
先頭から3行だけ出力されました。
必要部分のみ抽出して出力
お次はカウントに必要な情報。すなわちAPI部分のみ抜き出してみましょう。
必要な情報 → {"method":"GET","uri":"/api/API/apipipipi/x"}
awk
コマンド使います。様々な使い方あるので興味ある方は調べて見てください。
$ cat app.log | head -n3 | awk '{print $6}'
{"method":"GET","uri":"/api/API/apipipipi/1"}
{"time":"234.990[ms]","memory":"10[mb]"}
{"method":"GET","uri":"/api/API/apipipipi/2"}
これで「3行のログの中の”半角スペース(タブ)で区切られた6桁目”の文字列」を出力できました。
だいぶショートボディになりましたね。
あれ、でも今回のカウント作業で使わないデータが含まれてますね。
こいつ → {"time":"234.990[ms]","memory":"10[mb]"}
欲しい行だけ抽出しましょう。grep
コマンドを使います。
やり方は2パターンありますのでどちらを使っても問題ないですが、一致する行を抽出した方がきっと楽でしょう。
## 条件に一致する行を削除する方法
$ cat app.log | grep -v "memory"| head -n3 | awk '{print $6}'
{"method":"GET","uri":"/api/API/apipipipi/1"}
{"method":"GET","uri":"/api/API/apipipipi/2"}
{"method":"POST","uri":"/api/API/apipipipi/3"}
## 条件に一致する行を抽出する方法
$ cat app.log | grep "method"| head -n3 | awk '{print $6}'
{"method":"GET","uri":"/api/API/apipipipi/1"}
{"method":"GET","uri":"/api/API/apipipipi/2"}
{"method":"POST","uri":"/api/API/apipipipi/3"}
これで不要な行を除外できました。
さらに見やすいように成形
初期よりは見やすくなりましたが、まだ無駄な情報があったりしますよね。
ぶっちゃけこのままでもカウント取れるのですが、せっかくなので成形してみましょう。
またまたawk
を利用しますが、今回は少し特殊です。
特殊なawk
部分だけを見てみましょう。
awk -F '/' '{for(i=2;i<NF;i++){printf("%s%s",$i,OFS="/")}print $NF}'
-
-F
オプションで区切り文字を指定 -
for
文で「◯桁目〜改行文字まで」を出力 -
printf("%s%s",$i,OFS="/")
で対象の桁、”/”を繋げて出力 -
print $NF
で改行文字を出力
組み合わせると結果がこうなります。確認のため1行だけ出力します。
$ cat app.log | grep method| head -n1 | awk '{print $6}' |\
awk -F'/' '{for(i=2;i<NF;i++){printf("%s%s",$i,OFS="/")}print $NF}'
api/API/apipipipi/1"}
お、だんだん良い感じになってきましたね。
※ただfor文を利用する為負荷がかかります。ログが膨大な場合は注意が必要です。
いらない文字列を除外する
末尾の「"}」が邪魔ですね。消してしまいましょう。
sed
を使います。
$ cat app.log | grep method| head -n1 | awk '{print $6}' |\
awk -F'/' '{for(i=2;i<NF;i++){printf("%s%s",$i,OFS="/")}print $NF}' |\
sed -e "s/\"}//g"
api/API/apipipipi/1
いい感じにソートしよう
sort
を使います。また、全行出力するためにhead
を除いて実行!
$ cat app.log | grep method| awk '{print $6}' |\
awk -F'/' '{for(i=2;i<NF;i++){printf("%s%s",$i,OFS="/")}print $NF}' |\
sed -e "s/\"}//g" | sort
api/API/apipipipi/1
api/API/apipipipi/2
~~100行くらい~~
api/API/apipipipi/1
api/API/apipipipi/3
いい感じいい感じ。
ラスト!ユニークな値ごとにカウントしよう
uniq
を使います。
オプション-c
でカウントできます。
$ cat app.log | grep method| awk '{print $6}' |\
awk -F'/' '{for(i=2;i<NF;i++){printf("%s%s",$i,OFS="/")}print $NF}' |\
sed -e "s/\"}//g" | sort | uniq -c
2 api/API/apipipipi/1
3 api/API/apipipipi/2
2 api/API/apipipipi/3
1 api/API/apipipipi/8
2 api/API/apipipipi/5
20 api/API/apipipipi/11
~~数十行くらい
これでAPIごとのカウントが取得できました。
まとめ
コマンド便利。
今回紹介したコマンドですが、コピペではおそらく期待している結果は出力されないと思います。
サービスによってログの形式が異なるので、ニーズに合わせてカスタマイズしてください。
おまけ
ちょっと見やすくしてみた
$ cat app.log \
| grep method \
| awk '{print $6}' \
| awk -F'/' '{for(i=2;i<NF;i++){ \
printf("%s%s",$i,OFS="/") \
} print $NF}' \
| sed -e "s/\"}//g" \
| sort \
| uniq -c
;
一部冗長な表現があると思いますが、わかりやすく見せるために簡単に書いてます。
利用される機会があればぜひ。