M.C.P.C. (Mamesibori Creation Plus Communication)

印刷屋から五反田のWeb屋に転職したCLのブログです。

Apacheログ "%U%q" のパス出力は"%r"と互換性が無い

AcceptPathInfoがOffになっているような、標準とは違うApache環境下で動く不幸なCakePHPで、公開ディレクトリに書かれる.htaccessによるmod_rewite規則

<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
</IfModule>

という奴を使うと、例えば http://example.com/top/ というURLにアクセスすると、Apacheのカスタムログフォーマットでいうと、

%r: GET /top/ HTTP/1.1

%U: /top/
%q: ?url=top/

となり、

%U%q: /top/?url=top/

となります。/top/ になってほしいのに、内部rewrite処理で生成された?url=top が付いてしまうのです。redirect_logはこんな感じ。

(3) [perdir /var/www/html/] add path info postfix: /var/www/html/top -> /var/www/html/top/
(3) [perdir /var/www/html/] strip per-dir prefix: /var/www/html/top/ -> top/
(3) [perdir /var/www/html/] applying pattern '^(.*)$' to uri 'top/'
(4) [perdir /var/www/html/] RewriteCond: input='/var/www/html/top' pattern='!-d' => matched
(4) [perdir /var/www/html/] RewriteCond: input='/var/www/html/top' pattern='!-f' => matched
(2) [perdir /var/www/html/] rewrite 'top/' -> 'index.php?url=top/'
(3) split uri=index.php?url=top/ -> uri=index.php, args=url=top/
(3) [perdir /var/www/html/] add per-dir prefix: index.php -> /var/www/html/index.php
(2) [perdir /var/www/html/] trying to replace prefix /var/www/html/ with /
(5) strip matching prefix: /var/www/html/index.php -> index.php
(4) add subst prefix: index.php -> /index.php
(1) [perdir /var/www/html/] internal redirect with /index.php [INTERNAL REDIRECT]

fluentdのログ転送で使うLTSVフォーマット指定で、パスの表記に%U%qで指定している例が多いので、通常のApacheログとfluentdで収集したApacheログで表記が食い違うことに……

fluentdでApacheログをLTSVで出力している人は注意が必要です……

ここで唐突にApache CustomLog書式の確認

http://httpd.apache.org/docs/2.2/ja/mod/mod_log_config.html

フォーマット文字列 説明
%q 問い合せ文字列 (存在する場合は前に ? が追加される。 そうでない場合は空文字列)
%r リクエストの最初の行
%U リクエストされた URL パス。クエリ文字列は含まない

修飾子
(略)
修飾子 "<" と ">" は内部リダイレクトされたリクエストのログに 元のリクエストか最終的なリクエストのどちらを使用するかを 指定するために使います。デフォルトでは、% ディレクティブの %s, %U, %T, %D, %r は元のリクエストを、他は最終的なリクエストを 使用します。例えば、リクエストの最終ステータスを記録するには %>s を、内部的に認証されていないリソースへリダイレクトされた リクエストで元のリクエストで認証されたユーザを記録するためには %<u を使うことができます。

LogFormatで%U%<qすればいいじゃない

効かないんだなーこれが

fluentdでの解決法

解決法はQiitaに書く(要fluentd v0.12以降)→書いた