[アップデート] CloudWatch Application Signals に Service Events が追加され、例外やレイテンシ異常・デプロイイベントを自動的にキャプチャできるようになりました
いわさです。
CloudWatch Application Signals は OpenTelemetry 互換のアプリケーションパフォーマンスモニタリング機能で、サービスのレイテンシやエラーレート、リクエスト数などの自動計測から SLO の定義・アラーム設定まで一通りカバーしてくれます。
これまで Application Signals でサービスの異常を調査する際には、メトリクスの変動を検知した後、手動でトレースやログを辿ってスタックトレースや例外の詳細を確認する必要がありました。
デプロイ後に新しい例外が発生したかどうかを把握するにも別途ログ検索が必要で、問題の特定に時間がかかるケースがありました。
先日のアップデートで、Application Signals に Service Events 機能が追加されました。
Service Events を有効にすると、例外やレイテンシ異常のスナップショット、デプロイイベントなどをコード変更なしに自動キャプチャしてくれます。
対応言語は Java、Python、Node.js で、全商用リージョン(東京リージョン含む)で利用可能です。
今回こちらを確認してみたので紹介します。
実際に確認してみる
では早速、ECS Fargate 上の Python(Flask)アプリケーションで Service Events の動作を確認してみましょう。
準備
Application Signals を有効化した ECS Fargate タスクを用意しました。

ECS で Application Signals を使う場合、以下の 3 コンテナ構成になります。
- init コンテナ — ADOT の自動計装ライブラリを共有ボリュームにコピーするだけの初期化用コンテナ。起動後すぐ終了する
- ecs-cwagent — CloudWatch エージェント。アプリから受け取ったテレメトリデータを CloudWatch に転送する
- my-app — アプリケーション本体。init コンテナがコピーした自動計装ライブラリを
PYTHONPATH経由で読み込む
参考までに、今回使用したタスク定義を載せておきます。
{
"family": "hoge-service-events",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"volumes": [
{"name": "opentelemetry-auto-instrumentation-python"}
],
"containerDefinitions": [
{
"name": "init",
"image": "public.ecr.aws/aws-observability/adot-autoinstrumentation-python:v0.18.0",
"essential": false,
"command": ["cp", "-a", "/autoinstrumentation/.", "/otel-auto-instrumentation-python"],
"mountPoints": [
{"sourceVolume": "opentelemetry-auto-instrumentation-python", "containerPath": "/otel-auto-instrumentation-python"}
]
},
{
"name": "ecs-cwagent",
"image": "public.ecr.aws/cloudwatch-agent/cloudwatch-agent:latest",
"essential": true,
"secrets": [
{"name": "CW_CONFIG_CONTENT", "valueFrom": "ecs-cwagent"}
]
},
{
"name": "my-app",
"image": "<アカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/hoge-service-events-app:latest",
"essential": true,
"portMappings": [{"containerPort": 8080}],
"environment": [
{"name": "OTEL_SERVICE_NAME", "value": "hoge-service-events-app"},
{"name": "OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "value": "true"},
{"name": "OTEL_PYTHON_DISTRO", "value": "aws_distro"},
{"name": "OTEL_PYTHON_CONFIGURATOR", "value": "aws_configurator"},
{"name": "OTEL_EXPORTER_OTLP_PROTOCOL", "value": "http/protobuf"},
{"name": "OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT", "value": "http://localhost:4316/v1/metrics"},
{"name": "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "value": "http://localhost:4316/v1/traces"},
{"name": "OTEL_TRACES_SAMPLER", "value": "xray"},
{"name": "OTEL_PROPAGATORS", "value": "tracecontext,baggage,b3,xray"},
{"name": "OTEL_METRICS_EXPORTER", "value": "none"},
{"name": "OTEL_LOGS_EXPORTER", "value": "none"},
{"name": "PYTHONPATH", "value": "/otel-auto-instrumentation-python/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation-python:/app"}
],
"dependsOn": [{"containerName": "init", "condition": "SUCCESS"}],
"mountPoints": [
{"sourceVolume": "opentelemetry-auto-instrumentation-python", "containerPath": "/otel-auto-instrumentation-python"}
]
}
]
}
ポイントは OTEL_AWS_APPLICATION_SIGNALS_ENABLED=true で Application Signals を有効化している点です。
Service Events は Application Signals に追従して自動で有効になるため、Service Events 用の追加設定は不要でした。
なお、Service Events の利用には以下のバージョン要件があります。
- ADOT Python SDK: v0.18.0 以降(Service Events はこのバージョンで追加)
- CloudWatch エージェント: v1.300069.0 以降
今回は ADOT Python SDK v0.18.0、CloudWatch エージェント v1.300070.0 で検証しています。
サンプルアプリは以下の 3 つのエンドポイントを持つシンプルな Flask アプリです。
from flask import Flask
app = Flask(__name__)
@app.route("/hoge")
def hoge():
return "ok"
@app.route("/fuga")
def fuga():
raise RuntimeError("something went wrong")
@app.route("/piyo")
def piyo():
import time
time.sleep(6)
return "slow response"
/hoge は正常レスポンス、/fuga は例外を発生、/piyo は 6 秒スリープという構成です。
これにより、正常時の動作に加えて、例外発生時のインシデントスナップショットと、レイテンシ閾値超過時のインシデントスナップショットがそれぞれキャプチャされる...はず!!
リクエストを送信してイベントを発生させる
タスクが起動したら、各エンドポイントにリクエストを送ります。
# 正常リクエスト
$ curl http://203.0.113.1:8080/hoge
ok
# 例外発生
$ curl http://203.0.113.1:8080/fuga
# 500 Internal Server Error
# レイテンシ閾値超過
$ curl http://203.0.113.1:8080/piyo
slow response # 約6秒後にレスポンス
Service Events のログを確認する
数分待つと、CloudWatch Logs に /aws/service-events/hoge-service-events-app というロググループが自動的に作成されていました。
これは今回の Service Events で新しく追加されたロググループで、従来の Application Signals のデータ(/aws/application-signals/data)とは別に、サービスごとに /aws/service-events/{サービス名} として作成されます。

ログの中身を確認すると、3 種類のイベントが記録されていることがわかります。
まず、デプロイイベントです。
タスク起動時に自動で発行されています。eventName が aws.service_events.deployment_event になっていて、trigger が startup なのでアプリ起動に伴うデプロイイベントであることがわかります。
{
"scope": {"name": "serviceevents", "version": "1.0"},
"attributes": {
"event.name": "aws.service_events.deployment_event",
"aws.service_events.deployment.trigger": "startup"
},
"eventName": "aws.service_events.deployment_event"
}
公式ドキュメントによると、デプロイイベントはアプリケーション起動時と 24 時間ごとに自動発行されるとのこと。
git commit やデプロイ ID などのメタデータを付与するとより詳細なコンテキストが得られるようです。
The application emits deployment events automatically. Providing deployment metadata (git commit, deployment ID) enriches these events with additional context.
次に、例外のインシデントスナップショットです。
/fuga エンドポイントで発生した RuntimeError のスタックトレースがまるごとキャプチャされています。
{
"body": {
"request_context": {
"status_code": 500,
"type": "http"
},
"exception_info": [
{
"exception_type": "RuntimeError",
"stack_trace": "Traceback (most recent call last):\n ...\n File \"/app/app.py\", line 13, in fuga\n raise RuntimeError(\"something went wrong\")\nRuntimeError: something went wrong\n",
"exception_message": "something went wrong"
}
]
},
"attributes": {
"aws.service_events.trigger_type": "exception",
"aws.service_events.operation": "GET /fuga",
"aws.service_events.duration_ms": 1.645311,
"event.name": "aws.service_events.incident_snapshot",
"http.response.status_code": 500
}
}
attributes.aws.service_events.trigger_type が exception なので例外トリガーであることがわかります。
トレース ID とスパン ID も含まれているので、X-Ray のトレースと関連付けることもできますね。
そして、レイテンシ閾値超過のインシデントスナップショットです。
/piyo エンドポイントの 6000ms のレスポンスがキャプチャされています。
{
"body": {
"request_context": {
"status_code": 200,
"type": "http"
},
"exception_info": []
},
"attributes": {
"aws.service_events.trigger_type": "latency",
"aws.service_events.operation": "GET /piyo",
"aws.service_events.duration_ms": 6000.659899,
"event.name": "aws.service_events.incident_snapshot",
"http.response.status_code": 200
}
}
こちらは trigger_type が latency です。
ステータスコードは 200 ですが、duration_ms が 6000ms でレイテンシ閾値を超えているためスナップショットが作成されています。
このレイテンシ閾値のデフォルト値は 5000ms で、環境変数 OTEL_AWS_SERVICE_EVENTS_INCIDENT_SNAPSHOT_DURATION_THRESHOLD_MS で変更できます。
Errors タブの確認
CloudWatch コンソール > Application Signals からサービスを選択し、Errors タブを開きます。

Errors タブでは時系列の例外カウントチャートと、各例外タイプの発生オペレーション・発生回数・前期間比の変化を一覧するテーブルが表示されます。
今回は RuntimeError が GET /fuga オペレーションで 50 回発生していることが確認できますね。
例外を選択するとスタックトレースや例外メッセージ、トレース ID などの詳細が確認できます。

スタックトレースから /app/app.py の 13 行目で RuntimeError が raise されていることがわかります。これはエラー対応する方にはとても助かりそうですね。
設定のカスタマイズ
Service Events の動作は環境変数でカスタマイズできます。
主な設定項目を確認しておきます。
| 環境変数 | デフォルト | 説明 |
|---|---|---|
OTEL_AWS_SERVICE_EVENTS_ENABLED |
Application Signals に従う | Service Events の有効/無効 |
OTEL_AWS_SERVICE_EVENTS_PACKAGES_INCLUDE |
なし | Function-call metrics の計測対象パッケージ(カンマ区切り) |
OTEL_AWS_SERVICE_EVENTS_INCIDENT_SNAPSHOT_DURATION_THRESHOLD_MS |
5000 |
インシデントスナップショットをトリガーするレイテンシ閾値(ミリ秒) |
OTEL_AWS_SERVICE_EVENTS_LATENCY_THRESHOLDS |
なし | エンドポイント別のレイテンシ閾値(GET /health:200,POST /checkout:8000 形式) |
なお、Application Signals を有効化すると Service Events も自動で有効になるので、追加の設定は不要です。
無効にしたい場合は OTEL_AWS_SERVICE_EVENTS_ENABLED=false を設定します。
公式ドキュメントによると、Error metrics と Incident snapshots、Deployment events は追加設定なしに自動で収集されるようです。
Function-call metrics のみ、OTEL_AWS_SERVICE_EVENTS_PACKAGES_INCLUDE に計測したいパッケージプレフィックスを指定する必要があるとのこと。
Function instrumentation is enabled by default, but it produces no metrics until you configure which packages to instrument.
Java なら com.myapp、Python なら myapp、Node.js なら src/myapp のように指定するみたいです。
Comma-separated list of package prefixes to instrument. No wildcard needed. For example: Java uses
com.myapp, Python usesmyapp, Node.js usessrc/myapp.
エンドポイントフィルタリングの設定で、特定のエンドポイントのみインシデントスナップショットを生成するように制御することも可能みたいです。
ヘルスチェック用エンドポイントなどノイズになりそうなものを除外できるのは良さそう。
なお、Lambda 環境では Service Events は自動的に無効化されるとのこと。
Service Events is automatically disabled in Lambda environments.
さいごに
本日は CloudWatch Application Signals に Service Events 機能が追加されたので、ECS Fargate 上の Python アプリで確認してみました。
Application Signals を有効にするだけで例外のスタックトレースやデプロイイベントを自動キャプチャしてくれるのは、コード変更が不要なので気軽に試せて良いですね。
特にデプロイ後に「新しい例外が増えていないか」を Errors タブからさっと確認できるのは、リリース後の確認フローに組み込みやすそうです。
レイテンシ閾値超過もステータスコードに関係なくキャプチャされるので、パフォーマンス劣化の早期検知にも使えそう。
ちなみに今回の検証では、最初 ADOT Python SDK v0.12.0 で試したところ Service Events のログが一切出力されず少しハマりました。
Service Events の機能は v0.18.0 で追加されたもので、古いバージョンでは動作しません。
公式ドキュメントには「ADOT SDK を最新版にアップデートしてください」としか書かれていないので、Service Events のデータが出てこない場合はまず SDK バージョンを確認するのが良さそうです。
Function-call metrics についてはパッケージ指定が必要なので、検証する機会があればまた別途試してみたいと思います。









