mcpField
D
dt-obs-tracing

dt-obs-tracing

github.com/dynatraceクラウド・DevOps1692.7k 回インストール今日更新
リポジトリ

dt-obs-tracing について

dt-obs-tracingは、DQLでGrail内のspanを調べるDynatraceのトレーシング分析スキルです。性能、障害、サービス依存関係、span種別ごとのクエリパターンを扱います。

主な機能

  1. 01分散トレースとspanのクエリ・把握
  2. 02リクエスト遅延と性能パーセンタイルの分析
  3. 03サービスや理由別の障害調査
  4. 04サービス呼び出しと外部依存関係の分析
  5. 05複数のspan種別と属性のクエリ

活用シーン

  1. 01遅延しきい値を超えたリクエストの特定
  2. 02サービスや理由別の障害分析
  3. 03サービス間呼び出しの把握
  4. 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.md
entity-lookups.md
failure-detection.md
http-spans.md
logs-correlation.md
messaging-spans.md
networking-analysis.md
performance-analysis.md
request-attributes.md
rpc-spans.md
sampling-extrapolation.md
serverless-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. 優先度 1:request.is_root_span == true のスパン
  2. 優先度 2:親を持たないスパン(ルートスパン)
  3. 優先度 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_codehttp-spans.md
HTTP クライアント(送信)span.kind == "client" and isNotNull(http.request.method)server.address、server.porthttp-spans.md
データベースspan.kind == "client" and isNotNull(db.system)db.system、db.namespace、db.statementdatabase-spans.md
メッセージングisNotNull(messaging.system)messaging.system、messaging.destination.name、messaging.operation.typemessaging-spans.md
RPC / gRPCisNotNull(rpc.system)rpc.system、rpc.service、rpc.method、rpc.grpc.status_coderpc-spans.md
サーバーレス / FaaSisNotNull(faas.name) and span.kind == "server"faas.name、faas.trigger.type、cloud.providerserverless-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 エンティティモデルと関係のナビゲーション

参考資料

特定のトピックに関する詳細なドキュメント:

コメント