簡易ログ解析

はじめに

ウェブサービスをはじめとしたサーバを伴うアプリの運用では、ログの解析が必要となる場面が非常に多くある。
KPI などの集計の他、障害時対応で、限られた時間でアクセスログから目的のデータを抽出しなければならない、といったエンジニアとしての腕を試されるケースも多い。
本格的なログ解析はシェルなりスクリプト言語なりでプログラムを組めば良いが、ワンライナーでできる簡単なコマンドを備忘のためにまとめる。

ログといっても出力形式は多様なので、今回は以下のような、Web サーバ名、接続元 IP アドレス、日時、リクエストメソッドURI、ユーザーエージェントなど、一般的な内容のアクセスログを想定している。

sample.log.web-45 123.456.789.0 - - [25/Dec/2015:00:00:42 +0900] "POST /sampleapp/getuserdata/index?userid=012345167890 HTTP/1.1" 200 496 "-“ "SampleApp 1.0.0(iOS 8.2; iPhone7,2)" 21719

なお、実際の運用では bzip2 形式でアーカイブされたログを扱うといったことが多いが、この場合、cat は bzcat、grep は bzgrep に置き換えると、アーカイブを展開することなく同じ操作が行える。

ユースケース

サービス全体の PV 数

サービス全体の PV 数を求める場合、アクセスログの行数がそのまま PV 数となる。

$ cat sample.log | wc -l
42549685

  1. まず cat でログ全体の内容を出力する。
  2. "-l" オプション付きの wc に繋いで、行数を出力する。

特定の API に対する PV 数

$ grep -c "updateuserdata/index" sample.log
2152947

  1. 特定の API に対する PV 数の場合、cat の代わりに grep を使うことで、検索文字列に指定した URI に対する PV 数を抽出する。grep は “-c” オプションで一致した行数を取得することができるので、wc に繋ぐ必要はない。

特定の API に対するユニーク PV 数

特定の API に対する PV 数の内でも、ユニークユーザーが何人かといった場合に使う。

$ grep "updateuserdata/index" sample.log | awk '{ print $8 }' | sort | uniq | wc -l
1311529

  1. 特定の API に対する PV 数同様、grepURI 文字列にマッチする行を抽出する。ただし、今回は一致した行の内容 (userid) を調べなければならないため、”-c” は外しておく。
  2. ユニーク条件は "userid" なので、awk を使って userid が含まれる 8 番目 ($8) の内容のみを切り出す。これによって、”/sampleapp/getuserdata/index?userid=012345167890” が結果として取得される。ここでは userid 以外の文字列も含まれているが、userid 以前の文字列に変化はないため、このままこの結果をユニーク条件として使うことができる。
  3. 次に uniq を使って重複を取り除いた行を取り出すが、uniq コマンドはそのまま使うと離れた行の結果を正しく処理できない。そこで、先に sort を通して同一の結果行を連続させてから uniq を行う。
  4. 最後に wc に繋いで行数を出力する。

1時間ごとの PV 数を抽出

各時間帯における1時間当たりの PV 数を得る場合に使う。

$ cat sample.log | awk '{ print substr($5, 14, 2) ":00" }' | sort | uniq -c
99827 00:00
89332 01:00
.
.

  1. まず、cat で全行を出力する。
  2. 次に awk を使ってアクセス日時 ($5) を切り出す。さらに、必要なのは 00 〜 23 時の時間単位の指定箇所のみとなるため、この awk の時点で substr 関数を使って、文字列の切り出しを行う。出力フォーマットの整形のために、”:00” を一緒に print している。なお、substr は、オフセットに負値を指定して文字列末尾から範囲を指定する、といったことができないので、注意が必要。
  3. あとは、ユニーク数の出力となるので、最初に sort を行ってから uniq へと繋ぐ。uniq は "-c" オプションを使うことで、各行の重複行数を出すことができるため、これを利用してそのまま PV 数とする。

アクセス端末のランキングを作成

API にアクセスした端末やブラウザのシェアを出す時に使う。

$ cat sample.log | awk 'sub(/\)\"/, "", $16) { print $16 }' | sort | uniq -c | sort -r
52731 iPhone8,1
37480 iPhone5,2
32282 iPhone7,1
22477 iPhone5,3
19529 iPhone7,2
17290 SO-02G
15576 SO-01G
15541 SH-02G
15088 SO-04E
.
.

  1. まず、cat で全行を出力する。
  2. 次に awk を使って、端末情報が含まれる UserAgent 部分 ($16) を切り出す。ここで結果をそのまま print すると、iPhone7,2)” といった閉じ括弧とダブルクォーテーションが含まれた文字列が出力される。これを消すために、sub 関数を使って文字列の置換削除を行っている。sub 関数は正規表現による文字列抽出・置換が可能となっており、sedにパイプしなくても置換が実現できる。
  3. 端末名のみの行が出力されるので、sort => uniq と繋ぐ。uniq は、 "-c" オプションをつけて各行の重複行数を出力する。
  4. "uniq -c" による重複行数表示は行頭につくため、これを利用し、"-r" (リバースオプション) をつけた sort を行うことで、重複行数の多い端末からソートしてそのままランキング表示を行うことができる。ここでは sort を2回使うことになるが、1回目は正しく uniq の結果を得るための sort、2回目はランキング作成のための sort である。