LoginSignup
11
10

More than 5 years have passed since last update.

fluentdでApacheログをLTSVで転送するときのひと工夫(内部リダイレクト表記対策)

Last updated at Posted at 2015-05-25

Apacheのアクセスログの書式、それをfluentd等の他のログ収集システムに持っていくときの問題です。

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

Apacheのmod_rewriteによる内部リダイレクト規則を適用する場合、例えば下記のような

.htaccess
<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はこんな感じ。

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すればいいじゃない

効かないんだなーこれが

解決法

td-agent.conf
## 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 より加筆]

11
10
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10