- Spring
Spring Boot構造化ログ|logging.structuredでJSON出力
Spring Boot 3.4 以降は、logging.structured.format.console に ecs・gelf・logstash のどれかを書くだけで、ログが1行1件の JSON になります。 logback.xml の編集も、追加のライブラリも使いません。
logging:
structured:
format:
console: ecs # ecs / gelf / logstash / 自作クラスの完全修飾名
{"@timestamp":"2024-01-01T10:15:00.067462556Z","log":{"level":"INFO","logger":"org.example.Application"},"process":{"pid":39599,"thread":{"name":"main"}},"service":{"name":"simple"},"message":"No active profile set, falling back to 1 default profile: \"default\"","ecs":{"version":"8.11"}}
(上は Spring Boot リファレンスに載っている ECS 形式の出力例です。)
| 値 | 形式 | 主な送り先 | 時刻の形 |
|---|---|---|---|
ecs |
Elastic Common Schema 8.11 | Elasticsearch・Kibana・OpenTelemetry 系 | @timestamp(UTC の ISO 8601) |
gelf |
Graylog Extended Log Format 1.1 | Graylog | timestamp(エポック秒、小数3桁) |
logstash |
logstash-logback-encoder 互換 | Logstash・Fluentd など | @timestamp(実行環境のタイムゾーン付き) |
確認環境:Spring Boot 4.1.1・3.5.0・3.4.0 のソース(GitHub のタグ)を clone し、Logback 用の3つのフォーマッター(ElasticCommonSchemaStructuredLogFormatter など)とそのテストコードで、出力されるフィールドと順番を確かめました。設定はリファレンス「Logging」の Structured Logging の章で確認しています(確認日 2026年10月13日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring Boot構造化ログ|logging.structuredでJSON出力」
設定:コンソールとファイル
コンソールは logging.structured.format.console、ファイルは logging.structured.format.file で、別々に決められます。 ファイルに出すには、いつもどおり logging.file.name(または logging.file.path)も要ります。
spring:
application:
name: order-api # ECS の service.name、GELF の host に使われる
version: 1.4.0 # ECS の service.version に使われる
logging:
structured:
format:
console: ecs
file: logstash
ecs:
service:
environment: production
node-name: ap-1
file:
name: logs/app.json
手元では普通の1行ログ、本番だけ JSON にしたいときは、プロファイルで分けます。
# application.yml(共通。何も書かなければ従来のテキスト形式)
---
spring:
config:
activate:
on-profile: prod
logging:
structured:
format:
console: ecs
プロファイルの切り替えは、関連記事「Spring Bootプロファイル切り替え|application.ymlと優先順位」で扱っています。
構造化ログを有効にすると、起動時のバナー(:: Spring Boot ::)は出ません。 Spring Boot のスモークテスト(SampleStructuredLoggingApplicationTests)が、バナーが出ないことを確かめています。
出力フィールド一覧(3形式の比較)
Spring Boot 4.1.1 の Logback 用フォーマッターのソースから、出力される項目を書き出し順に並べました。
| 内容 | ecs | gelf | logstash |
|---|---|---|---|
| 時刻 | @timestamp |
timestamp |
@timestamp |
| 形式の版 | ecs.version("8.11") |
version("1.1") |
@version("1") |
| ログレベル | log.level("INFO") |
level(6)・_level_name |
level・level_value(20000) |
| ロガー名 | log.logger |
_log_logger |
logger_name |
| スレッド名 | process.thread.name |
_process_thread_name |
thread_name |
| プロセス ID | process.pid |
_process_pid |
(なし) |
| メッセージ | message |
short_message |
message |
| サービス名 | service.name |
host |
(なし) |
| サービスの版 | service.version |
_service_version |
(なし) |
| 環境・ノード | service.environment・service.node.name |
(なし) | (なし) |
| MDC・キーと値 | そのままの名前(. で入れ子) |
先頭に _ を付ける |
そのままの名前 |
| マーカー | tags(配列) |
(なし) | tags(配列) |
| 例外 | error.type・error.message・error.stack_trace |
full_message・_error_type・_error_message・_error_stack_trace |
stack_trace |
表の log.level などは、JSON の入れ子を . でつないだ書き方です(ECS では {"log":{"level":"INFO"}})。
ecs の出力(4.1.1)
フォーマッターは、@timestamp → log → process → service → message → MDC・キーと値 → error → tags → ecs の順に書き出します。テストコード(ElasticCommonSchemaStructuredLogFormatterTests)が使う値を、この順に並べると次のようになります。
{"@timestamp":"2024-07-02T08:49:53Z","log":{"level":"INFO","logger":"org.example.Test"},"process":{"pid":1,"thread":{"name":"main"}},"service":{"name":"name","version":"1.0.0","environment":"test","node":{"name":"node-1"}},"message":"message","mdc-1":"mdc-v-1","kv-1":"kv-v-1","ecs":{"version":"8.11"}}
| 設定 | 出力先 | 指定しないとき |
|---|---|---|
logging.structured.ecs.service.name |
service.name |
spring.application.name |
logging.structured.ecs.service.version |
service.version |
spring.application.version |
logging.structured.ecs.service.environment |
service.environment |
出ない |
logging.structured.ecs.service.node-name |
service.node.name |
出ない |
ECS では、MDC のキーに . があると入れ子になります。 テストでは、MDC の a1.b1.c1 が {"a1":{"b1":{"c1":"A1B1C1"}}} になることを確かめています。
gelf の出力(4.1.1)
{"version":"1.1","short_message":"message","timestamp":1719910193.000,"level":6,"_level_name":"INFO","_process_pid":1,"_process_thread_name":"main","host":"name","_service_version":"1.0.0","_log_logger":"org.example.Test","_mdc-1":"mdc-v-1","_kv-1":"kv-v-1"}
(テストコード GraylogExtendedLogFormatStructuredLogFormatterTests の値を、ソースの書き出し順に並べたものです。)
GELF ならではの決まりが、ソースに入っています。
| 決まり | ソースの動き |
|---|---|
level は数値 |
syslog の重大度に変換(INFO は 6) |
timestamp はエポック秒 |
ミリ秒を小数3桁で出す(1719910193.123 など) |
追加の項目は _ 始まり |
MDC の mdc-1 は _mdc-1 になる |
| 使えない名前 | ^[\w.\-]*$ に合わない名前と、id・_id は出さずに WARN を出す |
| 空のメッセージ | short_message を (blank) にする(Graylog 6 で空がエラーになるため、とコメントあり) |
host |
logging.structured.gelf.host。無ければ spring.application.name |
logstash の出力(4.1.1)
{"@timestamp":"2024-01-01T10:15:00.111037681+02:00","@version":"1","message":"No active profile set, falling back to 1 default profile: \"default\"","logger_name":"org.example.Application","thread_name":"main","level":"INFO","level_value":20000}
(リファレンスの出力例です。)@timestamp は、実行環境のタイムゾーンのオフセット付きです(ソースでは ZoneId.systemDefault() を使用)。コンテナで UTC にそろえたいときは、TZ 環境変数や -Duser.timezone=UTC で JVM のタイムゾーンを決めます。ECS の @timestamp は UTC(末尾 Z)です。
3.4 と 3.5 以降で、ECS の JSON の形が違う
Spring Boot 3.4.0 の ECS は、"log.level" のように . を含む名前の平らな JSON でした。3.5.0 以降は {"log":{"level":...}} の入れ子です。 3.4.0 と 3.5.0 のソースを比べて確かめました。
// 3.4.x(平ら)
{"@timestamp":"…","log.level":"INFO","process.pid":39599,"process.thread.name":"main","service.name":"simple","log.logger":"org.example.Application","message":"…","ecs.version":"8.11"}
// 3.5.0 以降(入れ子)
{"@timestamp":"…","log":{"level":"INFO","logger":"org.example.Application"},"process":{"pid":39599,"thread":{"name":"main"}},"service":{"name":"simple"},"message":"…","ecs":{"version":"8.11"}}
(3.4.0 のソースの書き出し順と項目名から作った形です。値は省略しています。)
jq '.["log.level"]' のように平らな名前で読んでいるスクリプトやダッシュボードは、3.5 に上げると値が取れなくなります。 jq '.log.level' に書き換えます。
版ごとに使えるプロパティも違います。
| プロパティ | 3.4 | 3.5 | 4.0・4.1 |
|---|---|---|---|
logging.structured.format.console・.file |
あり | あり | あり |
logging.structured.ecs.service.*・gelf.* |
あり | あり | あり |
logging.structured.json.include・exclude・rename・add |
あり | あり | あり |
logging.structured.json.customizer |
1クラス | 複数クラス | 複数クラス |
logging.structured.json.stacktrace.* |
なし | あり | あり |
logging.structured.json.context.include・prefix |
なし | あり | あり |
| ECS の例外の項目名 | error.stack_trace(平ら) |
error の入れ子 |
error の入れ子 |
MDC とキー・値を JSON に足す
MDC(スレッドに結び付けた値)と、SLF4J の fluent API の addKeyValue は、3形式とも JSON の項目になります。
package com.example.orderapi;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
private static final Logger log = LoggerFactory.getLogger(OrderController.class);
@PostMapping("/orders/{id}/ship")
public void ship(@PathVariable String id) {
MDC.put("order.id", id); // ECS では {"order":{"id":"…"}}
try {
log.atInfo()
.setMessage("出荷しました")
.addKeyValue("carrier", "yamato")
.addKeyValue("items", 3)
.log();
} finally {
MDC.remove("order.id");
}
}
}
ECS では、MDC の order.id が入れ子になり、carrier と items がそのまま項目になります。GELF では _order.id・_carrier・_items です。
| 書き方 | ecs | gelf | logstash |
|---|---|---|---|
MDC.put("order.id", "A1") |
"order":{"id":"A1"} |
"_order.id":"A1" |
"order.id":"A1" |
.addKeyValue("carrier", "yamato") |
"carrier":"yamato" |
"_carrier":"yamato" |
"carrier":"yamato" |
Micrometer Tracing を入れていると、MDC の traceId・spanId も同じように出ます。トレースの仕組みは、関連記事「AWS X-Rayの使い方|Lambda・APIのトレースとサービスマップ」でも扱っています。
MDC をまとめて外す・前に名前を付ける(3.5 以降)
logging:
structured:
json:
context:
include: true # false にすると MDC・キーと値を出さない(既定 true)
prefix: ctx # ECS は ctx.〜 の入れ子、logstash は ctx_〜 になる
ソースでは、ECS は . でつないだうえで入れ子に、logstash は _ でつないだ平らな名前にしています。
例外(スタックトレース)の出方
log.error("失敗しました", e) のように例外を渡すと、形式ごとに次の項目が付きます(4.1.1 のソースとテスト)。
| 形式 | 項目 | 中身 |
|---|---|---|
| ecs | error.type |
例外のクラス名(java.lang.RuntimeException) |
| ecs | error.message |
例外のメッセージ |
| ecs | error.stack_trace |
スタックトレース全文(1つの文字列。改行は \n) |
| gelf | full_message |
メッセージ+空行+スタックトレース |
| gelf | _error_type・_error_message・_error_stack_trace |
ecs と同じ中身 |
| logstash | stack_trace |
スタックトレース全文 |
スタックトレースは1つの JSON 文字列に入るので、ログが複数行に割れません。 CloudWatch Logs などで1件のログとして扱えます。CloudWatch Logs での検索は、関連記事「CloudWatch Logs Insightsのクエリ集|エラー・遅いリクエスト」で扱っています。
長さを抑えたいときは、3.5 以降で次のプロパティが使えます。
logging:
structured:
json:
stacktrace:
root: first # 原因(root)を先に出す。既定は last(Java と同じ順)
max-length: 2048 # 文字数の上限
max-throwable-depth: 30 # 1つのトレースで出すフレーム数の上限
include-common-frames: false
include-hashes: true # スタックトレースのハッシュを付ける
項目の除外・改名・追加
logging:
structured:
json:
exclude: process.pid, log.logger # この項目を出さない
rename:
log.logger: logger_name # その階層での名前を変える
add:
team: payments # 固定の項目を足す
app.region: ap-northeast-1 # ECS では {"app":{"region":…}} になる
| プロパティ | 動き(ソース) |
|---|---|
exclude |
指定したパス(. 区切り)の項目を出さない |
include |
指定したパスだけを出す |
rename |
キーはパス、値はその階層での新しい名前。log.logger: logger_name は "log":{"level":…,"logger_name":…} |
add |
固定値の項目を足す。ECS では名前の . で入れ子になる |
customizer |
StructuredLoggingJsonMembersCustomizer を実装したクラスで、細かく変える |
rename の値は、パスではなく名前です。 上の位置から外へ出したいときは、customizer を使います。Spring Boot のスモークテストは、rename[process.pid]=process.procid と書いた結果、"process.procid" という名前で出ることを確かめています。
自作の形式(StructuredLogFormatter)
JSON 以外の形にもできます。リファレンスとスモークテストにある StructuredLogFormatter の実装です。
package com.example.orderapi.logging;
import ch.qos.logback.classic.pattern.ThrowableProxyConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.springframework.boot.logging.structured.StructuredLogFormatter;
import org.springframework.core.env.Environment;
public class KeyValueLogFormatter implements StructuredLogFormatter<ILoggingEvent> {
private final Long pid;
private final ThrowableProxyConverter throwableProxyConverter;
// コンストラクタの引数は Spring Boot が渡す(Environment・ThrowableProxyConverter など)
public KeyValueLogFormatter(Environment environment, ThrowableProxyConverter throwableProxyConverter) {
this.pid = environment.getProperty("spring.application.pid", Long.class);
this.throwableProxyConverter = throwableProxyConverter;
}
@Override
public String format(ILoggingEvent event) {
StringBuilder sb = new StringBuilder();
sb.append("epoch=").append(event.getInstant().toEpochMilli());
if (pid != null) {
sb.append(" pid=").append(pid);
}
sb.append(" level=").append(event.getLevel());
sb.append(" msg=\"").append(event.getFormattedMessage()).append('"');
if (event.getThrowableProxy() != null) {
sb.append(" error=\"").append(throwableProxyConverter.convert(event)).append('"');
}
return sb.append('\n').toString();
}
}
logging:
structured:
format:
console: com.example.orderapi.logging.KeyValueLogFormatter
logback-spring.xml を自分で書いている場合
自前の Logback 設定があると、logging.structured.format.* を書いても、そのままでは JSON になりません。 encoder を StructuredLogEncoder に替え、Spring Boot が渡す CONSOLE_LOG_STRUCTURED_FORMAT を読みます(Spring Boot に入っている structured-console-appender.xml と同じ形)。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="org.springframework.boot.logging.logback.StructuredLogEncoder">
<format>${CONSOLE_LOG_STRUCTURED_FORMAT}</format>
<charset>${CONSOLE_LOG_CHARSET}</charset>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
<format>ecs</format> と直接書くこともできます。Log4j2 なら <StructuredLogLayout format="${sys:CONSOLE_LOG_STRUCTURED_FORMAT}" charset="${sys:CONSOLE_LOG_CHARSET}"/> です。logback-spring.xml 全体の書き方は、関連記事「Spring Bootログ設定|logback-spring.xml全文と設定対応表」で扱っています。
よくある質問
Q. logstash-logback-encoder(LogstashEncoder)とどちらを使えばいいですか。
A. Spring Boot の logstash 形式は、logstash-logback-encoder の標準フィールド(@timestamp・@version・message・logger_name・thread_name・level・level_value)に合わせた形です。依存を増やさずに済むのが Spring Boot 側、細かな出力設定(プロバイダーの組み合わせなど)が豊富なのが logstash-logback-encoder 側です。
Q. ローカルで JSON が読みにくいです。
A. プロファイルで本番だけ JSON にするか、jq で整形して読みます(./mvnw spring-boot:run | jq -R 'fromjson? // .')。fromjson? で JSON でない行もそのまま出せます。
Q. 日本語のメッセージは文字化けしませんか。
A. JSON は logging.charset.console の文字コードで書かれます。4.1.1 のソースでは、指定が無いとき、コンソールがあればその文字コード、無ければ JVM の既定の文字コードを使います。コンテナなどで化ける場合は、logging.charset.console: UTF-8 と明示し、受け取る側も UTF-8 にそろえます。
Q. どんな項目を出すべきかの設計は? A. 必須フィールドの考え方は、関連記事「構造化ログ(JSON)設計|必須フィールド一覧とPython・Node実装例」で扱っています。個人情報のマスクは、関連記事「ログ設計の出力項目一覧|ログ設計書テンプレートとマスク」で扱っています。
まとめ
logging.structured.format.console: ecsだけで JSON ログになる(Spring Boot 3.4 以降)- 形式は ecs・gelf・logstash の3つ。時刻・レベル・MDC の名前の付け方が違う
- 3.4 の ECS は平らな名前、3.5 以降は入れ子。
jqやダッシュボードの参照先に注意 - MDC と
addKeyValueは項目になる。ECS では.入りの名前が入れ子になる - 除外・改名・追加は
logging.structured.json.*、スタックトレースの長さはjson.stacktrace.*(3.5 以降)
参考・出典
確認日はいずれも 2026年10月13日です。
- Spring Boot Reference「Logging」(Structured Logging):https://docs.spring.io/spring-boot/reference/features/logging.html#features.logging.structured
- Spring Blog「Structured logging in Spring Boot 3.4」:https://spring.io/blog/2024/08/23/structured-logging-in-spring-boot-3-4
- Spring Boot ソース(タグ v4.1.1、logging/logback・logging/structured):https://github.com/spring-projects/spring-boot/tree/v4.1.1/core/spring-boot/src/main/java/org/springframework/boot/logging
- Elastic Common Schema 8.11 Reference:https://www.elastic.co/guide/en/ecs/8.11/ecs-reference.html
- Graylog「GELF」:https://go2docs.graylog.org/current/getting_in_log_data/gelf.html
- logstash-logback-encoder「Standard Fields」:https://github.com/logfellow/logstash-logback-encoder#standard-fields
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring Boot構造化ログ|logging.structuredでJSON出力」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。