トップ ブログ SpringBoot4のオブザーバビリティ機能を試してみる

SpringBoot4のオブザーバビリティ機能を試してみる

本記事は、NRIエンジニアによって2025年12月7日にQiitaに投稿された記事です。

0. はじめに

こんにちは。今年もAdvent Calendarの時期になりました。今回も何か書きたいなぁと思い、先月2025年11月に正式リリースされたSpringBoot4.0について調べていたところ、OpenTelemetryに関する新しい機能があったので試してみます。

この記事ではObservability、OpenTelemetryについて書いていきます。これらについてChatGPTに説明してもらったので、単語に馴染みのない方は以下をご参照ください。もしくはOpenTelemetry公式ドキュメント をご参照ください。

▼Observability(オブザーバビリティ/可観測性)とは
システムの内部状態を、外から得られるデータ(出力)だけでどれだけ深く理解できるかを示す性質のことです。
Observabilityは、主に以下の3種類のデータ(テレメトリデータ)を組み合わせて実現されます。
・メトリクス (Metrics):CPU使用率やリクエスト数など、定期的に測定される数値データ
・ログ (Logs):特定のイベントが発生した際のテキスト記録
・トレース (Traces):あるリクエストが、複数のサービスをまたいで処理されていく一連の流れを可視化したもの

▼OpenTelemetryとは
Observabilityを実現するために、上記3種類のテレメトリデータを収集・処理・転送するための、標準規格であり、ツールキットです。

1. Spring Boot × OpenTelemetry

SpringBoot4.0のリリースノートには「OpenTelemetry starter」というセクションがあり、以下のような記載があります。

A new starter, spring-boot-starter-opentelemetry has been added.

SpringBootでOpenTelemetryを簡単に使うための機能spring-boot-starter-opentelemetryが追加されたようです。
こちらについて以下のSpringの記事に詳細が書いてあったので、今回はこれを見ながらいろいろ試してみようと思います。

この記事ではspring-boot-starter-opentelemetryについて以下のように紹介していました。

With Spring Boot 4.0, we're introducing a new Spring Boot Starter for OpenTelemetry. It is called spring-boot-starter-opentelemetry (we're really clever with those names, aren't we) and is selectable via the "OpenTelemetry" dependency on start.spring.io.

spring-boot-starter-opentelemetrySpring Initializrで対応済みなので、簡単に雛形のアプリが作れそうです。

(余談)もう一方のOpenTelemetryのSpring Boot starterについて

OpenTelemetryからもSpring Boot starterなる機能が提供されています。こちらの名称はopentelemetry-spring-boot-starterです。ややこしい。こっちに関しては昨年のAdvent Calendarで以下のような記事を執筆しているので、良ければご覧ください。

2. Spring InitializrとAIでアプリを作ってみる

早速トレースとか見てみたいところですが、まずは元となるアプリを作っていこうと思います。
Spring Initializr で以下のような構成のプロジェクトを作りました。

IDEを開いて、ChatGPTさんに以下のようにお願いしてみます。

以下の要件を満たす簡単なSpringアプリを作ってください。
・タスクの追加、削除ができるTODOアプリであること
・Controller、Service、Daoの3層構造のWebアプリケーションであること
・DaoクラスはH2DBとやりとりすること
・依存は追加しないこと

体裁が崩れているところなど少し修正をお願いしたのち、以下のようなアプリができました。

適当にタスクを追加してみます。

「完了」と押すと取り消し線が引かれるようです。

「未完了に戻す」「削除」も正常に動作します。特にエラーは起きていなさそうです。AIに感謝です。

一方、ターミナルを見ると以下のようなWARNが定期的(デフォルトだと1分毎)に出力されています。

2025-11-29T13:59:17.911+09:00  WARN 11480 --- [demo] [trics-publisher] [                                                 ] i.m.registry.otlp.OtlpMeterRegistry      : Failed to publish metrics to OTLP receiver (context: url=http://localhost:4318/v1/metrics, resource-attributes={service.name=demo})

java.lang.Exception: java.net.ConnectException: Connection refused: getsockopt
        at io.micrometer.registry.otlp.OtlpHttpMetricsSender.send(OtlpHttpMetricsSender.java:61) ~[micrometer-registry-otlp-1.16.0.jar:1.16.0]
        at io.micrometer.registry.otlp.OtlpMeterRegistry.publish(OtlpMeterRegistry.java:190) ~[micrometer-registry-otlp-1.16.0.jar:1.16.0]

どうやらspring-boot-starter-opentelemetryによってメトリクス情報を送信しようとしているようです。まだ受信するものを用意していないので、デフォルトの送信先http://localhost:4318/v1/metricsに送信できずWARNが出力されています。トレースやログなど、そのほかのシグナルに関しては出力がないので、デフォルトでは送信していなさそうです。

以降はコンテナ環境で、テレメトリデータ送信先のバックエンドを立てたうえでspring-boot-starter-opentelemetryの設定を行っていこうと思います。

3. spring-boot-starter-opentelemetryを試してみる

3-1. 構成図

こんな感じの構成でコンテナを立てていきます。

collectorOpenTelemetryのコレクタのことです。jaegerprometheuslokigrafanaは、テレメトリデータを可視化するためのツールです。今回はトレース、メトリクス、ログのすべてを可視化できるGrafanaをメインで使っていきます。また、実線がテレメトリデータの流れを、破線がGrafanaのデータアクセスを表しています。

3-2. メトリクス

ここではメトリクスに関しての設定を行っていこうと思います。

まずはメトリクスデータの送信先を設定します。management.otlp.metrics.export.urlというプロパティで指定します。デフォルトでは以下のように設定されているようです。

src/main/resources/application.yml

management:
    otlp:
        metrics:
            export:
                url: http://localhost:4318/v1/metrics  ## デフォルト

spring-boot-starter-opentelemetryでは、Micrometerで取れるメトリクスに加えて、OpenTelemetryが定めるセマンティック規約で仕様が安定しているメトリクスの実装が用意されているようです。
以下のBean設定により、OpenTelemetryで仕様が定まっているメトリクスが取れるようになります。簡単に設定できて便利ですが、将来的にはこれらの設定も不要になる見込みのようです。

OpenTelemetryConfiguration.java

@Configuration(proxyBeanMethods = false)
public class OpenTelemetryConfiguration {

    @Bean
    OpenTelemetryServerRequestObservationConvention openTelemetryServerRequestObservationConvention() {
        return new OpenTelemetryServerRequestObservationConvention();
    }

    @Bean
    OpenTelemetryJvmCpuMeterConventions openTelemetryJvmCpuMeterConventions() {
        return new OpenTelemetryJvmCpuMeterConventions(Tags.empty());
    }

    @Bean
    ProcessorMetrics processorMetrics() {
        return new ProcessorMetrics(List.of(), new OpenTelemetryJvmCpuMeterConventions(Tags.empty()));
    }

    @Bean
    JvmMemoryMetrics jvmMemoryMetrics() {
        return new JvmMemoryMetrics(List.of(), new OpenTelemetryJvmMemoryMeterConventions(Tags.empty()));
    }

    @Bean
    JvmThreadMetrics jvmThreadMetrics() {
        return new JvmThreadMetrics(List.of(), new OpenTelemetryJvmThreadMeterConventions(Tags.empty()));
    }

    @Bean
    ClassLoaderMetrics classLoaderMetrics() {
        return new ClassLoaderMetrics(new OpenTelemetryJvmClassLoadingMeterConventions());
    }

}

設定は以上です。メトリクスを確認してみましょう。
Grafanaでは以下のように確認できました。

私の環境ではBean追加前後で取得できるメトリクスが69種類から78種類に増えました。Beanを追加せずとも、MicrometerのAutoConfigによりかなりのメトリクスが取れるようです。
どんなメトリクスが取れるのか興味のある方は以下をご参照ください。

ちなみに、spring-boot-starter-opentelemetryのAutoConfigではメトリクス用のAPIとしてOpenTelemetryのAPIを用意していないようです。MicrometerのAPIを使用することが推奨されています。

3-3. トレース

ここではトレースに関しての設定を行っていこうと思います。
メトリクス同様に、以下の設定値を使用してトレースの送信先を設定します。

src/main/resources/application.yml

management:
    opentelemetry:
        tracing:
            export:
                otlp:
                    endpoint: http://localhost:4318/v1/traces  ## デフォルト

spring-boot-starter-opentelemetryではMicrometerの機能を利用してトレースを行っています。Micrometerでは、デフォルトでリクエストの10%のみトレースデータを送信するというサンプリング設定が入っているので、以下のように100%送信するよう設定します。

src/main/resources/application.yml

management:
  tracing:
    sampling:
      probability: 1.0  ## デフォルトは0.1

設定は以上です。トレースを確認してみましょう。
アプリにアクセスし、適当なタスクを追加します。

Grafanaではこんな感じで確認できました。

OTLP形式のトレースが取れていることがわかります。

3-3-1. スパンの追加

デフォルトだとサーバスパンが1つあるだけですが、Micrometerのアノテーションを使用することでスパンをメソッド単位で作成することができます。
まず、以下の設定を追加し、アノテーション機能を有効化します。

src/main/resources/application.yml

management:
    observations:
        annotations:
            enabled: true  ## デフォルトはfalse

次に、@Observed アノテーションをメソッドに付与します。これにより、メソッドの処理に対応したスパンが作成されるようになります。
今回はTodoを追加する際に呼ばれるサービスメソッドへアノテーションを付与しました。

    @Observed
    public Todo addTodo(String title) {
        Todo todo = new Todo(title);
        return todoDao.save(todo);
    }

トレースはこんな感じになります。

無事、スパンが1つ増えました。
Micrometerが提供する、@Observed を含むオブザーバビリティ関連のアノテーションについて興味のある方は以下をご参照ください。

3-4. ログの設定

ここではログに関しての設定を行っていこうと思います。
メトリクス、トレース同様に、以下の設定値を使用してログの送信先を設定します。

src/main/resources/application.yml

management:
    opentelemetry:
        logging:
            export:
                otlp:
                    endpoint: http://localhost:4318/v1/logs  ## デフォルト

次に以下の依存を追加します。

build.gradle

         implementation("ch.qos.logback:logback-classic")
         implementation("io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0:2.21.0-alpha")
         implementation("org.springframework.boot:spring-boot-starter-actuator")

そしてlogbackの設定ファイルを作成します。

src/main/resources/logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml"/>

    <appender name="OTEL" class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="OTEL"/>
    </root>
</configuration>

最後に、logbackで使うAppenderにOpenTelemetryインスタンスを渡します。

InstallOpenTelemetryAppender.java

@Component
class InstallOpenTelemetryAppender implements InitializingBean {

    private final OpenTelemetry openTelemetry;

    InstallOpenTelemetryAppender(OpenTelemetry openTelemetry) {
        this.openTelemetry = openTelemetry;
    }

    @Override
    public void afterPropertiesSet() {
        OpenTelemetryAppender.install(this.openTelemetry);
    }
    
}

必要な設定は以上です。
せっかくなのでアプリのログ出力を実装してみようと思います。先ほどスパンを作成したaddTodoメソッド内でログ出力を行います。

    @Observed
    public Todo addTodo(String title) {
        logger.info("Start addTodo. title={}", title);
        Todo todo = new Todo(title);
        Todo saved = todoDao.save(todo);
        logger.info("End addTodo. savedTodoId={}", saved.getId());
        return saved;
    }

それではログを確認してみましょう。
アプリにアクセスし、適当なタスクを追加します。

Grafanaでは以下のように確認できます。

ログメッセージに加えて、いくつかのメタデータも送信されています。スパンIDやトレースIDも含まれていますので、Grafanaの設定を行うことでトレース⇔ログの連携もできるようになります。

4. おわりに

今回はspring-boot-starter-opentelemetryを使ってメトリクス、トレース、ログを生成してみました。簡単な設定でSpringアプリにオブザーバビリティを導入できました。
spring-boot-starter-opentelemetry(by Spring)を使うべきか、opentelemetry-spring-boot-starter(by OpenTelemetry)を使うべきか悩むところですが、そのうちどなたか比較記事を書いてくださるような気がするので、それを待とうと思います。私の印象は、前者は安定した機能が揃っておりMicrometerによる計装も自動で行える、後者は前者よりも多くの機能・様々な通信方式をサポートしている、という感じです。
ちなみにこの記事では自前でアプリを作って試しましたが、公式のサンプルアプリもあるようなので、興味のある方は確認してみてください。
最後まで読んでくださった方、ありがとうございます。

関連OSS

  • Spring Boot
    サポート対象

    Spring Boot

    スプリング ブート。Springフレームワークのアプリケーションを簡単に開発できるオープンソースのJavaフレームワークです。

  • OpenTelemetry
    サポート対象

    OpenTelemetry

    OpenTelemetryは、分散アプリケーションのトレーシングとメトリクス収集のための統一的な規格化とAPIを提供するオープンソースプロジェクトです。

お気軽にお問い合わせください
オープンソースに関するさまざまな課題、OpenStandiaがまるごと解決します。
下記コンテンツも
あわせてご確認ください。