Apacheのアクセスログの書式、それをfluentd等の他のログ収集システムに持っていくときの問題です。
Apacheログ "%U%q" のパス出力は"%r"と互換性が無い
Apacheのmod_rewriteによる内部リダイレクト規則を適用する場合、例えば下記のような
<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で収集したLTSV表記Apacheログでパス表記が食い違うことに……
ここで唐突に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すればいいじゃない
効かないんだなーこれが
解決法
## Apache 拡張ltsvログ用
# LogFormat "domain:%V\thost:%h\tserver:%A\tuser:%u\t
#time:%{%d/%b/%Y:%H:%M:%S %z}t\tmethod:%m\tpath:%U%q\tprotocol:%H\tcode:%>s
#\tsize:%b\treferer:%{Referer}i\tagent:%{User-Agent}i\tresponse_time:%D\t
#cookie:%{cookie}i\tset_cookie:%{Set-Cookie}o\tcountry:%{GEOIP_COUNTRY_CODE}e\t
#requestFirstLine:\"%r\"" combined_ltsv
<source>
type tail
path /var/log/httpd/ltsv_access_log
pos_file /var/log/td-agent/apache_access.pos
format ltsv
time_key time
time_format %d/%b/%Y:%H:%M:%S %z
tag apache.access.addHostname.fixPath
types code:integer,size:integer,response_time:integer
</source>
## record_transformerプラグインで、ホストを追加する
<filter **.addHostname>
@type record_transformer
<record>
hostname ${hostname}
</record>
</filter>
## record_transformerプラグインで、requestFirstLineからpathを分離し、追加する
<filter **.fixPath>
@type record_transformer
enable_ruby true
<record>
path ${requestFirstLine.split(/ /)[1]}
</record>
remove_keys requestFirstLine
</filter>
# Apache関連のログを転送
<match apache.**>
type copy
# デバッグ用
#<store>
# type stdout
#</store>
# fluentdサーバに送信
<store>
type forward
flush_interval 30s
<server>
host fluentd-server.example.com
port 24224
</server>
</store>
</match>
など、apacheの方で%rでrequestFirstLineを追加、record_transformerプラグインでrequestFirstLineからpath部を分離し、pathとして追加する方法を採用しました。
filterやrecord_transformerが使える、fluentd v0.12以降(td-agent2なら2015/4/6のtd-agent v2.2.0以降)が必要。
[http://cl.hatenablog.com/entry/apache-customlog より加筆]