View a markdown version of this page

HTTP エンドポイントの可視性を設定する - Amazon CloudWatch

HTTP エンドポイントの可視性を設定する

このページでは、HTTP サービスオペレーションが特定のエンドポイント名ごとに分離して表示されることを期待していたにもかかわらず、実際には複数のエンドポイントが同一のメトリクスにまとめられて表示された Application Signals のお客様向けのガイダンスを提供します。デフォルトでは、Application Signals はメトリクスのカーディナリティを低く保つため、HTTP サービスの Operation メトリクスディメンションを URL パスの最初のセグメントのみに切り詰めます (例: /api/v1/usersGET /api になる)。その結果、複数のエンドポイントが同じプレフィックスを共有するサービスでは、個々のエンドポイントの運用状態を十分に把握できない可能性があります。以下の手順に従って、サービスオペレーションのエンドポイントを希望の粒度で設定できます。

重要

エンドポイントの可視性を変更すると、サービスオペレーションの Application Signals メトリクスディメンションに影響を及ぼし、互換性が損なわれます。したがって、SLO のしきい値、アラーム、および/またはダッシュボードを必ず更新してください。

AWS Distro for OpenTelemetry (ADOT) ソリューション

AWS Distro for OpenTelemetry (ADOT) を使用している場合は、HTTP サービスの URL パステンプレートのカンマ区切りの一覧を使用して OTEL_AWS_HTTP_OPERATION_PATHS 環境変数を設定します。

export OTEL_AWS_HTTP_OPERATION_PATHS="/path/to/endpoint, /another/{placeholder}/endpoint"

この変数は、HTTP サーバースパンの url.path 属性に対して一致した中で一番長いプレフィックスを使用し、オペレーション名を決定します。ワイルドカードパターンは任意の 1 つの URL セグメントに一致し、{placeholder}:placeholder、または * で表すことができます。変数を設定したら、新しいエンドポイントのグループ化を反映させるためにアプリケーションを再起動してください。

この変数は、ADOT for Java、Python、Node.js、および .NET でサポートされています。

次のトラフィックを受信する API サービスを考えてみましょう。

GET /api/users GET /api/users/42 GET /api/users/42/orders POST /api/users/99/orders POST /api/users/42/orders GET /api/users/42/orders/7/items GET /api/products

デフォルトでは、Application Signals はこれらすべてを GET /api または POST /api のサービスメトリクスにグループ化するため、エンドポイント間でパフォーマンスを区別することはできません。

これを修正するには、希望するパステンプレートを使用して環境変数を設定してください。

export OTEL_AWS_HTTP_OPERATION_PATHS="/api/users/{userId}/orders/{orderId}/items, /api/users/{userId}/orders, /api/users/{userId}, /api/users, /api/products"

この設定では、Application Signals に別々のオペレーションが表示されます。複数のリクエストが、同じ設定済みテンプレートに解決される場合があります。

エンドポイントの可視性設定の結果例
受信リクエスト デフォルトのオペレーション 設定あり
GET /api/users GET /api GET /api/users
GET /api/users/42 GET /api GET /api/users/{userId}
GET /api/users/42/orders GET /api GET /api/users/{userId}/orders
POST /api/users/99/orders POST /api POST /api/users/{userId}/orders
POST /api/users/42/orders POST /api POST /api/users/{userId}/orders
GET /api/users/42/orders/7/items GET /api GET /api/users/{userId}/orders/{orderId}/items
GET /api/products GET /api GET /api/products

ネイティブ OpenTelemetry ソリューション

(ADOT を使わずに) ネイティブ OpenTelemetry SDK を使用している場合、OpenTelemetry Collector の transform プロセッサを使用するか、あるいはアプリケーションコードで直接、スパン名を上書きできます。

注記

CloudWatch が OpenTelemetry スパン名を解析して Application Signals のオペレーション名として使用できるように、OTLP エクスポーターでコレクターを設定して OpenTelemetry スパン名を保持する必要があります。詳細については、「Sending OTLP data to CloudWatch」を参照してください。

オプション 1 (推奨): コレクター側の変換

transform プロセッサを使用して、URL パターンに基づいてスパン名を設定します。transform プロセッサは OTTL (OpenTelemetry Transformation Language) を使用しており、スパン name フィールドを直接変更することができます。

ルールを最も浅いものから最も深いものの順に並べます。ステートメントは順番に実行されるため、リストの後半にあるより具体的なルールは、リストの前半にある一般的なルールよりも優先されます。次の例では、スパン属性 url.path の値が照合され、その結果として生成されるスパン名には、リクエストメソッドの後に目的の URL パターンが続くように設定されています。

processors: transform/operation_names: trace_statements: - context: span conditions: - IsMatch(attributes["url.path"], "^/api/contests(/|$)") statements: - set(name, Concat([attributes["http.request.method"], "/api/contests"], " ")) - context: span conditions: - IsMatch(attributes["url.path"], "^/api/contests/[^/]+$") statements: - set(name, Concat([attributes["http.request.method"], "/api/contests/{id}"], " ")) - context: span conditions: - IsMatch(attributes["url.path"], "^/api/contests/[^/]+/leaderboard(/|$)") statements: - set(name, Concat([attributes["http.request.method"], "/api/contests/{id}/leaderboard"], " ")) service: pipelines: traces: receivers: [otlp] processors: [resourcedetection, transform/operation_names, batch] exporters: [otlphttp/xray]

オプション 2: アプリケーションコードでスパン名を設定する

OpenTelemetry API を使用して、アプリケーションコードでサーバースパンのスパン名を手動で設定できます。ルートテンプレートでパラメータ化されたプレースホルダーが使用されている箇所で、名前を {HTTP_METHOD} {route_template} に設定します。これはハードコーディングされたフォールバックオプションであり、この変更を適用するには、各 HTTP リクエストハンドラーのスパン名を手動で更新する必要があります。

Java

import io.opentelemetry.api.trace.Span; // Inside your request handler Span.current().updateName("GET /api/contests/{id}/leaderboard");

Python

from opentelemetry import trace # Inside your request handler span = trace.get_current_span() span.update_name("GET /api/contests/{id}/leaderboard")

Go

import "go.opentelemetry.io/otel/trace" // Inside your request handler span := trace.SpanFromContext(ctx) span.SetName("GET /api/contests/{id}/leaderboard")

Node.js

import { trace } from '@opentelemetry/api'; // Inside your request handler const span = trace.getActiveSpan(); if (span) { span.updateName('GET /api/contests/{id}/leaderboard'); }