nginx で nginx.conf
に access_log
と log_format
を設定すると,ログフォーマットをカスタマイズできる.log_format
のデフォルト設定は combined
だけど,ログ集計などを考慮して,JSON フォーマットに変更する場面も多いと思う.例えば,以下のように log_format
を設定すると,JSON フォーマットでアクセスログを出力できる.
log_format json '{' '"time": "$time_local",' '"remote_addr": "$remote_addr",' '"host": "$host",' '"remote_user": "$remote_user",' '"status": "$status",' '"server_protocol": "$server_protocol",' '"request_method": "$request_method",' '"request_uri": "$request_uri",' '"request": "$request",' '"body_bytes_sent": "$body_bytes_sent",' '"request_time": "$request_time",' '"upstream_response_time": "$upstream_response_time",' '"http_referer": "$http_referer", ' '"http_user_agent": "$http_user_agent",' '"http_x_forwarded_for": "$http_x_forwarded_for",' '"http_x_forwarded_proto": "$http_x_forwarded_proto"' '}'; access_log /var/log/nginx/access.log json;
アクセスログに "
が含まれる場合
例えば,アクセスログに "
が含まれる場合,自動的に \x22
に変換されてしまう.検証として,以下のように User Agent に kakaka"kakku
と設定し,nginx にリクエストを送る.
$ curl -H 'User-Agent: kakaka"kakku' http://www.example.com/
すると,以下のように出力される(アクセスログを整形している).User Agent を http_user_agent
の値で確認すると,確かに kakaka\x22kakku
になっている.なお,今回は例として "
で検証をしているけど,正確に言うと "
以外にも「RFC 4627」で定義された文字は変換されてしまう.
{ "time": "22/Nov/2019:00:00:00 +0900", "remote_addr": "x.x.x.x", "host": "www.example.com", "remote_user": "-", "status": "200", "server_protocol": "HTTP/1.1", "request_method": "GET", "request_uri": "/", "request": "GET / HTTP/1.1", "body_bytes_sent": "0", "request_time": "0.000", "upstream_response_time": "-", "http_referer": "-", "http_user_agent": "kakaka\x22kakku", "http_x_forwarded_for": "x.x.x.x", "http_x_forwarded_proto": "http" }
解決策 : escape=json
を設定する
ドキュメントを読むと,「nginx 1.11.8(2016年12月リリース)」から使える escape
パラメータを使って,escape=json
を設定すると,今回の件は解決する.
escape=json
を設定し,最終的な nginx.conf
は以下のようになる.
log_format json escape=json '{' '"time": "$time_local",' '"remote_addr": "$remote_addr",' '"host": "$host",' '"remote_user": "$remote_user",' '"status": "$status",' '"server_protocol": "$server_protocol",' '"request_method": "$request_method",' '"request_uri": "$request_uri",' '"request": "$request",' '"body_bytes_sent": "$body_bytes_sent",' '"request_time": "$request_time",' '"upstream_response_time": "$upstream_response_time",' '"http_referer": "$http_referer", ' '"http_user_agent": "$http_user_agent",' '"http_x_forwarded_for": "$http_x_forwarded_for",' '"http_x_forwarded_proto": "$http_x_forwarded_proto"' '}'; access_log /var/log/nginx/access.log json;
もう1度,User Agent に kakaka"kakku
と設定し,nginx にリクエストを送ると,http_user_agent
の値が kakaka\"kakku
となり,うまくエスケープされて出力できるようになった.なお,remote_user
など,もともと値が -
だった部分もブランクになっていて,ここはドキュメントには仕様が書かれてなく,理由までは追えなかった.
{ "time": "22/Nov/2019:00:00:00 +0900", "remote_addr": "x.x.x.x", "host": "www.example.com", "remote_user": "", "status": "200", "server_protocol": "HTTP/1.1", "request_method": "GET", "request_uri": "/", "request": "GET / HTTP/1.1", "body_bytes_sent": "0", "request_time": "0.000", "upstream_response_time": "", "http_referer": "", "http_user_agent": "kakaka\"kakku", "http_x_forwarded_for": "x.x.x.x", "http_x_forwarded_proto": "http" }
まとめ
nginx でアクセスログを JSON フォーマットにする場合,「nginx 1.11.8(2016年12月リリース)」から使える escape
パラメータを使って,escape=json
を設定する.数年前から使えるパラメータだとしても,無限に知らないことはあるし,引き続き学んだことをブログにアウトプットしていくぞ!