[アップデート] CloudWatch Application Signals に Dynamic Instrumentation が追加され、本番アプリケーションの変数やスタックトレースを再デプロイなしで取得できるようになりました
いわさです。
CloudWatch Application Signals は、OpenTelemetry 互換のアプリケーションパフォーマンスモニタリング(APM)を提供するサービスです。
アプリケーションの自動計装によりメトリクス・トレース・ログを収集し、SLO の定義やダッシュボードの構成をまとめて行うことができます。
本番環境で問題が発生した際、変数の中身やメソッドの引数を確認したい場面があります。
しかしこれまでは、ログ出力のコードを追加してからアプリケーションをビルド・デプロイし直し、問題の再現を待つ必要がありました。
特に再現が難しい問題の場合、この「修正→デプロイ→再現待ち」のサイクルを何度も繰り返すことになり、調査に時間がかかっていました。
先日のアップデートで、Application Signals に Dynamic Instrumentation 機能が追加されました。
この機能を使うと、実行中のアプリケーションに対してコードの特定箇所にブレークポイントやプローブを設定し、変数の値・メソッド引数・戻り値・スタックトレースといったランタイムの状態を、再起動や再デプロイなしに取得できます。
取得されたデータはスナップショットとして CloudWatch Logs に配信され、アクティブなトレースと相関付けされます。
対応言語は Java、Python、JavaScript/TypeScript で、すべての商用 AWS リージョンで利用可能とのことです。
今回こちらを確認してみたので紹介します。
実際に確認してみる
では早速、EKS 上の Java アプリケーションに対して AWS CLI でブレークポイントを設定し、スナップショットが CloudWatch Logs に出力される流れを確認してみましょう。
公式ドキュメントによると、Dynamic Instrumentation では「ブレークポイント」と「プローブ」という 2 種類のインストルメンテーションを設定できるみたいです。
Breakpoint: Temporary instrumentation that auto-expires. The default expiration is 24 hours, configurable from 5 minutes to 24 hours. Use breakpoints for debugging and investigation.
Probe: Permanent instrumentation that persists until explicitly deleted. Use probes for ongoing observability.
ブレークポイントは一時的なもので、最大 24 時間で自動的に無効化されます。デバッグ・調査用途向けですね。
プローブは明示的に削除するまで永続し、継続的なオブザーバビリティに使うみたいです。
いずれの場合も、インストルメンテーションが発火するとランタイムの状態がスナップショットとして CloudWatch Logs に書き込まれます。
なお、各インストルメンテーションポイントには 1 秒あたり 5 回のキャプチャというレートリミットがあるみたいです。
料金は標準的な CloudWatch Logs の取り込み・保存料金が適用されるとのことです。
今回はブレークポイントを使って検証してみます。
前提条件
Dynamic Instrumentation を使うには以下の条件を満たす必要があります。
- Application Signals が有効化されていること
- ADOT SDK(AWS Distro for OpenTelemetry)の最新バージョンでアプリケーションを計装していること
- 環境変数
OTEL_AWS_DYNAMIC_INSTRUMENTATION_ENABLED=trueを設定していること - 環境変数
OTEL_SERVICE_NAMEでサービス名を指定していること - 環境変数
OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name={環境名}を指定していること - CloudWatch Agent が Application Signals の設定で稼働していること
なお、公式ドキュメントによると Lambda 環境ではサポートされていないとのことです。
Dynamic Instrumentation is not supported in Lambda environments.
EKS の場合は CloudWatch Observability EKS add-on をインストールすれば、ADOT SDK と CloudWatch Agent がまとめてセットアップされます。
サンプルアプリケーション
今回は EKS クラスターに add-on v6.3.0 をインストールした環境を事前に作成しておきました。

以下のようなシンプルな Spring Boot アプリケーションをデプロイしています。
package com.example.hoge;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HogeController {
@GetMapping("/calculate")
public String calculate(@RequestParam int a, @RequestParam int b) {
int result = doCalculation(a, b);
return "result: " + result;
}
private int doCalculation(int x, int y) {
int sum = x + y;
int product = x * y;
return sum + product;
}
}
/calculate?a=3&b=5 のようにリクエストを送ると、doCalculation メソッドで計算を行い result: 23 を返すアプリです。
Deployment では以下の環境変数を設定しています。
env:
- name: OTEL_SERVICE_NAME
value: "hoge-service"
- name: OTEL_RESOURCE_ATTRIBUTES
value: "deployment.environment.name=hoge-env"
- name: OTEL_AWS_DYNAMIC_INSTRUMENTATION_ENABLED
value: "true"
Application Signals のサービス一覧に hoge-service が表示されることを確認できました。
この時点ではアプリケーション側にデバッグ用のログ出力などは一切入れていません。

ブレークポイントの作成
ブレークポイントは、コードの特定箇所に対して「次にここが実行されたら、そのときの変数の値や引数をキャプチャしてね」と指示する設定です。
IDE のデバッガーのブレークポイントと似たイメージですが、アプリケーションは停止せずそのまま動き続けます。
AWS CLI の application-signals create-instrumentation-configuration コマンドでブレークポイントを作成します。
今回は calculate メソッドの引数と戻り値を取得してみました。
aws application-signals create-instrumentation-configuration \
--instrumentation-type BREAKPOINT \
--service "hoge-service" \
--environment "hoge-env" \
--signal-type SNAPSHOT \
--location '{
"CodeLocation": {
"Language": "Java",
"CodeUnit": "com.example.hoge",
"ClassName": "HogeController",
"MethodName": "calculate",
"FilePath": "HogeController.java"
}
}' \
--capture-configuration '{
"CodeCapture": {
"CaptureArguments": ["a", "b"],
"CaptureReturn": true,
"CaptureStackTrace": true,
"CaptureLimits": {
"MaxHits": 100,
"MaxStringLength": 255,
"MaxCollectionWidth": 20,
"MaxObjectDepth": 3,
"MaxFieldsPerObject": 20,
"MaxStackFrames": 20
}
}
}' \
--region ap-northeast-1
Java の場合、location には CodeUnit(パッケージ名)、ClassName、MethodName、FilePath が必須です。
オプションで LineNumber を指定して行レベルのブレークポイントも設定できます。
capture-configuration では取得するデータを制御します。
今回は引数 a, b、戻り値、スタックトレースを取得する設定にしました。
実行すると以下のようなレスポンスが返ってきます。
{
"InstrumentationType": "BREAKPOINT",
"Service": "hoge-service",
"Environment": "hoge-env",
"SignalType": "SNAPSHOT",
"Location": {
"CodeLocation": {
"Language": "Java",
"CodeUnit": "com.example.hoge",
"ClassName": "HogeController",
"MethodName": "calculate",
"FilePath": "HogeController.java"
}
},
"LocationHash": "756fd5d86d8c51c0",
"ExpiresAt": "2026-07-18T06:58:08+09:00",
"CaptureConfiguration": {
"CodeCapture": {
"CaptureArguments": ["a", "b"],
"CaptureReturn": true,
"CaptureStackTrace": true,
"CaptureLimits": {
"MaxHits": 100,
"MaxStringLength": 255,
"MaxCollectionWidth": 20,
"MaxStackFrames": 20,
"MaxObjectDepth": 3,
"MaxFieldsPerObject": 20
}
}
},
"CreatedAt": "2026-07-17T06:58:08+09:00",
"ARN": "arn:aws:application-signals:ap-northeast-1:123456789012:instrumentationConfig/hoge-service/hoge-env/SNAPSHOT/756fd5d86d8c51c0"
}
ExpiresAt が 24 時間後に設定されていることがわかります。
LocationHash はこのブレークポイントを一意に識別するハッシュ値で、一覧取得や削除の際に使用します。
ブレークポイントの一覧取得
作成したブレークポイントの一覧は list-instrumentation-configurations で確認できます。
aws application-signals list-instrumentation-configurations \
--service "hoge-service" \
--environment "hoge-env" \
--instrumentation-type BREAKPOINT \
--region ap-northeast-1
レスポンスには SyncedAt(エージェントが最後に設定を同期した時刻)と Changed(前回の同期から変更があるか)が含まれます。
エージェントは約 60 秒間隔で設定をポーリングしているみたいです。
ステータスの遷移
公式ドキュメントによると、ブレークポイントのステータスは以下の遷移をたどるみたいです。
| ステータス | 説明 |
|---|---|
| READY | エージェントが設定を受信した状態 |
| ACTIVE | アプリケーションにインストルメンテーションが適用された状態 |
| ERROR | 適用に失敗した状態 |
| DISABLED | 有効期限切れまたは削除された状態 |
When an instrumentation enters the ERROR state, the following causes might be reported: FILE_NOT_FOUND, METHOD_NOT_FOUND, LINE_NOT_EXECUTABLE, OVERLOADED_METHODS, LANGUAGE_MISMATCH, RUNTIME_ERROR
CloudWatch コンソールの Application Signals > サービス詳細 > 「インストルメンテーション」タブからステータスの確認や管理が行えます。

スナップショットの確認
ブレークポイントが ACTIVE になった状態でアプリケーションにリクエストを送ると、スナップショットが CloudWatch Logs に配信されます。
curl "http://<アプリケーションのエンドポイント>/calculate?a=3&b=5"
# result: 23
今回の検証では、スナップショットはロググループ /aws/service-events/hoge-service に出力されていました。

スナップショットの JSON には以下の情報が含まれています。
{
"scope": {
"name": "aws.dynamic_instrumentation",
"version": "1.0"
},
"body": {
"stack": [
{
"file_path": "HogeController.java",
"function": "calculate",
"line_number": 12
},
...
],
"captures": {
"entry": {
"arguments": {
"a": {
"type": "java.lang.Integer",
"value": "3"
},
"b": {
"type": "java.lang.Integer",
"value": "5"
}
}
},
"return": {
"return_value": {
"type": "java.lang.String",
"value": "result: 23",
"size": 10
}
}
}
},
"attributes": {
"aws.di.class_name": "HogeController",
"aws.di.code_unit": "com.example.hoge",
"aws.di.duration_ms": 0,
"aws.di.file_path": "HogeController.java",
"aws.di.instrumentation_level": "method",
"aws.di.instrumentation_type": "BREAKPOINT",
"aws.di.location_hash": "756fd5d86d8c51c0",
"aws.di.method_name": "calculate",
"aws.di.snapshot_id": "9d28b02f-ec76-40d7-a5a6-05181f891018",
"event.name": "aws.dynamic_instrumentation.snapshot"
},
"traceId": "6a59545058cb604caf5c384da4052570",
"spanId": "c8dff2cf8d7b20d3"
}
引数 a=3, b=5、戻り値 "result: 23"、呼び出し元のスタックトレース、さらに traceId と spanId によるトレースとの相関付けが確認できます。
aws.di.duration_ms にはメソッドの実行時間も記録されていますし、aws.di.snapshot_id でスナップショットを一意に特定することもできるみたいですね。
コードにログ出力を追加して再デプロイすることなく、実行時の内部状態を把握できることがわかります。
注意事項・制限
IAM 権限
今回の検証で、ADOT Java エージェントがブレークポイントの設定をポーリングするために application-signals:ListInstrumentationConfigurations 権限がノードロール(またはサービスアカウント)に必要であることがわかりました。
この権限がない場合、エージェントが設定を取得できずブレークポイントが ACTIVE になりません。
CloudWatch Observability EKS add-on のインストール時に自動設定されるポリシーでは足りない場合があるので、以下のアクションを追加する必要があるかもしれません。
{
"Effect": "Allow",
"Action": [
"application-signals:ListInstrumentationConfigurations",
"application-signals:GetInstrumentationConfiguration",
"application-signals:UpdateInstrumentationConfiguration"
],
"Resource": "*"
}
あわせて、CloudWatchAgentServerPolicy と AWSXRayDaemonWriteAccess もノードロールにアタッチされている必要があります。
キャプチャ制限
取得データのサイズと深さを制御するキャプチャ制限も確認しておきます。
公式ドキュメントによると以下のデフォルト値が設定されているみたいです。
| 制限 | デフォルト値 | 範囲 | 説明 |
|---|---|---|---|
| maxStringLength | 255 | 1–255 | 文字列値の最大文字数 |
| maxCollectionWidth | 20 | 1–20 | コレクション/配列の最大要素数 |
| maxObjectDepth | 3 | 1–5 | ネストしたオブジェクトの最大走査深度 |
| maxFieldsPerObject | 20 | 1–20 | オブジェクトあたりの最大フィールド数 |
| maxStackFrames | 20 | 1–20 | スタックフレームの最大数 |
| maxHits | 100 | 1–1000 | 自動無効化までの最大キャプチャ回数(ブレークポイントのみ) |
ブレークポイントの場合、maxHits に達すると自動的に無効化されるとのことです。
プローブには maxHits の制限がないので、継続的に監視したい箇所にはプローブが適しています。
言語ごとの制約
公式ドキュメントによると、JavaScript/TypeScript の場合は行レベルのブレークポイントのみ対応で、プローブや関数レベルのブレークポイントはサポートされていないとのことです。
JavaScript or TypeScript: Only line-level breakpoints are supported. Probes and function-level breakpoints are not supported. TypeScript is supported when you provide source maps.
言語ごとに利用できるインストルメンテーションの種類が異なる点は留意しておくと良さそうです。
仕組みについて
ここまで検証してきて「再ビルドも再デプロイもしていないのになぜ動くのか」が気になるところです。
Java の場合、JVM には java.lang.instrument パッケージが用意されており、実行中のプログラムのバイトコードを動的に書き換えることができます。
Provides services that allow Java programming language agents to instrument programs running on the Java Virtual Machine (JVM). The mechanism for instrumentation is modification of the bytecodes of methods.
ADOT の Java エージェントが -javaagent オプションで JVM にアタッチされているので、ブレークポイントの設定を受け取ると対象メソッドのバイトコードにスナップショット収集コードを注入し、メソッド実行時に引数や戻り値をキャプチャしているということのようです。
さいごに
本日は CloudWatch Application Signals に Dynamic Instrumentation が追加されたので確認してみました。
本番環境でのデバッグは「ログを足す→ビルド→デプロイ→再現待ち」のサイクルが悩ましいところですが、この機能を使えば再デプロイなしに欲しい情報を取得できるので、調査のスピードが上がりそうです。
ブレークポイントは最大 24 時間で自動期限切れになるので、デバッグ目的で一時的に仕込んで消し忘れる心配が少ないのも良いですね。
スナップショットに traceId が含まれているので、X-Ray のトレースと突き合わせて問題箇所を特定する使い方もできそうです。
なお、今回の検証では private メソッドにブレークポイントを設定した際にアプリケーションが 500 エラーを返す現象に遭遇しました。
public メソッドに変更したところ正常に動作したので、メソッドのアクセス修飾子によって制約があるのかもしれません。
私の環境の問題かもしれないので、うまく動いた方がいたら教えてください。
MCP サーバー経由で IDE から直接設定できる仕組みも用意されているみたいなので、そちらも別途試してみたいところです。
なお、今回は EKS で検証しましたが、公式ドキュメントによると EKS 以外の環境(ECS や EC2 など)でも ADOT SDK と CloudWatch Agent を手動でセットアップすれば利用可能とのことです。
EKS の場合は add-on でまとめてセットアップできるので楽ですが、他の環境でも同様に使えるのは良いですね。








