mcpField
D
dt-obs-tracing

dt-obs-tracing

github.com/dynatrace云与 DevOps1692.7k 次安装今天更新
代码仓库

关于 dt-obs-tracing

dt-obs-tracing 是面向 Dynatrace 追踪分析的 Skill,帮助用户通过 DQL 查询 Grail 中的 span。涵盖性能、失败原因、服务依赖和不同 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 中的分布式追踪由多个 span 组成,这些 span 是代表工作单元的构建块。借助 Grail 中的 Traces,可以通过 DQL 访问每个 span,并对所有属性进行全文搜索。本技能涵盖追踪基础知识、常见分析模式以及针对不同 span 类型的查询。


使用场景

1. 调查慢请求
  • 目标: 找出并诊断超出延迟阈值的请求
  • 触发条件:“慢请求”“高延迟”“p99 响应时间”“找出耗时超过 5 秒的追踪”
  • 完成标准: 列出慢追踪的耗时、端点、服务和追踪 ID,以便深入调查
2. 分析请求失败
  • 目标: 找出失败的请求、失败原因和异常模式
  • 触发条件:“失败的 span”“HTTP 500 错误”“异常分析”“按服务统计失败率”
  • 完成标准: 按原因(HTTP 代码、异常、gRPC 状态)细分失败情况,并提供示例追踪
3. 映射服务依赖关系
  • 目标: 了解服务间通信模式和外部 API 调用
  • 触发条件:“服务依赖关系”“X 会调用哪些服务”“传出的 HTTP 调用”
  • 完成标准: 显示服务间调用次数、延迟和错误率的依赖关系图

核心概念

了解追踪和 span

Span 代表分布式追踪中的逻辑工作单元:

  • HTTP 请求、RPC 调用、数据库操作
  • 消息系统交互
  • 内部函数调用
  • 自定义插桩点

Span 类型:

  • span.kind: server - 服务收到的调用
  • span.kind: client - 服务发出的调用
  • span.kind: consumer - 服务收到的消息消费调用
  • span.kind: producer - 服务发出的消息生产调用
  • span.kind: internal - 服务内部的操作

根 span:请求根 span(request.is_root_span == true)代表服务收到的调用。可用它分析端到端请求性能。

关键追踪属性

追踪分析所需的基本属性:

属性说明
trace.id唯一追踪标识符
span.id唯一 span 标识符
span.parent_id父 span ID(根 span 为 null)
request.is_root_span布尔值;请求入口点为 true
request.is_failed布尔值;请求失败时为 true
durationSpan 耗时,单位为纳秒
span.timing.cpuSpan 的总 CPU 时间(稳定)
span.timing.cpu_self不含子 span 的 CPU 时间(稳定)
dt.smartscape.service服务 Smartscape 节点 ID
dt.service.name根据服务检测规则确定的 Dynatrace 服务名称,与 Smartscape 服务节点名称相同。
endpoint.name端点/路由名称
服务上下文

Span 通过 Smartscape 节点 ID 和检测到的服务名称 dt.service.name 引用服务,该名称也存在于每个 span 中。

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") - 访问指定的节点属性

📖 了解更多:请参阅实体查询,了解高级实体选择器、基础设施关联和硬件分析。

采样与外推

由于以下原因,一个 span 可能代表多个实际操作:

  • 聚合:一个 span 中包含多个操作(aggregation.count)
  • ATM(自适应流量管理):由 Agent 执行的基于头部的采样
  • ALR(自适应负载降低):服务端采样
  • 读取采样:通过 samplingRatio 参数在查询时进行采样

何时进行外推:统计实际操作次数时(而不只是统计 span 数量),始终进行外推。使用倍数因子:

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)

📖 了解更多:请参阅采样与外推,了解详细公式和示例。

常见查询模式

基本 Span 访问

获取 Span 并按类型探索:

fetch spans | limit 1

按函数和类型探索 Span:

fetch spans
| summarize count(), by: { span.kind, code.namespace, code.function }
请求根筛选

列出请求根 Span(传入的服务调用):

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
Trace ID 查找

查找特定 Trace 中的所有 Span:

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)而非平均值来洞察性能。

慢 Trace 检测

查找超过阈值的请求:

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 - Span 状态表明发生失败
  • 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

Trace 聚合

完整 Trace 分析

聚合一个 Trace 中的所有 Span,以了解完整的请求流:

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

根 Span 检测策略:使用 takeMin(record(...)) 和检测辅助工具,可靠地查找根请求:

  1. 优先级 1:request.is_root_span == true 的 Span
  2. 优先级 2:没有父 Span 的 Span(根 Span)
  3. 优先级 3:所有其他 Span
多服务 Trace

查找跨多个服务的 Trace:

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 在请求根 Span 上捕获的自定义请求属性:

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 聚合的完整模式

Span 类型

Span 类型检测方式关键字段参考资料
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

⚠️ 数据库 Span:可能经过聚合(一个 Span = 多次调用)。为准确统计操作次数,请始终使用 aggregation.count 外推。

📖 各 Span 类型的详细模式: 请参阅上方的参考文件。

高级主题

异常分析

异常以 span.events 的形式存储在 Span 中:

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() 检查 Span 事件数组中的条件。

→ 日志关联 — 联接日志和追踪,并按日志内容筛选追踪 → 网络分析 — 客户端 IP、DNS 解析、子网分析

最佳实践

领域规则
筛选优先应用 request.is_root_span == true 和端点筛选条件
采样使用 samplingRatio(例如,100 = 读取 1%)以提升性能
百分位数性能分析使用 p95/p99,而非平均值
根 Span使用 request.is_root_span == true 进行端到端分析
追踪分组按 trace.id 分组,以获取完整的追踪指标
请求分组按 request.id 分组,以获取仅限 OneAgent 的请求指标
外推始终应用倍数,以准确统计操作次数
示例使用 takeAny(record(start_time, trace.id)) 启用 UI 下钻

故障排除

问题原因解决方案
持续时间值似乎不正确(过大)duration 的单位是纳秒,而非毫秒除以 1000000,或与 5s(DQL 持续时间字面量)比较
Span 数量与预期的请求量不符未考虑采样或聚合使用倍数外推 — 请参阅“采样和外推”参考资料
getNodeName(dt.smartscape.service) 返回 null服务尚未解析,或 OneAgent 未监控该服务确认 OneAgent 正在监控该服务;实体解析可能会有短暂延迟
request.is_root_span 筛选条件没有结果查询的是不含 OneAgent 的 OpenTelemetry 追踪使用 isNull(span.parent_id) 作为根 Span 检测的回退方案
trace.id 筛选条件没有结果Trace ID 未转换为 UID 格式对基于字符串的 Trace ID 使用 filter trace.id == toUid("abc123...")
数据库 Span 数量过少数据库 Span 经过聚合(一个 Span = N 次调用)统计数据库操作次数时,请始终使用 aggregation.count 外推

相关技能

  • dt-dql-essentials — 查询追踪数据的核心 DQL 语法
  • dt-app-dashboards — 在仪表板中嵌入追踪查询
  • dt-migration — Smartscape 实体模型和关系导航

参考资料

特定主题的详细文档:

评论

支持 Markdown