2026年5月25日

2026年5月25日

Nginxのログ形式をカスタマイズする方法|log_formatの書き方とJSON・条件付きログ

Nginx のアクセスログの形式は、log_format で名前を付けて定義し、access_log でその名前を指定して使います。既定の形式(combined)には応答にかかった時間やバックエンドの情報が含まれないため、遅いリクエストの調査や監視システムへの取り込みには、項目を足した形式を作ります。

この記事の設定は、Nginx 1.30.6(nginx.org の公式 Docker イメージ)で nginx -t と実際のログ出力を確認しました(2026年11月時点)。ディレクティブの仕様は公式ドキュメント(ngx_http_log_module)に基づいています。

log_format と access_log の基本

ディレクティブ 書ける場所 役割
log_format 名前 [escape=default/json/none] 文字列 ...; http ブロックだけ ログ形式を定義する
access_log パス [形式名] [buffer=] [flush=] [if=]; http・server・location など どのファイルにどの形式で書くかを指定する

log_format を server や location の中に書くと、nginx -t が次のエラーを出します。

nginx: [emerg] "log_format" directive is not allowed here in /etc/nginx/conf.d/bad.conf:1

access_log で形式名を省略すると、あらかじめ定義されている combined が使われます。combined の中身は次のとおりです。

log_format combined '$remote_addr - $remote_user [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    '"$http_referer" "$http_user_agent"';

combined という名前は組み込みなので、同じ名前で定義し直すことはできません。独自の形式には別の名前を付けます。

設定ファイルを置く場所

log_format は http ブロックの中に入る必要があります。Debian/Ubuntu のパッケージや nginx.org の公式パッケージでは、/etc/nginx/nginx.conf の http ブロックに include /etc/nginx/conf.d/*.conf; があるので、conf.d にファイルを置けば http ブロックに書いたのと同じになります。念のため確認します。

grep -n "include" /etc/nginx/nginx.conf

ここでは /etc/nginx/conf.d/log_format.conf に形式を定義し、各サイトの server ブロックで access_log を指定します。設定ファイルの編集はエディターで行ってください。echo "... $remote_addr ..." > ファイル のようにシェルの二重引用符で書くと、$remote_addr などがシェルの変数として展開されて消えてしまいます。

応答時間とバックエンドの情報を足した形式

combined の後ろに、ホスト名・処理時間・バックエンド(proxy_pass や fastcgi_pass の接続先)の情報を足した例です。

# /etc/nginx/conf.d/log_format.conf
log_format main_ext '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" "$http_user_agent" '
                    'host=$host rt=$request_time '
                    'uct=$upstream_connect_time urt=$upstream_response_time '
                    'ua=$upstream_addr us=$upstream_status';
# 各サイトの server ブロック
server {
    server_name example.com;
    access_log /var/log/nginx/example.access.log main_ext;
    # ...
}

確認環境では次のように出力されました。バックエンドを通らないリクエストでは $upstream_* が - になります。

127.0.0.1 - - [10/Nov/2026:09:32:37 +0000] "GET /app/x HTTP/1.1" 200 8 "http://ref/?a=b" "Mozilla \x22quote\x22 test" host=127.0.0.1 rt=0.000 uct=0.000 urt=0.000 ua=127.0.0.1:8080 us=200
127.0.0.1 - - [10/Nov/2026:09:32:37 +0000] "GET / HTTP/1.1" 200 3 "-" "curl/8.14.1" host=127.0.0.1 rt=0.000 uct=- urt=- ua=- us=-

既定のエスケープ(escape=default)では、" や \、制御文字などが \x22 のような形に置き換わります。

よく使う変数

変数 内容
$remote_addr 接続元の IP アドレス(ロードバランサーなどの後ろでは、その機器のアドレスになる)
$http_x_forwarded_for リクエストの X-Forwarded-For ヘッダー
$time_local / $time_iso8601 時刻(Common Log Format 形式 / ISO 8601 形式)
$msec ログを書いた時刻(ミリ秒単位の UNIX 時間)
$request リクエスト行全体(例: GET /path HTTP/1.1)
$request_method / $request_uri メソッド / 引数を含む URI
$host リクエストのホスト名
$status 応答のステータスコード
$body_bytes_sent / $bytes_sent 送った本文のバイト数 / ヘッダーを含む全体のバイト数
$request_length 受け取ったリクエストの長さ(ヘッダーと本文を含む)
$request_time 最初のバイトを受け取ってから、最後のバイトを送り終えるまでの秒数(ミリ秒単位)
$upstream_connect_time バックエンドとの接続にかかった秒数
$upstream_header_time バックエンドから応答ヘッダーを受け取るまでの秒数
$upstream_response_time バックエンドから応答を受け取り終えるまでの秒数
$upstream_addr / $upstream_status 実際に使ったバックエンドのアドレス / その応答のステータス
$request_id リクエストごとに生成される 16 バイトの ID(16 進 32 文字)
$ssl_protocol / $ssl_cipher TLS のバージョン / 暗号スイート(HTTPS のとき)

$upstream_* は、バックエンドを複数試した場合に 0.001, 0.002 のようにカンマ区切りで複数の値が入ります。

$request_time が大きく $upstream_response_time が小さいときは、バックエンドではなくクライアントへの送信(回線が遅い、ファイルが大きいなど)に時間がかかっています。両方が大きいときは、バックエンド側の処理を調べます。

JSON 形式で出す(escape=json)

ログ収集の仕組み(Elasticsearch、Loki、クラウドのログサービスなど)に取り込む場合は、JSON 形式にしておくと項目の切り出しが不要になります。escape=json を付けると、値の中の " や \ が JSON として正しい形にエスケープされ、値が無い変数は空文字になります。

# /etc/nginx/conf.d/log_format.conf
log_format json_combined escape=json
    '{'
        '"time":"$time_iso8601",'
        '"remote_addr":"$remote_addr",'
        '"host":"$host",'
        '"method":"$request_method",'
        '"uri":"$request_uri",'
        '"protocol":"$server_protocol",'
        '"status":$status,'
        '"bytes_sent":$body_bytes_sent,'
        '"referer":"$http_referer",'
        '"user_agent":"$http_user_agent",'
        '"request_time":$request_time,'
        '"upstream_response_time":"$upstream_response_time",'
        '"request_id":"$request_id"'
    '}';

確認環境での出力です(1 リクエスト 1 行)。

{"time":"2026-11-10T09:32:37+00:00","remote_addr":"127.0.0.1","host":"127.0.0.1","method":"GET","uri":"/app/x","protocol":"HTTP/1.1","status":200,"bytes_sent":8,"referer":"http://ref/?a=b","user_agent":"Mozilla \"quote\" test","request_time":0.000,"upstream_response_time":"0.000","request_id":"5c4f1ceb37b553029aa364001c192a6c"}

$status・$body_bytes_sent・$request_time は常に数値なので、引用符を付けずに数値として出しています。$upstream_response_time は値が無いと空になり、複数の値がカンマ区切りで入ることもあるため、文字列として扱っています。

条件付きでログを書く・書かない

エラーの応答だけを別のファイルに書く

access_log の if= に変数を指定すると、その値が空でも 0 でもないときだけ書きます。map は http ブロックに書きます。

# /etc/nginx/conf.d/log_format.conf
map $status $loggable {
    ~^[23]  0;
    default 1;
}
# server ブロック
access_log /var/log/nginx/example.access.log main_ext;
access_log /var/log/nginx/example.error-status.log main_ext if=$loggable;

同じブロックに access_log を複数書くと、すべてのファイルに書かれます。確認環境では、2xx・3xx の応答は 1 つ目のファイルだけに、404 の応答は両方のファイルに記録されました。

ヘルスチェックなどを記録しない

location = /healthz {
    access_log off;
    return 200 "ok\n";
}

下位のブロック(location など)に access_log を書くと、上位のブロックの access_log は引き継がれず、その location の指定だけが使われます。

バッファーで書き込み回数を減らす

アクセスが多いサーバーでは、buffer= を付けるとリクエストごとにディスクへ書かず、まとめて書き込みます。

access_log /var/log/nginx/example.json.log json_combined buffer=32k flush=5s;
  • buffer=32k:32KB たまったら書き込む
  • flush=5s:たまっていなくても 5 秒経ったら書き込む

書き込みが遅れるので、tail -f で見ていてもすぐには表示されません。バッファーはワーカープロセスごとに持つため、確認環境では行の並びが時刻の順と前後することがありました。

反映と確認

sudo nginx -t
sudo systemctl reload nginx
curl -s -o /dev/null http://example.com/
sudo tail -n 3 /var/log/nginx/example.access.log

reload で反映できます。再起動(restart)は不要です。新しいログファイルは Nginx が自動で作ります。

ステータスコードごとの件数は次のように数えられます(combined と main_ext では 9 番目の項目がステータスコード)。

awk '{print $9}' /var/log/nginx/example.access.log | sort | uniq -c

ログのローテーション

Debian/Ubuntu のパッケージでは /etc/logrotate.d/nginx が入っており、/var/log/nginx/*.log が毎日ローテーションされ 14 世代保存されます(Debian 13 のパッケージで確認)。新しく作るログファイルも /var/log/nginx/ に .log の名前で置けば対象になります。別の場所に置く場合は、logrotate の設定に追加します。ローテーション後に Nginx に新しいファイルを開かせるには、nginx -s reopen を使います。

注意点

  • ログには IP アドレスや URL の引数(検索語、トークンなど)が残ります。閲覧できるユーザーを限り、保存期間を決めておきます。
  • 形式を変えると、ログを読む側のツール(GoAccess、awk のスクリプト、監視の設定など)が項目の位置を誤ることがあります。既存のファイル名の形式を変えるより、別の名前のファイルに書き始めるほうが安全です。
  • エラーログ(error_log)の形式は変えられません。変えられるのはアクセスログだけです。

ログを集計して見るには GoAccessでNginxのアクセスログを分析する方法、条件の書き方は Nginxのmapモジュールの使い方 を参照してください。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲