Nginxが出力するログを標準形式からJSON形式に変更する方法を説明します。
なぜJSON形式にするのか?
UbuntuでWebサーバーをNginxで運用していると、アクセスログを分析して「どのページが人気か」「エラーが出ていないか」といったことを確認したくなることがあります。
しかし、標準のログ形式(combined)はテキストベースのため、ツールで解析する際に複雑な設定が必要になることが多くなります。
そこで今回は、NginxのログをJSON形式に変更して、プログラムや解析ツールから扱いやすくする方法を紹介します。
JSON形式に変更するメリット・デメリット
ログの出力形式をJSONに変更することで、以下のようなメリット・デメリットがあります。
メリット
- 解析ツールとの相性が抜群: GoAccess、alp、Datadog、ELKスタック(Elasticsearch, Logstash, Kibana)、CloudWatch Logsなど、モダンなツールの多くはJSONをそのまま解釈することができる。
- 項目の追加が簡単: ログに新しい項目(応答時間やキャッシュの状態など)を追加しても、JSONならキーを追加するだけなので、既存の解析処理が壊れにくい。
- 文字化けに強い: escape=json という設定を使えば、日本語などのマルチバイト文字や特殊記号も安全にエスケープして記録できる。
デメリット
- ファイルサイズが少し増える: 毎回「キー名(項目名)」を書き込むため、標準形式に比べるとディスク容量を多く消費する。
- 人間には読みづらい: catやtailでログを直接見ると、1行が非常に長くて読みにくくなる。
設定方法
設定は「/etc/nginx/nginx.conf」に「log_format」でログのフォーマットを設定し、各サイトの設定でログの出力フォーマットを指定するという作業を行います。
ログフォーマットの設定(log_format)
「/etc/nginx/nginx.conf」の「http」ブロックに「log_format」設定することで、出力させたい情報をカスタマイズします。
今回はjsonという名前でログのフォーマットを設定してみます。
※出力させたい項目は、必要に応じて追加することが出来ます。
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# JSON形式のログフォーマット追加
log_format json escape=json '{'
'"time_local":"$time_local",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":"$status",'
'"body_bytes_sent":"$body_bytes_sent",'
'"request_time":"$request_time",'
'"http_referer":"$http_referer",'
'"http_user_agent":"$http_user_agent",'
'"http_x_forwarded_for":"$http_x_forwarded_for"'
'}';
access_log /var/log/nginx/access.log main;
# 既存の設定
}
「escape=json」を指定することで、変数に含まれる特殊文字をJSONに適した形式に自動でエスケープしてくれるようになります。(Nginx 1.11.8以降の機能)
これを忘れると、不正なJSON形式になって解析ツールが止まってしまうことがあるので、必ず指定してください。
ログ出力先の設定
作成した「json」フォーマットを使うように、サイト個別の設定を行います。
例えば、バーチャルホストのサイトごとの設定ファイル(/etc/nginx/conf.d/vpslife.conf)の「server」ブロック内で、以下のようにフォーマット名を指定します。
server {
# 既存の設定
access_log /var/log/nginx/vpslife_access.log json ;
# 既存の設定
}
設定の反映
設定が終わったら、「nginx -t」で設定ファイルの文法チェックを行います。
以下のように、「syntax is ok」と表示されれば文法的には問題はありません。
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Nginxを再起動して設定を反映させます。
$ sudo systemctl restart nginx
ログの出力確認
実際にWebサイトにアクセスして、ログがJSON形式になっているか確認します。
$ sudo tail -f /var/log/nginx/vpslife_access.log
{"time_local":"01/May/2026:11:15:29 +0900","remote_addr":"xxx.xxx.xxx.xxx","request":"GET / HTTP/1.1","status": "304","body_bytes_sent":"0","request_time":"0.005","http_referrer":"https://vpslife.server-memo.net/","http_user_agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36","http_x_forwarded_for":""}
「jq」コマンドでログを見やすくする
JSON形式のログはそのままだと読みづらいですが、「jq」コマンドを使えばターミナルでも劇的に見やすくなります。
実際に「jq」コマンドを使用して、ログを表示させてみます。
$ sudo tail -f /var/log/nginx/vpslife_access.log | jq .
{
"time_local": "01/May/2026:11:15:29 +0900",
"remote_addr": "xxx.xxx.xxx.xxx",
"request": "GET / HTTP/1.1",
"status": "304",
"body_bytes_sent": "0",
"request_time": "0.005",
"http_referrer": "https://vpslife.server-memo.net/",
"http_user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
"http_x_forwarded_for": ""
}
このように、ログの構造が一目でわかるようになり解析がしやすくなりました。
jqを使ったフィルタリング例
jqコマンドのselectを使用すると、特定の条件に一致するログだけを抽出して表示(フィルタリング)することもできます。
以下の例は、ステータスコードが「404(Not Found)」のものだけをリアルタイムで表示する例です。
「--unbuffered」は、バッファリングによって表示が遅れるのを防ぐために使用しています。
$ sudo tail -f /var/log/nginx/vpslife_access.log | jq --unbuffered 'select(.status == "404")'
{
"time_local": "01/May/2026:21:56:27 +0900",
"remote_addr": "xxx.xxx.xxx.xxx",
"request": "GET /xxx/ HTTP/1.1",
"status": "404",
"body_bytes_sent": "555",
"request_time": "0.006",
"http_referer": "",
"http_user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36",
"http_x_forwarded_for": ""
}
必要な項目のみ出力させる
出力させる項目を指定することが出来ます。
以下は、「time_local」「remote_addr」「status」項目のみを表示させる例となります。
$ sudo tail -f /var/log/nginx/vpslife_access.log | jq -r --unbuffered '"\(.time_local) \(.remote_addr) \(.status)"' 01/May/2026:20:42:53 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:42:54 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:42:54 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:42:54 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:42:54 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:43:22 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:43:22 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:43:22 +0900 xxx.xxx.xxx.xxx 200 01/May/2026:20:43:23 +0900 xxx.xxx.xxx.xxx 304 01/May/2026:21:56:27 +0900 xxx.xxx.xxx.xxx 404
- 「-r」オプション:文字列を囲むダブルクォーテーションを消してくれています。
- "\(.変数名)":文字列の中で変数を展開する書き方で、好きな区切り文字を自由に挟めるので、自分好みの表示形式を指定できます。
このように表示形式を変更することも可能です。
$ sudo tail -f /var/log/nginx/vpslife_access.log | jq -r --unbuffered '"[\(.time_local)] \(.remote_addr) Status:\(.status)"' [01/May/2026:20:42:54 +0900] xxx.xxx.xxx.xxx Status:200 [01/May/2026:20:42:54 +0900] xxx.xxx.xxx.xxx Status:200 [01/May/2026:20:42:54 +0900] xxx.xxx.xxx.xxx Status:200 [01/May/2026:20:43:22 +0900] xxx.xxx.xxx.xxx Status:200 [01/May/2026:20:43:22 +0900] xxx.xxx.xxx.xxx Status:200 [01/May/2026:20:43:22 +0900] xxx.xxx.xxx.xxx Status:200 [01/May/2026:20:43:23 +0900] xxx.xxx.xxx.xxx Status:304 [01/May/2026:21:56:27 +0900] xxx.xxx.xxx.xxx Status:404 [01/May/2026:22:34:38 +0900] xxx.xxx.xxx.xxx Status:404 [01/May/2026:22:35:29 +0900] xxx.xxx.xxx.xxx Status:404
「select」と表示項目指定の組み合わせ
「select」でフィルタリングした結果を「|」(パイプ)で渡すことで、必要な項目のみを表示するということも出来ます。
以下は、「status」が「404」のログをフィルタリングして、「time_local」「remote_addr」「request」「status」項目のみを抽出して表示させる例となります。
$ sudo tail -f /var/log/nginx/vpslife_access.log | jq -r --unbuffered 'select(.status == "404") | "\(.time_local) \(.remote_addr) \(.request) \(.status)"' 01/May/2026:22:34:38 +0900 xxx.xxx.xxx.xxx GET /robots.txt HTTP/1.1 404 01/May/2026:22:35:29 +0900 yyy.yyy.yyy.yyy GET /test/ HTTP/1.1 404


コメント