Spring Boot Observability 2026年完全ガイド: OpenTelemetry、分散トレーシング、面接対策

Spring Boot 3.4のObservation API、OpenTelemetryとの連携、分散トレーシングの実装方法を解説。Micrometer TracingとOTLPエクスポートの設定、実践的な面接質問も網羅。

Spring Boot Observability 2026年完全ガイド: OpenTelemetry、分散トレーシング、面接対策

Spring Bootのオブザーバビリティは、ロギング、メトリクス、分散トレーシングを統合し、マイクロサービス全体でリクエストがどのように流れるかを可視化するシステムです。Spring Boot 3以降、フレームワークはMicrometer Tracing(Spring Cloud Sleuthの後継)を採用し、Observation APIを導入しました。このAPIは単一のインスツルメンテーションポイントを提供し、メトリクスとトレースの両方を出力します。

Observation APIパターン

Spring Bootでは、OpenTelemetryを直接呼び出すのではなく、Observation.observe()の使用を推奨しています。1回のインスツルメンテーション呼び出しで、Micrometer経由のメトリクスとOpenTelemetryブリッジ経由のトレースが生成され、コードの重複を減らし、シグナル間でタグ命名の一貫性を確保します。

Observation APIがMicrometerとOpenTelemetryを橋渡しする仕組み

Observation APIは、メトリクスとトレーシングの両方に対するファサードとして機能します。コードがObservation.createNotStarted()を呼び出すと、Spring Bootは観測を登録されたハンドラーにルーティングします。具体的には、Micrometerメトリクス用のMeterObservationHandlerと分散トレース用のTracingObservationHandlerです。このアーキテクチャにより、一度のインスツルメンテーションですべての場所にエクスポートできます。

ブリッジ依存関係micrometer-tracing-bridge-otelは、Micrometer TracingをOpenTelemetryのSDKに接続します。トレースはOpenTelemetryのSdkTracerProviderを通じて流れ、OTLPを介してJaeger、Tempo、または任意のOpenTelemetry互換コレクターにエクスポートされます。

ObservabilityConfig.javajava
@Configuration
public class ObservabilityConfig {

    @Bean
    public ObservationRegistryCustomizer<ObservationRegistry> addLowCardinalityTags() {
        return registry -> registry.observationConfig()
            .observationHandler(new ObservationTextPublisher()); // Logs observations to console
    }
}

上記の設定は、すべての観測をログに記録するObservationHandlerを登録します。本番環境では、自動設定されたハンドラーが追加のコードなしでMicrometerレジストリとOpenTelemetryエクスポーターにデータをプッシュします。

Spring Boot 3.4トレーシングに必要な依存関係

Spring Boot 3.4では、分散トレーシングに明示的な依存関係が必要です。spring-boot-starter-actuatorはObservation APIを提供しますが、トレーシングにはOpenTelemetryブリッジとエクスポーターが必要です。

xml
<!-- pom.xml -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.micrometer</groupId>
        <artifactId>micrometer-tracing-bridge-otel</artifactId>
    </dependency>
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-otlp</artifactId>
    </dependency>
</dependencies>

micrometer-tracing-bridge-otelアーティファクトは、Micrometer TracingをOpenTelemetryのAPIに橋渡しします。opentelemetry-exporter-otlpは、HTTPまたはgRPC経由でOTLPプロトコルを使用してコレクターにスパンを送信します。Spring Bootは依存関係管理を通じてバージョンの整合性を管理します。

JaegerまたはTempoへのOTLPエクスポートの設定

OTLP依存関係が存在する場合、Spring BootはOtlpHttpSpanExporterを自動設定します。エクスポーターはapplication.ymlで指定されたエンドポイントにトレースを送信します。

yaml
# application.yml
management:
  tracing:
    sampling:
      probability: 1.0  # 100% sampling for dev, reduce in production
  otlp:
    tracing:
      endpoint: http://localhost:4318/v1/traces
  opentelemetry:
    resource-attributes:
      service.name: order-service
      deployment.environment: staging

resource-attributesは、すべてのスパンにメタデータを付与します。Grafana TempoJaegerなどのバックエンドは、これらの属性を使用してサービスと環境ごとにトレースをグループ化します。開発中はsampling.probability1.0に設定してすべてのリクエストをキャプチャしますが、本番システムでは通常、ストレージコストを制御するために1%から10%の間でサンプリングします。

低カーディナリティと高カーディナリティタグによるカスタム観測の作成

観測は2種類のタグをサポートします。メトリクス用の低カーディナリティ(HTTPメソッドやステータスコードなどの有限値)とトレース用の高カーディナリティ(ユーザーIDやリクエストIDなどの無制限値)です。

PaymentService.javajava
@Service
public class PaymentService {

    private final ObservationRegistry observationRegistry;

    public PaymentService(ObservationRegistry observationRegistry) {
        this.observationRegistry = observationRegistry;
    }

    public PaymentResult processPayment(PaymentRequest request) {
        return Observation.createNotStarted("payment.process", observationRegistry)
            .lowCardinalityKeyValue("payment.method", request.getMethod().name())  // enum: CARD, BANK_TRANSFER
            .lowCardinalityKeyValue("currency", request.getCurrency())              // bounded: USD, EUR, GBP
            .highCardinalityKeyValue("payment.id", request.getPaymentId())          // unique per request
            .highCardinalityKeyValue("customer.id", request.getCustomerId())        // high cardinality
            .observe(() -> executePayment(request));
    }

    private PaymentResult executePayment(PaymentRequest request) {
        // Payment gateway call
        return new PaymentResult(true, "TXN-" + UUID.randomUUID());
    }
}

低カーディナリティタグはメトリクスとトレースの両方に表示されます。高カーディナリティタグはトレースにのみ表示されます。これは、無制限のディメンションを持つメトリクスがストレージを爆発的に増加させるためです。この区別により、トレースの詳細を豊富に保ちながら、Prometheusのカーディナリティ爆発を防ぎます。

面接質問: タグのカーディナリティ

面接官は、特定のタグをメトリクスから除外すべき理由をよく質問します。答えはカーディナリティに関連しています。メトリクスのユーザーIDタグは、ユーザーごとに1つの時系列を作成し、潜在的に数百万の時系列になる可能性があります。監視バックエンドは高カーディナリティに苦労し、メモリ枯渇やクエリの遅延を引き起こします。トレースはサンプリングを通じて高カーディナリティを処理するため、リクエスト固有の識別子を配置する正しい場所です。

Spring Bootの面接対策はできていますか?

インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。

非同期境界を越えたトレースコンテキストの伝播

分散トレーシングにはコンテキスト伝播が必要です。HTTPヘッダーはサービス間でトレースIDを運びますが、@AsyncメソッドやCompletableFutureチェーンなどの非同期操作は、設定されていない場合コンテキストを失う可能性があります。

Spring Boot 3.4は、設定時にリアクティブストリームと@Asyncメソッドの自動伝播を提供します:

yaml
# application.yml
spring:
  reactor:
    context-propagation: auto
  task:
    execution:
      propagate-context: true

カスタムTaskExecutorビーンの場合は、ContextPropagatingTaskDecoratorでラップします:

AsyncConfig.javajava
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
        executor.initialize();
        return executor;
    }
}

デコレーターは、現在のObservationとトレースコンテキストを非同期スレッドにコピーします。これがないと、非同期タスクで開始されたスパンには親がなく、トレースツリーが壊れます。

@Observedと@NewSpanアノテーションの使用

Spring Boot 3.4は、アノテーションによる宣言的オブザーバビリティをサポートしています。AspectJウィーバーを追加してアノテーション処理を有効にします:

xml
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
yaml
# application.yml
management:
  observations:
    annotations:
      enabled: true
InventoryService.javajava
@Service
public class InventoryService {

    @Observed(name = "inventory.check", contextualName = "checkStock")
    public StockLevel checkStock(String productId) {
        // Database query
        return new StockLevel(productId, 42);
    }

    @NewSpan("inventory.reserve")  // Creates a child span under the current trace
    public ReservationResult reserveStock(String productId, int quantity) {
        // Update inventory
        return new ReservationResult(true, "RES-" + System.currentTimeMillis());
    }
}

@Observedはメトリクス(タイマー)とスパンの両方を作成します。@NewSpanはスパンのみを作成し、操作が他の場所でメトリクスを持っている場合に便利です。両方のアノテーションは、例外記録とスパンステータスを自動的に処理します。

Spring Boot 3.4の自動インスツルメンテーションコンポーネント

Spring Boot 3.4は、コード変更なしで複数のコンポーネントを自動インスツルメントします:

コンポーネント観測名キャプチャ内容
Spring MVChttp.server.requestsリクエストパス、メソッド、ステータス、例外
WebClienthttp.client.requestsアウトバウンドHTTPコール
RestClienthttp.client.requestsアウトバウンドHTTPコール(Spring 6.1+)
Spring Kafkaspring.kafka.listenerコンシューマーグループ、トピック、パーティション
Spring Data JPAspring.data.repositoryリポジトリメソッド、クエリ時間
Scheduled Tasksspring.schedulingタスク名、実行時間

Spring Boot Actuatorドキュメントには、すべての自動インスツルメントコンポーネントが一覧されています。Datasource Micrometerなどのサードパーティライブラリは、JDBCクエリトレーシングを追加します。

ノイズを減らすための観測フィルタリング

ヘルスチェック、レディネスプローブ、静的リソースはトレースにノイズを生成します。述語を使用してそれらをフィルタリングします:

ObservationFilterConfig.javajava
@Configuration
public class ObservationFilterConfig {

    @Bean
    public ObservationPredicate noHealthChecks() {
        return (name, context) -> !name.equals("http.server.requests")
            || !isHealthEndpoint(context);
    }

    private boolean isHealthEndpoint(Observation.Context context) {
        if (context instanceof ServerRequestObservationContext http) {
            String path = http.getCarrier().getRequestURI();
            return path.startsWith("/actuator/health") || path.startsWith("/actuator/ready");
        }
        return false;
    }
}

または、設定で名前によって観測を無効にします:

yaml
# application.yml
management:
  observations:
    enable:
      spring.security: false  # Disable Spring Security observations
      http.server.requests.actuator: false  # Custom predicate name
面接コンテキスト: トレースサンプリング vs フィルタリング

サンプリングは、すべてのエンドポイントで収集されるトレースの割合を減らします。フィルタリングは、特定のエンドポイントを完全に削除します。診断価値を提供しないエンドポイント(ヘルスチェック、メトリクススクレイピング)にはフィルタリングを使用します。代表的なデータを保持しながらコストを制御するにはサンプリングを使用します。

トレースIDとログの相関付け

Micrometer Tracingがアクティブな場合、Spring Boot 3.4はトレースIDとスパンIDをMDC(Mapped Diagnostic Context)に自動的に追加します。LogbackとLog4j2は、ログ出力にこれらのIDを含めることができます:

xml
<!-- logback-spring.xml -->
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - traceId=%X{traceId} spanId=%X{spanId} - %msg%n</pattern>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

このパターンにより、すべてのログ行にトレースIDが含まれます。トレースIDでログを検索すると、すべてのサービスにわたる単一のリクエストからのすべてのメッセージが返され、JaegerまたはTempoのスパンとログが相関付けられます。

Spring Bootオブザーバビリティに関する一般的な面接質問

面接官は、オブザーバビリティに関する概念的理解と実務経験の両方を評価します。これらの質問は、シニアバックエンドロールで頻繁に登場します。

Q: Micrometer TracingとOpenTelemetryの違いは何ですか?

Micrometer Tracingは、分散トレーシングのためのベンダー中立的なAPIであり、Micrometerがメトリクスを抽象化するのと同様です。OpenTelemetryは特定の実装とプロトコルです。Spring BootはAPIとしてMicrometer Tracingを使用し、micrometer-tracing-bridge-otelを介してエクスポート用にOpenTelemetryにブリッジします。このレイヤリングにより、アプリケーションはコード変更なしでバックエンドを切り替えることができます。

Q: なぜSpringは直接OpenTelemetryインスツルメンテーションよりもObservation APIを推奨するのですか?

Observation APIは、メトリクスとトレースの両方を出力する単一のインスツルメンテーションポイントを提供します。直接OpenTelemetry呼び出しはトレースのみを生成します。Observation.observe()を使用すると、同じコードからタイマーメトリクスとスパンが生成され、重複を減らし、一貫した命名を確保します。

Q: スパン間にギャップがあるトレースをデバッグするにはどうすればよいですか?

ギャップは、インスツルメンテーションの欠落またはコンテキスト伝播の中断を示しています。コードがContextPropagatingTaskDecoratorなしで非同期操作を使用しているかどうかを確認します。HTTPクライアント(WebClient、RestClient、または手動でラップされたRestTemplate)がインスツルメントされていることを確認します。メッセージキューの場合、トレースヘッダーがメッセージプロパティを通じて伝播されることを確認します。

Q: 高カーディナリティタグをメトリクスに追加するとどうなりますか?

各一意のタグ値は新しい時系列を作成します。数百万のユーザーを持つユーザーIDタグは数百万の時系列を作成し、Prometheusまたは他のTSDBバックエンドのメモリを枯渇させます。修正は、lowCardinalityKeyValue()ではなくhighCardinalityKeyValue()を使用することで、高カーディナリティが期待されるトレースにのみタグを追加します。

Docker Composeを使用した分散トレーシングパイプラインの構築

ローカルのオブザーバビリティスタックは、本番環境にデプロイする前にインスツルメンテーションを検証するのに役立ちます。この例では、OpenTelemetry CollectorとJaegerを使用します:

yaml
# docker-compose.yml
services:
  otel-collector:
    image: otel/opentelemetry-collector-contrib:0.102.0
    command: ["--config", "/etc/otel-collector-config.yml"]
    volumes:
      - ./otel-collector-config.yml:/etc/otel-collector-config.yml
    ports:
      - "4318:4318"   # OTLP HTTP
      - "4317:4317"   # OTLP gRPC

  jaeger:
    image: jaegertracing/jaeger:2.3
    ports:
      - "16686:16686"  # UI
    environment:
      - COLLECTOR_OTLP_ENABLED=true

  app:
    build: .
    environment:
      - MANAGEMENT_OTLP_TRACING_ENDPOINT=http://otel-collector:4318/v1/traces
    depends_on:
      - otel-collector
yaml
# otel-collector-config.yml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
      grpc:
        endpoint: 0.0.0.0:4317

processors:
  batch:
    timeout: 1s
    send_batch_size: 1024

exporters:
  otlp/jaeger:
    endpoint: jaeger:4317
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp/jaeger]

コレクターはSpring Bootアプリケーションからスパンを受信し、Jaegerに転送します。このアーキテクチャにより、アプリケーションコードを変更せずにエクスポーター(Tempo、Zipkin、クラウドバックエンド)を追加できます。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

Spring Bootオブザーバビリティの重要ポイント

  • Observation APIはメトリクスとトレーシングを統合します。1回のObservation.observe()呼び出しでタイマーメトリクスとスパンの両方が生成され、二重インスツルメンテーションが不要になります。
  • OTLPエクスポートを有効にするには、micrometer-tracing-bridge-otelopentelemetry-exporter-otlpを追加します。Spring Boot 3.4は、management.otlp.tracing.endpointのエンドポイントでHTTPエクスポーターを自動設定します。
  • メトリクスに表示されるディメンション(有限値)にはlowCardinalityKeyValue()を使用します。トレースのみのデータ(ユーザーID、リクエストID)にはhighCardinalityKeyValue()を使用します。
  • spring.task.execution.propagate-context=trueで非同期コードのコンテキスト伝播を有効にし、カスタムエクスキューターをContextPropagatingTaskDecoratorでラップします。
  • ObservationPredicateビーンを使用して/actuator/healthなどのノイズの多いエンドポイントをフィルタリングし、トレースをビジネス操作に集中させます。
  • ログパターンには、分散トレースとログ行を相関付けるために%X{traceId}を含めます。
  • オブザーバビリティに関する面接質問は、カーディナリティ、コンテキスト伝播の失敗、Observation APIと直接OpenTelemetry使用の区別に焦点を当てています。
今日のチャレンジ

Spring Boot のバグを見つけられますか

実際のコード、隠れたバグ、1日1回。アカウントなしで試せます。

Anthony Fillion-Maillet

執筆

Anthony Fillion-Maillet

SharpSkill 創業者

10 年以上フルスタック開発に携わっています。SharpSkill を運営し、ここで公開される内容に責任を負っています。

2026年9月19日 更新

共有

関連記事