dt-obs-tracing について
dt-obs-tracingは、DQLでGrail内のspanを調べるDynatraceのトレーシング分析スキルです。性能、障害、サービス依存関係、span種別ごとのクエリパターンを扱います。
主な機能
- 01分散トレースとspanのクエリ・把握
- 02リクエスト遅延と性能パーセンタイルの分析
- 03サービスや理由別の障害調査
- 04サービス呼び出しと外部依存関係の分析
- 05複数のspan種別と属性のクエリ
活用シーン
- 01遅延しきい値を超えたリクエストの特定
- 02サービスや理由別の障害分析
- 03サービス間呼び出しの把握
- 04エンドポイント別の応答時間推移の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dynatrace/ dynatrace-for-ai --skill dt-obs-tracingインストール後、Claude Code で/dt-obs-tracingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/database-spans.mdentity-lookups.mdfailure-detection.mdhttp-spans.mdlogs-correlation.mdmessaging-spans.mdnetworking-analysis.mdperformance-analysis.mdrequest-attributes.mdrpc-spans.mdsampling-extrapolation.mdserverless-spans.md詳細
アプリケーショントレーシングスキル
概要
Dynatrace の分散トレースは、作業単位を表す構成要素であるスパンで構成されます。Grail のトレースでは、すべてのスパンに DQL からアクセスでき、すべての属性を対象に全文検索できます。このスキルでは、トレースの基本事項、一般的な分析パターン、スパンの種類ごとのクエリについて説明します。
ユースケース
1. 遅いリクエストを調査する
- 目標: レイテンシーのしきい値を超えるリクエストを見つけ、診断します
- きっかけ: 「遅いリクエスト」、「高レイテンシー」、「p99 応答時間」、「5 秒を超えるトレースを見つける」
- 完了条件: 詳細調査用の実行時間、エンドポイント、サービス、トレース ID を含む、遅いトレースの一覧
2. リクエストの失敗を分析する
- 目標: 失敗したリクエスト、失敗の理由、例外のパターンを特定します
- きっかけ: 「失敗したスパン」、「HTTP 500 エラー」、「例外分析」、「サービス別の失敗率」
- 完了条件: 理由(HTTP コード、例外、gRPC ステータス)ごとの失敗の内訳と、代表的なトレース
3. サービスの依存関係をマッピングする
- 目標: サービス間の通信パターンと外部 API の呼び出しを把握します
- きっかけ: 「サービスの依存関係」、「X が呼び出すサービス」、「送信 HTTP 呼び出し」
- 完了条件: サービス間の呼び出し回数、レイテンシー、エラー率を示す依存関係マップ
基本概念
トレースとスパンを理解する
スパンは、分散トレースにおける論理的な作業単位を表します。
- HTTP リクエスト、RPC 呼び出し、データベース操作
- メッセージングシステムとのやり取り
- 内部関数の呼び出し
- カスタム計装ポイント
スパンの種類:
span.kind: server- サービスへの着信呼び出しspan.kind: client- サービスからの発信呼び出しspan.kind: consumer- サービスへの着信メッセージ消費呼び出しspan.kind: producer- サービスからの発信メッセージ生成呼び出しspan.kind: internal- サービス内の内部処理
ルートスパン:リクエストのルートスパン(request.is_root_span == true)は、サービスへの着信呼び出しを表します。これを使用して、リクエストのエンドツーエンドのパフォーマンスを分析します。
主なトレース属性
トレース分析に不可欠な属性:
| 属性 | 説明 |
|---|---|
trace.id | 一意のトレース識別子 |
span.id | 一意のスパン識別子 |
span.parent_id | 親スパン ID(ルートスパンの場合は null) |
request.is_root_span | ブール値。リクエストのエントリーポイントの場合は true |
request.is_failed | ブール値。リクエストが失敗した場合は true |
duration | スパンの実行時間(ナノ秒) |
span.timing.cpu | スパン全体の CPU 時間(安定) |
span.timing.cpu_self | 子スパンを除いた CPU 時間(安定) |
dt.smartscape.service | サービスの Smartscape ノード ID |
dt.service.name | サービス検出ルールから導出される Dynatrace サービス名。Smartscape サービスノード名と同じです。 |
endpoint.name | エンドポイント/ルート名 |
サービスのコンテキスト
スパンは、Smartscape ノード ID と、すべてのスパンに含まれる検出済みサービス名 dt.service.name を介してサービスを参照します。
fetch spans
| summarize spans=count(), by: { dt.smartscape.service, dt.service.name }
ノード関数:
getNodeName(dt.smartscape.service)- 人が読めるサービス名を含むdt.smartscape.service.nameフィールドを追加しますgetNodeField(dt.smartscape.service, "attribute_name")- 特定のノード属性にアクセスします
📖 詳細はこちら:エンティティの参照で、高度なエンティティセレクター、インフラストラクチャとの関連付け、ハードウェア分析をご覧ください。
サンプリングと外挿
次の理由により、1 つのスパンが複数の実際の操作を表すことがあります。
- 集約:1 つのスパンに複数の操作をまとめる(
aggregation.count) - ATM(Adaptive Traffic Management):エージェントによるヘッドベースのサンプリング
- ALR(Adaptive Load Reduction):サーバー側のサンプリング
- 読み取りサンプリング:
samplingRatioパラメーターを使用したクエリ時のサンプリング
外挿するタイミング:実際の操作数を数える場合(スパン数だけを数える場合を除く)は、必ず外挿します。多重度係数を使用します。
fetch spans
| fieldsAdd sampling.probability = (power(2, 56) - coalesce(sampling.threshold, 0)) * power(2, -56)
| fieldsAdd sampling.multiplicity = 1 / sampling.probability
| fieldsAdd multiplicity = coalesce(sampling.multiplicity, 1)
* coalesce(aggregation.count, 1)
* dt.system.sampling_ratio
| summarize operation_count = sum(multiplicity)
📖 詳細はこちら:サンプリングと外挿で、詳しい数式と例をご覧ください。
よく使われるクエリパターン
基本的なスパンへのアクセス
スパンを取得し、種類ごとに確認します:
fetch spans | limit 1
関数と種類ごとにスパンを確認します:
fetch spans
| summarize count(), by: { span.kind, code.namespace, code.function }
リクエストルートのフィルタリング
リクエストルートスパン(受信したサービス呼び出し)を一覧表示します:
fetch spans
| filter request.is_root_span == true
| fields trace.id, span.id, start_time, response_time = duration, endpoint.name
| limit 100
サービスパフォーマンスの概要
エラー率を含めてサービスパフォーマンスを分析します:
fetch spans
| filter request.is_root_span == true
| summarize
total_requests = count(),
failed_requests = countIf(request.is_failed == true),
avg_duration = avg(duration),
p95_duration = percentile(duration, 95),
by: {dt.service.name}
| fieldsAdd error_rate = (failed_requests * 100.0) / total_requests
| sort error_rate desc
トレース ID の検索
特定のトレース内のすべてのスパンを検索します:
fetch spans
| filter trace.id == toUid("abc123def456")
| fields span.name, duration, dt.service.name
パフォーマンス分析
レスポンスタイムのパーセンタイル
エンドポイントごとにパーセンタイルを計算します:
fetch spans
| filter request.is_root_span == true
| summarize {
requests=count(),
avg_duration=avg(duration),
p95=percentile(duration, 95),
p99=percentile(duration, 99)
}, by: { endpoint.name }
| sort p99 desc
💡 ベストプラクティス:パフォーマンスの分析には、平均値よりもパーセンタイル(p95、p99)を使用します。
遅いトレースの検出
しきい値を超えるリクエストを検索します:
fetch spans, from:now() - 2h
| filter request.is_root_span == true
| filter duration > 5s
| fields trace.id, span.name, dt.service.name, duration
| sort duration desc
| limit 50
外れ値を含む期間バケット
fetch spans, from:now() - 24h
| filter http.route == "/api/v1/storage/findByISBN"
| summarize {
spans=count(),
trace=takeAny(record(start_time, trace.id))
}, by: { bin(duration, 10ms) }
| fields `bin(duration, 10ms)`, spans, trace.id=trace[trace.id], start_time=trace[start_time]
パフォーマンス時系列
レスポンスタイムを時系列として抽出します:
fetch spans, from:now() - 24h
| filter request.is_root_span == true
| makeTimeseries {
requests=count(),
avg_duration=avg(duration),
p95=percentile(duration, 95),
p99=percentile(duration, 99)
}, by: { endpoint.name }
📖 詳細はこちら:高度なパターンと時系列の手法については、パフォーマンス分析を参照してください。
障害の調査
失敗したリクエストの概要
サービスごとに失敗を要約します:
fetch spans
| filter request.is_root_span == true
| summarize
total = count(),
failed = countIf(request.is_failed == true),
by: { dt.service.name }
| fieldsAdd failure_rate = (failed * 100.0) / total
| sort failure_rate desc
失敗理由の分析
失敗検出理由ごとに分類します:
fetch spans
| filter request.is_failed == true and isNotNull(dt.failure_detection.results)
| expand dt.failure_detection.results
| summarize count(), by: { dt.failure_detection.results[reason] }
失敗理由:
http_code- HTTP レスポンスコードによって失敗が発生しましたgrpc_code- gRPC ステータスコードによって失敗が発生しましたexception- 例外によって失敗が発生しましたspan_status- スパンのステータスが失敗を示しましたcustom_rule- カスタム失敗検出ルールに一致しました
HTTP コードによる失敗
HTTP ステータスコードごとに失敗を検索します:
fetch spans
| filter request.is_failed == true
| filter iAny(dt.failure_detection.results[][reason] == "http_code")
| summarize count(), by: { http.response.status_code, endpoint.name }
| sort `count()` desc
最近失敗したリクエスト
詳細とともに最近の失敗を一覧表示します:
fetch spans
| filter request.is_root_span == true and request.is_failed == true
| fields
start_time,
trace.id,
endpoint.name,
http.response.status_code,
duration
| sort start_time desc
| limit 100
📖 詳細はこちら:例外分析とカスタムルールの調査については、失敗の検出を参照してください。
サービス依存関係
サービス間の分析
サービス間の通信パターンを分析します:
fetch spans, from:now() - 1h
| filter isNotNull(server.address)
| fieldsAdd
remote_side = server.address
| summarize
call_count = count(),
avg_duration = avg(duration),
by: {dt.service.name, remote_side}
| sort call_count desc
外部への HTTP 呼び出し
外部 API への依存関係を特定します:
fetch spans
| filter span.kind == "client" and isNotNull(http.request.method)
| summarize
calls = count(),
avg_latency = avg(duration),
p99_latency = percentile(duration, 99),
by: { dt.service.name, server.address, server.port }
| sort calls desc
トレースの集約
トレース全体の分析
トレース内のすべてのスパンを集約して、リクエストの全体的な流れを把握します:
fetch spans, from:now() - 30m
| summarize {
spans = count(),
client_spans = countIf(span.kind == "client"),
// Endpoints involved in the trace
endpoints = toString(arrayRemoveNulls(collectDistinct(endpoint.name))),
// Extract the first request root in the trace
trace_root = takeMin(record(
root_detection_helper = coalesce(
if(request.is_root_span, 1),
if(isNull(span.parent_id), 2),
3),
start_time, endpoint.name, duration
))
}, by: { trace.id }
| fieldsFlatten trace_root
| fieldsRemove trace_root.root_detection_helper, trace_root
| fields
start_time = trace_root.start_time,
endpoint = trace_root.endpoint.name,
response_time = trace_root.duration,
spans,
client_spans,
endpoints,
trace.id
| sort start_time
| limit 100
ルート検出戦略:検出ヘルパーと takeMin(record(...)) を使用して、リクエストルートを確実に特定します:
- 優先度 1:
request.is_root_span == trueのスパン - 優先度 2:親を持たないスパン(ルートスパン)
- 優先度 3:その他すべてのスパン
複数サービスにまたがるトレース
複数のサービスにまたがるトレースを検索します:
fetch spans, from:now() - 1h
| summarize {
services = collectDistinct(dt.service.name),
trace_root = takeMin(record(root_detection_helper = coalesce(if(request.is_root_span, 1), 2), endpoint.name))
}, by: { trace.id }
| fieldsAdd service_count = arraySize(services)
| filter service_count > 1
| fields endpoint = trace_root[endpoint.name], service_count, services = toString(services), trace.id
| sort service_count desc
| limit 50
リクエストレベルの分析
リクエスト属性
リクエストルートスパン上で OneAgent が取得したカスタムリクエスト属性にアクセスします:
fetch spans
| filter request.is_root_span == true
| filter isNotNull(request_attribute.PaidAmount)
| makeTimeseries sum(request_attribute.PaidAmount)
フィールドパターン:request_attribute.<name>、captured_attribute.<name>(常に配列)
→ リクエスト属性 — リクエスト属性、取得属性、リクエスト ID 集約の全パターン
スパンの種類
| スパンの種類 | 検出条件 | 主なフィールド | 参照 |
|---|---|---|---|
| HTTP サーバー(受信) | span.kind == "server" and isNotNull(http.request.method) | http.route、http.request.method、http.response.status_code | http-spans.md |
| HTTP クライアント(送信) | span.kind == "client" and isNotNull(http.request.method) | server.address、server.port | http-spans.md |
| データベース | span.kind == "client" and isNotNull(db.system) | db.system、db.namespace、db.statement | database-spans.md |
| メッセージング | isNotNull(messaging.system) | messaging.system、messaging.destination.name、messaging.operation.type | messaging-spans.md |
| RPC / gRPC | isNotNull(rpc.system) | rpc.system、rpc.service、rpc.method、rpc.grpc.status_code | rpc-spans.md |
| サーバーレス / FaaS | isNotNull(faas.name) and span.kind == "server" | faas.name、faas.trigger.type、cloud.provider | serverless-spans.md |
⚠️ データベーススパン:集約されている場合があります(1 つのスパンが複数の呼び出しを表します)。正確な操作数を求めるには、必ず aggregation.count による外挿を使用してください。
📖 スパンの種類ごとの詳細なパターン: 上記のリファレンスファイルを参照してください。
高度なトピック
例外分析
例外は、スパン内の span.events として保存されます。
fetch spans
| filter iAny(span.events[][span_event.name] == "exception")
| expand span.events
| fieldsFlatten span.events, fields: { exception.type }
| summarize {
count(),
trace=takeAny(record(start_time, trace.id))
}, by: { exception.type }
| fields exception.type, `count()`, trace.id=trace[trace.id], start_time=trace[start_time]
💡 ヒント:スパンイベント配列内の条件を確認するには、iAny() を使用してください。
→ ログの相関 — ログとトレースの結合、ログ内容によるトレースのフィルタリング → ネットワーク分析 — クライアント IP、DNS 解決、サブネット分析
ベストプラクティス
| 項目 | ルール |
|---|---|
| フィルタリング | 最初に request.is_root_span == true とエンドポイントフィルターを適用します |
| サンプリング | パフォーマンス向上のため、samplingRatio(例:100 = 1% を読み取り)を使用します |
| パーセンタイル | パフォーマンス分析では平均値よりも p95/p99 を使用します |
| ルートスパン | エンドツーエンド分析には request.is_root_span == true を使用します |
| トレースのグループ化 | 完全なトレースメトリクスを得るには、trace.id でグループ化します |
| リクエストのグループ化 | OneAgent のみのリクエストメトリクスを得るには、request.id でグループ化します |
| 外挿 | 正確な操作数を得るために、必ず多重度を適用します |
| エグザンプラー | UI での詳細表示を有効にするには、takeAny(record(start_time, trace.id)) を使用します |
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| 期間の値が不自然に大きい | duration はミリ秒ではなくナノ秒単位です | 1000000 で割るか、5s(DQL の期間リテラル)と比較してください |
| スパン数が想定されるリクエスト数と一致しない | サンプリングまたは集約が考慮されていません | 多重度による外挿を使用してください。サンプリングと外挿のリファレンスを参照してください |
getNodeName(dt.smartscape.service) が null を返す | サービスがまだ解決されていないか、OneAgent による監視が行われていません | OneAgent がサービスを監視していることを確認してください。エンティティの解決には少し時間がかかる場合があります |
request.is_root_span フィルターで何も返されない | OneAgent を使用しない OpenTelemetry 専用のトレースをクエリしています | ルートスパンの検出には、代わりに isNull(span.parent_id) を使用してください |
trace.id フィルターで結果が返されない | トレース ID が UID 形式に変換されていません | 文字列ベースのトレース ID には filter trace.id == toUid("abc123...") を使用してください |
| データベーススパン数が少なすぎる | データベーススパンは集約されています(1 つのスパンが N 回の呼び出しを表します) | データベース操作数を求めるには、必ず aggregation.count による外挿を使用してください |
関連スキル
- dt-dql-essentials — トレースデータのクエリに使う DQL の基本構文
- dt-app-dashboards — トレースクエリをダッシュボードに埋め込む
- dt-migration — Smartscape エンティティモデルと関係のナビゲーション
参考資料
特定のトピックに関する詳細なドキュメント:
- パフォーマンス分析 - 高度な時系列分析、期間バケット、エンドポイントのランキング
- 障害検出 - 障害の理由、例外の調査、カスタムルール
- サンプリングと外挿 - 多重度の計算、データベースの外挿
- リクエスト属性 - リクエスト属性、キャプチャされた属性、リクエスト ID の集計
- エンティティの検索 - 高度なノード検索、インフラストラクチャの相関付け、ハードウェア分析
- HTTP スパン分析 - ステータスコード、ペイロード分析、クライアント IP
- データベーススパン分析 - 外挿したカウント、遅いクエリ、ステートメント分析
- メッセージングスパン分析 - Kafka、RabbitMQ、SQS のスループットとレイテンシ
- RPC スパン分析 - gRPC、SOAP、サービスの依存関係
- サーバーレススパン分析 - Lambda、Azure Functions、コールドスタート分析
- ログの相関付け - ログとトレースの結合、相関パターン
- ネットワーク分析 - IP アドレス、DNS 解決、通信マッピング