ablog

不器用で落着きのない技術者のメモ

Apache のログにレスポンスタイムを記録する

Apache のログにリクエスト処理時間を記録したい場合、LogFormatで %D(マイクロ秒)、%T(秒)で指定する。

ログ出力

**.0.3.*** - - [29/Nov/2020:13:56:07 +0000] 260★マイクロ秒(%D) 0★秒(%T) "GET / HTTP/1.1" 403 3630 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36"
**.0.3.*** - - [29/Nov/2020:13:56:07 +0000] 75 0 "GET /icons/apache_pb2.gif HTTP/1.1" 200 4234 "http://ec2-**-***-***-***.ap-northeast-1.compute.amazonaws.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36"
**.0.3.*** - - [29/Nov/2020:13:56:07 +0000] 61 0 "GET /favicon.ico HTTP/1.1" 404 196 "http://ec2-**-***-***-***.ap-northeast-1.compute.amazonaws.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36"
**.0.3.*** - - [29/Nov/2020:13:56:10 +0000] 175 0 "GET / HTTP/1.1" 403 3630 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.67 Safari/537.36"

インストール・設定

$ sudo yum -y install httpd
<IfModule log_config_module>
    #
    # The following directives define some format nicknames for use with
    # a CustomLog directive (see below).
    #
    LogFormat "%h %l %u %t %D %T \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined # ★ %D と %T を追記
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

(中略)

    #
    # If you prefer a logfile with access, agent, and referer information
    # (Combined Logfile Format) you can use the following directive.
    #
    CustomLog "logs/access_log" combined
</IfModule>
  • 起動
$ sudo systemctl start httpd.service
  • ステータス確認
$ sudo systemctl status httpd.service
* httpd.service - The Apache HTTP Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
   Active: active (running) since Sun 2020-11-29 13:48:49 UTC; 1min 40s ago
     Docs: man:httpd.service(8)
 Main PID: 21146 (httpd)
   Status: "Total requests: 0; Idle/Busy workers 100/0;Requests/sec: 0; Bytes served/sec:   0 B/sec"
   CGroup: /system.slice/httpd.service
           |-21146 /usr/sbin/httpd -DFOREGROUND
           |-21147 /usr/sbin/httpd -DFOREGROUND
           |-21148 /usr/sbin/httpd -DFOREGROUND
           |-21153 /usr/sbin/httpd -DFOREGROUND
           |-21169 /usr/sbin/httpd -DFOREGROUND
           `-21179 /usr/sbin/httpd -DFOREGROUND

Nov 29 13:48:49 ip-172-**-*-178.ap-northeast-1.compute.internal systemd[1]: Starting The Apache HTTP Server...
Nov 29 13:48:49 ip-172-**-*-178.ap-northeast-1.compute.internal systemd[1]: Started The Apache HTTP Server.

参考

フォーマット文字列 説明
%D リクエストを処理するのにかかった時間、マイクロ秒単位
%T リクエストを扱うのにかかった時間、秒単位
mod_log_config - Apache HTTP サーバ バージョン 2.2

Apacheのログに%T or %Dでリクエスト処理時間を載せることができます。%Tと%Dは内部的には同じ計算値で、単位が異なるだけです(%Tは秒、%Dはマイクロ秒)。
このリクエスト処理時間は、いつからいつまでの処理時間でしょうか。つまり計測時間の開始と終了はどのタイミングでしょうか。
終了はレスポンスデータをsend(2)し切ったタイミングです。
開始として思いつくのは次の3つのタイミングです。

  1. listenキューに入ったタイミング(3wayハンドシェイクの最初のACKを受けたタイミング)
  2. accept(2)が返ったタイミング(3wayハンドシェイクの最後のACKを受けたタイミング)
  3. リクエストデータをrecv(2)する前

実はどれも間違いです。正解は、リクエストデータの1行目のリクエスト行を読み終わったタイミングです(ソースコードで言うと server/protocol.cのread_request_line()で r->request_time に値を設定している行です)。

上記3案のうち、aは少し不自然なタイミングです。しかも、通常のソケットプログラミングでは、このタイミングはpoll/selectによるポーリングでしか知りえません。
b.のタイミングで計測すると、ワーカースレッドの数が足りない場合にワーカースレッドを待つ時間が加算されることになります。状況によっては意図と異なる値です。
c.のタイミングで計測すると、HTTPクライアントが接続だけしてリクエストデータを送信してこない時の待ち時間が加算されることになります。
そんなわけで、リクエスト行を読み終わったタイミングから計測開始、ということなのでしょう。

Apacheのリクエスト処理時間(%T or %D)は正確には何の時間か — ありえるえりあ