Elastic Common Schema と OpenTelemetry Semantic Convention を通して Structured Logging について学んだ

Background image generated by Grok

背景

Structured logging in Spring Boot 3.4 で、 Structured logging のサポートが発表されていた。

Spring Boot として提供される機能は分かったが、そもそもの Structured logging、構造化ロギングについて理解出来ていなかったので、調べることにした。

とっかかりとして、 Spring Boot がサポートする Structured logging フォーマットとしていの一番に挙げられている Elastic Common Schema (ECS) を通して調べたことをまとめる。

Structured logging とは

Getting Started | Elastic Common Schema (ECS) Reference [8.16] | Elastic には以下のようにある:

ECS enables and encourages users to normalize event data in order to better analyze, visualize, and correlate their events. Collected events can be normalized at ingest time, consistently searched across indices, and visualized predictably.

Note that when adopting an Elastic solution, such as Observability or Security, all events will map to ECS out of the box. Elastic provides an extensive set of integrations to simplify ingesting your data sources.

特に後半は特定の Elastic ソリューションとの連携について語られているので、一見すると Spring Boot が Elastic 製品特化の機能をサポートしたかのように思えてしまうかもしれない。

しかし、イベントデータの標準化によって Observability 等で一貫性の恩恵を得られるのは、何も Elastic 製品利用時に限ったことではないはず。

Elastic スタックと共に語られる事の多い Grafana の分散トレーシングソリューションである Tempo のドキュメント には、vendor-agnositc な Observability フレームワークである OpenTelemetry が提供する OpenTelemetry Semantic Conventions 1.29.0 | OpenTelemetry について言及されている。

OpenTelemetry Semantic Conventions に従うことの恩恵は以下のように述べられている:

The benefit to using Semantic Conventions is in following a common naming scheme that can be standardized across a codebase, libraries, and platforms. This allows easier correlation and consumption of data.

異なるシステム間のデータでも共通の規約に従ってさえいれば、突合などし易いし便利だよね、というのは、 ECS と同様に見える。

目指すものは同じということで、実際 ECS と OTel SemConv は統合していく ことが発表されている。

Elastic Common Schema と OpenTelemetry Semantic Convention の統合

両者の関係は oteps/text/0199-support-elastic-common-schema-in-opentelemetry.md at main · open-telemetry/oteps の図を見ると分かり易い。

提供価値被ってるし、統合した方がよいよね、ということで Elastic と OpenTelemetry からそれぞれ以下のアナウンスがあった:

以下のように、 ECS への追加は OTel SemConv からのバックポートとして行うようなことが案内されていたり、

https://github.com/elastic/ecs/blob/main/CONTRIBUTING.md#ecs-donation-to-opentelemetry

OpenTelemetry Project Roadmap | OpenTelemetry には Further Stabilizing Semantic Conventions が most important major initiatives として挙げられているので、統合に向けて確実に進んでいるようではあるが、現在のところは安定して利用出来る具体的な成果物はまだ無いようだ。

どちらかがいつか使えなくなるような破壊的な統合というわけではなさそうなので、今のところはこの両者があり、相反するものではなくいずれ統合される予定、ということを認識しておくだけで問題無さそうである。

Spring Boot での Elastic Common Schema 利用

先に挙げた Structured logging in Spring Boot 3.4 に拠れば、 logging.structured.format.console=ecs と設定するだけで以下のようなログが出力されるようになる:

{
  "@timestamp": "2024-07-30T08:41:10.561295200Z",
  "log.level": "INFO",
  "process.pid": 67455,
  "process.thread.name": "main",
  "service.name": "structured-logging-demo",
  "log.logger": "com.example.structured_logging_demo.StructuredLoggingDemoApplication",
  "message": "Started StructuredLoggingDemoApplication in 0.329 seconds (process running for 0.486)",
  "ecs.version": "8.11"
}

これらのフィールドは以下のように ECS で定義されている:

実は Elastic 提供の elastic/ecs-logging-java at v1.6.0 というものもあるのだが、 Spring Boot はこれを使っているわけ ではない独自に実装している

ecs-logging-java の方には process.* のフィールドは含まれないなど、出力項目にも違いがある模様。

ただし、いずれもフィールド名は ECS に則っている。

ecs-logging-java の Logback 版には、以下の issue が報告されている: Support structured logging with Logback · Issue #49 · elastic/ecs-logging-java

この対応は Spring Boot の実装版には入っているようす。

他にも、依存している Logback のバージョンが ecs-logging-java は UNMAINTAINED な 1.2 系だったりもするので、 Spring Boot を使うならば、 ecs-logging-java ではなく、組込みの機能を使っておいた方がよさそうだ。

Spring Boot での OpenTelemetry 利用

ECS と OTel SemConv 統合作業は今まさに進行中という感じで、ログに関しても open-telemetry/community: OpenTelemetry community content から辿れる OpenTelemetry Log SIG - Google ドキュメント にて、 Logs/Events guidance on what to put to attributes vs body · Issue #1651 · open-telemetry/semantic-conventions が挙げられていた事からも、活発に議論がされているようす。

いずれ ECS と OTel SemConv は同等になっていくと思われるので、目下は ECS を利用しておけば問題はなさそうだが、統合先となる OTel SemConv を今から使っていく、ということは出来るのだろうか。

以下の議論を見る限り、 OTel SemConv への対応は消極的?

Micrometer Tracing with OTEL Bridge does not honour Semantic Conventions · Issue #34132 · spring-projects/spring-boot Implement OTel HTTP semantic conventions · Issue #929 · micrometer-metrics/tracing

Java の artifact がまだ alpha 状態であることが理由だそう。 確かに、 stable な spec に対する artifact でも 1.29.0-alpha のようなバージョニングになっている。

ただし、上記はメトリクスの命名規約の文脈なので、 ECS 対応のように独自実装で対応されることはあるのかもしれない。

現状でも、 OpenTelemetry の Appender を利用することは可能。 https://docs.spring.io/spring-boot/reference/actuator/loggers.html#actuator.loggers.opentelemetry

これは OpenTelemetry Protocol (OTLP) に従って、 gRPC や HTTP などで OpenTelemetry Specification に則ったデータを送信する機能で、 OTel SemConv のような標準規格というよりは OpenTelemetry という実装に特化した機能。

これで利用される Logs Data Model は OTel SemConv に則って定義されていたりするので、無関係というわけではない。

実際に先程のような Spring Boot 起動ログを OTLP で送信した内容を JSON で表現すると以下のようになる:

{
  "body": "Started PetclinicApplicationKt in 4.534 seconds (process running for 4.958)",
  "severity": "INFO",
  "attributes": {
    "code.filepath": "StartupInfoLogger.java",
    "code.function": "logStarted",
    "code.lineno": 59,
    "code.namespace": "org.springframework.boot.StartupInfoLogger",
    "process.pid": "388042",
    "thread.name": "restartedMain"
  },
  "resources": {
    "service.name": "petclinic-fullstack",
    "telemetry.sdk.language": "java",
    "telemetry.sdk.name": "opentelemetry",
    "telemetry.sdk.version": "1.43.0"
  },
  "instrumentation_scope": {
    "name": "net.yewton.petclinic.PetclinicApplicationKt"
  }
}

構造が入れ子になっていたりするが、末端のフィールド名は OTel SemConv に準拠している。

https://github.com/yewton/petclinic/tree/otlp に確認に利用したプロジェクトがある。

以下で述べられているように、このように適切に構造化してデータ連携する場合は OTLP で直接送信するのがお手軽だが、 この方式はアプリケーションプロセスから通信処理をする為、オーバーヘッドがある。

ファイルや標準出力に吐いたログを別プロセスで収集する方が当然パフォーマンスメリットはあるが、その為に いい感じに構造化・シリアライズしてログ出力し、それを OTLP で送信するような機構は提供されていない。

https://opentelemetry.io/docs/languages/java/instrumentation/#log-instrumentation

実際には Fluent Bit などのログ収集ソリューションと併用することになりそう。

https://docs.fluentbit.io/manual/pipeline/outputs/opentelemetry

参考までに、上記の JSON は OTLP で収集したデータを加工したもので、実際は以下のようなデータが送信されている:

ResourceLog #0
Resource SchemaURL:
Resource attributes:
     -> service.name: Str(petclinic-fullstack)
     -> telemetry.sdk.language: Str(java)
     -> telemetry.sdk.name: Str(opentelemetry)
     -> telemetry.sdk.version: Str(1.43.0)
ScopeLogs #3
ScopeLogs SchemaURL:
InstrumentationScope net.yewton.petclinic.PetclinicApplicationKt
LogRecord #0
ObservedTimestamp: 2025-01-04 13:41:59.821289163 +0000 UTC
Timestamp: 2025-01-04 13:41:59.820892799 +0000 UTC
SeverityText: INFO
SeverityNumber: Info(9)
Body: Str(Started PetclinicApplicationKt in 4.534 seconds (process running for 4.958))
Attributes:
     -> code.filepath: Str(StartupInfoLogger.java)
     -> code.function: Str(logStarted)
     -> code.lineno: Int(59)
     -> code.namespace: Str(org.springframework.boot.StartupInfoLogger)
     -> process.pid: Str(388042)
     -> thread.name: Str(restartedMain)
Trace ID:
Span ID:
Flags: 0

終わりに

ECS や OTel SemConv といった標準的なスキーマが定まっていく動きはとてもありがたい。

長期的には OTel SemConv がデファクトスタンダードになっていきそうな勢いではあるが、 現在の状況はかなり流動的な為、実装としては Spring Boot でもサポートされる ECS を採用しておけば問題なさそう。

規約に則ってさえいれば、可観測性やセキュリティといった非機能要件はこうしておけば OK! …というような知見が今後貯まっていくはずなので期待。

yewton
yewton
ソフトウェアエンジニア

父親兼エンジニア

comments powered by Disqus
前へ