0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS FargateでJavaアプリのログをX-Rayトレースと紐付ける方法

Last updated at Posted at 2025-04-03

1. はじめに

前回の記事「AWS FargateでJavaアプリを自動計装してX-Rayでトレースする方法」では、アプリケーションコードを変更することなく、AWS Distro for OpenTelemetry (ADOT)を使用したJavaアプリケーションの自動計装方法を解説しました。

AWS FargateでJavaアプリを自動計装してX-Rayでトレースする方法

これにより、X-Rayでのトレース収集は実現できましたが、より効果的なトラブルシューティングのためには「ログとトレースの紐付け」が重要です。

今回は、前回構築した環境をさらに発展させ、アプリケーションログにX-Rayトレース情報を埋め込む方法を解説します。これにより、CloudWatch Logsで見つけたエラーログからX-Rayトレースへの直接リンク、またはその逆の流れが可能になり、問題の根本原因分析が大幅に効率化されます。

2. ログとトレースの紐付けがもたらす価値

2.1 なぜログとトレースの紐付けが重要か?

分散システムでは、問題が発生したとき「何が」「どこで」「なぜ」起きたのかを特定するのが難しいことがあります。

  • ログだけでは: 詳細な情報は得られるものの、リクエストの全体像が見えない
  • トレースだけでは: リクエストの流れは把握できるが、詳細なアプリケーション状態が分からない

両方を紐付ければ:

  1. エラーログを見つけたら、そのリクエストの全体的な処理フロー(トレース)を確認できる
  2. トレースで異常を見つけたら、関連する詳細なアプリケーションログを参照できる

2.2 AWS環境での実現方法

AWS環境では、X-RayとCloudWatch Logsの連携により、この紐付けを実現できます。

  1. アプリケーションログにX-Ray形式のトレースIDを埋め込む
  2. CloudWatch LogsのログイベントからX-Rayトレースへリンクする機能を活用

3. 実装手順:ログとトレースの紐付け

前回の自動計装環境に、以下の3つのファイル修正/追加を行います。

AWS FargateでJavaアプリを自動計装してX-Rayでトレースする方法

  1. build.gradle.kts - OpenTelemetry Logbackの依存関係を追加
  2. logback.xml - ログにトレースIDを埋め込む設定を追加
  3. Dockerfile - 上記設定ファイルの配置と環境変数の設定

3.1 build.gradle.ktsの更新

まず、build.gradle.ktsファイルに、OpenTelemetryとLogbackの連携に必要な依存関係を追加します。

plugins {
  java
  application
  id("org.springframework.boot") version "3.1.3"
  id("io.spring.dependency-management") version "1.1.3"
}

sourceSets {
  main {
    java.setSrcDirs(setOf("."))
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation("org.springframework.boot:spring-boot-starter-web")
  // OpenTelemetryとLogbackの連携のための依存関係を追加
  implementation("io.opentelemetry.instrumentation:opentelemetry-logback-mdc-1.0:1.33.0-alpha")
}

変更ポイント:

  • opentelemetry-logback-mdc-1.0 ライブラリを追加(バージョンは必ず使用する自動計装エージェントと揃える)
  • このライブラリにより、Logbackでトレースコンテキストに含まれる情報にアクセスできるようになります

3.2 logback.xml の作成

次に、Logbackの設定ファイルを作成します。この設定により、ログメッセージにトレースIDを含めることができます。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} trace_id=%X{AWS-XRAY-TRACE-ID} span_id=%X{span_id} trace_flags=%X{trace_flags} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- OpenTelemetryAppenderでラップして、トレースコンテキストの情報をMDCに注入 -->
  <appender name="OTEL" class="io.opentelemetry.instrumentation.logback.mdc.v1_0.OpenTelemetryAppender">
    <appender-ref ref="CONSOLE"/>
  </appender>

  <!-- OTELアペンダーを使用 -->
  <root level="DEBUG">
    <appender-ref ref="OTEL"/>
  </root>
</configuration>

重要ポイント:

  • trace_id=%X{AWS-XRAY-TRACE-ID} でX-Ray形式のトレースIDをログに埋め込む
  • OpenTelemetryAppender でLogbackアペンダーをラップし、トレースコンテキスト情報をMDC(Mapped Diagnostic Context)に自動注入
  • MDCとは、スレッドローカルにキー/値ペアを保存するLogbackの機能で、トレース情報をログに埋め込むのに最適です

3.3 Dockerfileの更新

前回のDockerfileを以下のように更新します。

FROM public.ecr.aws/docker/library/gradle:jdk17 AS builder

WORKDIR /adot-java-sample
COPY *.java .
COPY build.gradle.kts .
COPY logback.xml .

RUN ["gradle", "assemble"]

FROM gcr.io/distroless/java17-debian11:latest

WORKDIR /adot-java-sample
COPY --from=builder /adot-java-sample/build/libs/ ./build/libs/
COPY logback.xml .

ENV SERVER_PORT=80

# ADOTエージェントのダウンロード
ADD https://github.com/aws-observability/aws-otel-java-instrumentation/releases/download/v1.33.0/aws-opentelemetry-agent.jar ./aws-opentelemetry-agent.jar
ENV JAVA_TOOL_OPTIONS "-javaagent:/adot-java-sample/aws-opentelemetry-agent.jar"

# OpenTelemetry関連の環境変数設定
ENV OTEL_RESOURCE_ATTRIBUTES "service.name=xraytestapp"
ENV OTEL_IMR_EXPORT_INTERVAL "60000"
ENV OTEL_EXPORTER_OTLP_ENDPOINT "http://127.0.0.1:4317"
ENV OTEL_METRICS_EXPORTER "none"
ENV OTEL_TRACES_EXPORTER="otlp"

EXPOSE 80

CMD ["/adot-java-sample/build/libs/adot-java-sample.jar"]

変更ポイント:

  • logback.xml をコピー
  • LOGGING_CONFIG 環境変数でlogback.xmlの場所を指定

4. 実装の詳細解説

4.1 トレースコンテキストがログに伝播される仕組み

この実装で重要なのは、トレースコンテキスト情報がアプリケーションログに自動的に含まれる仕組みです。

  1. OpenTelemetry Java自動計装エージェント:

    • サーバーリクエスト受信時に自動的にトレースを開始
    • 実行スレッドにトレースコンテキスト(トレースID、スパンIDなど)を関連付け
  2. OpenTelemetry Logback MDC統合:

    • opentelemetry-logback-mdc-1.0 ライブラリが現在のトレースコンテキスト情報をLogbackのMDCに注入
    • 主要なコンテキスト情報: AWS-XRAY-TRACE-ID, span_id, trace_flags
  3. logback.xml:

    • MDCに注入された値をログパターンで参照(%X{キー名}記法で)

4.2 X-Ray形式のトレースID

AWS X-Rayでは、トレースIDは以下の形式で表現されます。

1-5759e988-bd862e3fe1be46a994272793

この形式は:

  • 1- で始まるプレフィックス(バージョン)
  • タイムスタンプ(8桁の16進数)
  • ランダムな24桁の16進数

CloudWatch Logsでは、この形式のトレースIDを含むログエントリが自動的にX-Rayトレースにリンクされます。

5. 実際にやってみた

3章でも記載の通り前回の自動計装環境に、以下の3つのファイル修正/追加を行います。
環境の準備ができていない方は以下記事をご参照ください。
AWS FargateでJavaアプリを自動計装してX-Rayでトレースする方法

5.1 ファイル準備

1. すべてのファイルを用意

前回の作業ディレクトリに、更新したbuild.gradle.ktsと新しいlogback.xmlを追加します。

すべてのファイル作成コマンド(まとめて実行)
# DiceApplication.javaの作成
cat > DiceApplication.java << 'EOL'
package otel;

import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DiceApplication {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(DiceApplication.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.run(args);
    }
}
EOL

# RollController.javaの作成
cat > RollController.java << 'EOL'
package otel;

import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RollController {
    private static final Logger logger = LoggerFactory.getLogger(RollController.class);

    @GetMapping("/rolldice")
    public String index(@RequestParam("player") Optional<String> player) {
        int result = this.getRandomNumber(1, 6);
        if (player.isPresent()) {
            logger.info("{} is rolling the dice: {}", player.get(), result);
        } else {
            logger.info("Anonymous player is rolling the dice: {}", result);
        }
        return Integer.toString(result);
    }

    @GetMapping("/health")
    public String health() {
        return "OK";
    }

    private int getRandomNumber(int min, int max) {
        return ThreadLocalRandom.current().nextInt(min, max + 1);
    }
}
EOL

# 更新したbuild.gradle.ktsの作成
cat > build.gradle.kts << 'EOL'
plugins {
    java
    application
    id("org.springframework.boot") version "3.1.3"
    id("io.spring.dependency-management") version "1.1.3"
}

sourceSets {
  main {
    java.setSrcDirs(setOf("."))
  }
}

repositories {
  mavenCentral()
}

dependencies {
  implementation("org.springframework.boot:spring-boot-starter-web")
  // OpenTelemetryとLogbackの連携のための依存関係を追加
  implementation("io.opentelemetry.instrumentation:opentelemetry-logback-mdc-1.0:1.33.0-alpha")
}
EOL

# logback.xmlの作成
cat > logback.xml << 'EOL'
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} trace_id=%X{AWS-XRAY-TRACE-ID} span_id=%X{span_id} trace_flags=%X{trace_flags} - %msg%n</pattern>
    </encoder>
  </appender>

  <!-- OpenTelemetryAppenderでラップして、トレースコンテキストの情報をMDCに注入 -->
  <appender name="OTEL" class="io.opentelemetry.instrumentation.logback.mdc.v1_0.OpenTelemetryAppender">
    <appender-ref ref="CONSOLE"/>
  </appender>

  <!-- OTELアペンダーを使用 -->
  <root level="DEBUG">
    <appender-ref ref="OTEL"/>
  </root>
</configuration>
EOL

# 更新したDockerfileの作成
cat > Dockerfile << 'EOL'
FROM public.ecr.aws/docker/library/gradle:jdk17 AS builder

WORKDIR /adot-java-sample
COPY *.java .
COPY build.gradle.kts .
COPY logback.xml .

RUN ["gradle", "assemble"]

FROM gcr.io/distroless/java17-debian11:latest

WORKDIR /adot-java-sample
COPY --from=builder /adot-java-sample/build/libs/ ./build/libs/
COPY logback.xml .

ENV SERVER_PORT=80

# ADOTエージェントのダウンロード
ADD https://github.com/aws-observability/aws-otel-java-instrumentation/releases/download/v1.33.0/aws-opentelemetry-agent.jar ./aws-opentelemetry-agent.jar
ENV JAVA_TOOL_OPTIONS "-javaagent:/adot-java-sample/aws-opentelemetry-agent.jar"

# OpenTelemetry関連の環境変数設定
ENV OTEL_RESOURCE_ATTRIBUTES "service.name=xraytestapp"
ENV OTEL_IMR_EXPORT_INTERVAL "60000"
ENV OTEL_EXPORTER_OTLP_ENDPOINT "http://127.0.0.1:4317"
ENV OTEL_METRICS_EXPORTER "none"
ENV OTEL_TRACES_EXPORTER="otlp"

# logback.xmlの場所を指定
ENV LOGGING_CONFIG=/adot-java-sample/logback.xml

EXPOSE 80

CMD ["/adot-java-sample/build/libs/adot-java-sample.jar"]
EOL

5.2 イメージのビルドとデプロイ

1. Dockerイメージのビルド

docker build -t adot-java-sample-with-logs .

2. ECRリポジトリへの認証

aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin $(aws sts get-caller-identity --query 'Account' --output text).dkr.ecr.ap-northeast-1.amazonaws.com

3. イメージのタグ付け

docker tag adot-java-sample:latest $(aws sts get-caller-identity --query 'Account' --output text).dkr.ecr.ap-northeast-1.amazonaws.com/adot-java-sample:latest

ECR名は適宜更新してください。

4. イメージのプッシュ

docker push $(aws sts get-caller-identity --query 'Account' --output text).dkr.ecr.ap-northeast-1.amazonaws.com/adot-java-sample:latest

ECR名は適宜更新してください。

5. ECSサービスの更新

前回作成したECSサービスが新しいイメージを使用するように更新されます。
ECSサービスのデプロイが完了後、アプリケーションにアクセスしてみましょう。

5.3 結果の確認

1. アプリケーションへのアクセス

ALB経由で/rolldiceエンドポイントに複数回アクセスします。

https://your-alb-domain/rolldice
https://your-alb-domain/rolldice?player=Alice

2. CloudWatchログの確認

CloudWatch Logsコンソールで、アプリケーションのログを確認します。ログには以下のようにトレースID情報が含まれています。

05:46:03.853 [http-nio-80-exec-7] INFO otel.RollController trace_id=1-67ee209b-047d7d7e766d57968527e70d@706540b73155c174 span_id=706540b73155c174 trace_flags=01 - Anonymous player is rolling the dice: 6

このログエントリには:

  • trace_id=1-67ee209b-047d7d7e766d57968527e70d - X-Ray形式のトレースID
  • span_id=706540b73155c174 - 現在のスパンID
  • trace_flags=01 - トレースフラグ(サンプリングされたかどうかなど)

が含まれています。

3. X-Rayトレースの確認

X-Rayコンソールでトレースを確認すると、トレース内の各セグメントに対応するログを見ることができます。
Xray検証-2.jpg

6. まとめ

今回の実装により、以下のメリットが得られます。

  1. 効率的なトラブルシューティング:

    • トレースからログへのスムーズな移動
    • エラーの発生したコンテキストをより明確に把握可能
  2. 観測可能性の向上:

    • アプリケーション内部の状態(ログ)と分散システム全体の状態(トレース)の両方を一貫して監視可能
  3. 低侵襲な実装:

    • アプリケーションコードへの変更は不要
    • 設定ファイルの追加と依存関係の更新のみで実現

この方法は、特にマイクロサービスアーキテクチャを採用している環境や、多数のコンテナを運用している場合に、運用効率を大きく向上させます。自動計装とログ・トレース紐付けの組み合わせにより、AWSのマネージドサービスの利点を最大限に活かした可観測性基盤を構築できます。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?