Xserver のアクセスログを lltsv で読み取れるように ltsv 形式に変換する。awk の printf() は改行しようとすると制約があるっぽい。
~/.bashrc
apacheparse() { awk -f /dev/fd/3 3<<'!' | nkf --url-input BEGIN { OFS="\t" } { match($0, /^(\S+) (\S+) (\S+) (\S+) (\[.+?\]) "([A-Z]+) (\S+) (\S+)" ([0-9]{3}) ([0-9]+) "([^"]+?)" "([^"]+?)"$/, m) split(m[7], uri, "?") gsub("\\\\x", "%", m[12]) print("vhost:" m[1], "host:" m[2], "ident:" m[3], "user:" m[4], "time:" m[5], "method:" m[6], "path:" uri[1], "query:" uri[2], "protocol:" m[8], "status:" m[9], "size:" m[10], "referer:" m[11], "ua:" m[12]) } ! }
WordPress の場合、パーマリンクに記事タイトルを使用していると 記事タイトル が %E8%A8%98%E4%BA%8B%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB のようにエンコードされる。これだけ見ても何の記事かわからないので nkf でデコードする。nkf はシンレンタルサーバーだと /usr/bin/nkf にある(エックスサーバーは未確認だけどほぼ同じなのであると思う)。
gsub("\\\\x", "%", m[12]) に関しては Cloudflare でヘッダーを変換しているとき用に入れているので必須ではない。Cloudflare で ip.src.region と ip.src.city を書き出していると例えば「兵庫」の場合に ip.src.region の値は "Hy\xC5\x8Dgo" になってしまう。nkf は \xC5\x8D のような 16 進数を変換してくれないので gsub() で %C5%8D に置き換える。そうすると Hyōgo として出力してくれるようになる。
わざわざヒアドキュメントを使って AWK のスクリプトを生成しているのはちょっと前に上の変換を printf コマンドを使ってやっていたんだけどなんか知らんが match() の中で "" を使うと [0-9]{1,3} のような正規表現がマッチしなくなってしまったためその影響を受けないようにするため。
試してみたけど AWK の match() には名前付きグループ ?P やグループをキャプチャリングしない ?: が無いっぽい。パスとクエリの分割を match() でやろうとすると正規表現は (\/[^\s?]*)(\?(\S*))? みたいなものになるんだろうけど単純に split() で ? で分割した方が楽。
使い方はログファイルを標準入力で読み込ませる。
gzip -dc ログファイル.gz | apacheparse
パース前のログ。
hobby.mattintosh-note.jp 172.70.43.92 - - [05/Jul/2024:01:01:40 +0900] "GET /tag/%E3%83%96%E3%83%AC%E3%83%9E%E3%83%BC%E3%83%88%E3%83%B3-%E3%82%A2%E3%82%BA%E3%83%BC%E3%83%AB%E3%83%AC%E3%83%BC%E3%83%B3/ HTTP/2.0" 200 132264 "-" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.175 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
パース後のログ。
vhost:hobby.mattintosh-note.jp host:172.70.43.92 ident:- user:- time:[05/Jul/2024:01:01:40 +0900] method:GET path:/tag/ブレマートン-アズールレーン/ query: protocol:HTTP/2.0 status:200 size:132264 referer:- ua:Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.175 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)
PHP を使う場合はこんな感じだろうか。これも php -r を使用すると "" のエスケープ等が面倒になるのでヒアドキュメントを使用する。
~/.bashrc
apacheparse() { php -f /dev/fd/3 3<<'!' <?php while (($line = fgets(STDIN)) !== false) { if (preg_match('/^(\S+) (\S+) (\S+) (\S+) (\[.+?\]) "(\S+) (\S+) (\S+)" (\d{3}) (\d+) "(\S+)" "(.+?)"$/', $line, $matches)) { $path = urldecode(parse_url($matches[7], PHP_URL_PATH)); $query = urldecode(parse_url($matches[7], PHP_URL_QUERY)); $referer = urldecode($matches[11]); echo implode("\t", array( 'vhost:' . $matches[1], 'host:' . $matches[2], 'ident:' . $matches[3], 'user:' . $matches[4], 'time:' . $matches[5], 'method:' . $matches[6], 'path:' . $path, 'query:' . $query, 'protocol:' . $matches[8], 'status:' . $matches[9], 'size:' . $matches[10], 'referer:' . $referer, 'ua:' . $matches[12], )) . PHP_EOL; } else { echo $line . PHP_EOL; } } ! }