ログ初心者必見!アクセスログを快適に読むスクリプト

エンジニアの関です。

気づけば、社会人になって早2年と3ヶ月ちょっと。
社会人になってまず最初にハードルを感じるのは・・・そう、「ログを読む」ことですね。

かく言う私もかつてログに苦しめられた一人。
学生時代にエラーログはまだしもアクセスログはあまり気にしてこなかったのもあり、入社当初は「grep?なにそれ美味しいの?」と四苦八苦しながらログを読んでました。
(今もあまり得意な方ではないことはここだけの秘密です・・・汗)

ということで、(たぶん)みんな苦しめられてきたアクセスログの解析をもっと快適に、簡単にできるスクリプトを組んでいきたいと思います。

目次

何を解析するか

今回はアクセスログから以下の2つを解析していきます。
解析結果は、後々グラフを作成したりしたいのでCSV形式で出力されるように実装します。

  • 日時ごとのアクセス数
  • デバイス種別(PC・SP・タブレット)ごとのアクセス数

ログフォーマットを確認

対象のサーバではどのような形式でログが吐き出されているか、必ず確認しましょう。
Apacheの場合、httpd.conf内の以下の項目を参照してみてください。

LogFormat "%h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-Agent}i"" combined
LogFormat "%h %l %u %t "%r" %>s %b" common

※今回は「combined」のフォーマットを使用しています。
※それぞれの要素の詳しい内容を知りたい場合はこちら

ログを各要素に分解

まず最初にログのフォーマットを元にpreg_matchで要素を抽出していきます。

$log = '127.0.0.1 - - [23/Jun/2017:14:36:13 +0900] "GET /author/hseki/ HTTP/1.1" 200 21528 "https://tech.linkbal.co.jp/" "Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"';
// ログフォーマット
$pattern = '/^(S+)s(S+)s(S+)s[([S|s]+)]s"([S|s]+)"s(d+)s(d+)s"(S+)"s"([S|s]+)"/';
// ログを分解
preg_match($pattern, $log, $elements);

こうすることで$elementsに以下のようにログの各要素が格納され、操作がしやすくなります。

array(10) {
    // ログ全文
    [0]=> string(239) "127.0.0.1 - - [23/Jun/2017:14:36:13 +0900] "GET /author/hseki/ HTTP/1.1" 200 21528 "https://tech.linkbal.co.jp/" "Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1""
    // IPアドレス
    [1]=> string(9) "127.0.0.1"
    // リモートログ名
    [2]=> string(1) "-"
    // リモートホスト名
    [3]=> string(1) "-"
    // リクエスト日時
    [4]=> string(26) "23/Jun/2017:14:36:13 +0900"
    // リクエストの最初の行
    [5]=> string(27) "GET /author/hseki/ HTTP/1.1"
    // ステータスコード
    [6]=> string(3) "200"
    // レスポンスのバイト数
    [7]=> string(5) "21528"
    // Referer
    [8]=> string(27) "https://tech.linkbal.co.jp/"
    // UserAgent
    [9]=> string(124) "Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"
}

最後に、以下のようにまとめて各変数に代入していきます。
list表現を使うと一括で代入ができるため、コードがすっきりします。超オススメです。

list($log, $ip, $r_log, $r_user, $date, $header, $status, $byte, $referer, $useragent) = $elements;

以上を踏まえて作成したスクリプトがこちらです。

https://github.com/hseki-luckey/access-logs-analytics/

/logs内にgzip形式のログファイルを格納しておくと、/results内に結果のCSVが出力されます。
日付ごとにログを分けている場合などでも、/logsに設置されているすべてのログファイルを対象に解析することが可能です。

おわりに

何か障害が起こったときや、現在のサーバ状況を調べたりするときなどでログ解析は重宝します。サービスの運用・開発を継続していく上での必須スキルと言っても過言ではありません。
(それと、そういったときにスッとログの解析結果を出せるとカッコイイです。)

ぜひ自分好みにカスタマイズして、日々のログ解析に役立てていただければと思います。