关于 dt-obs-tracing
dt-obs-tracing 是面向 Dynatrace 追踪分析的 Skill,帮助用户通过 DQL 查询 Grail 中的 span。涵盖性能、失败原因、服务依赖和不同 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 中的分布式追踪由多个 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 |
duration | Span 耗时,单位为纳秒 |
span.timing.cpu | Span 的总 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:
request.is_root_span == true的 Span - 优先级 2:没有父 Span 的 Span(根 Span)
- 优先级 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_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 |
⚠️ 数据库 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 实体模型和关系导航
参考资料
特定主题的详细文档:
- 性能分析 - 高级时间序列、时长分桶、端点排名
- 故障检测 - 故障原因、异常调查、自定义规则
- 采样与外推 - 倍数计算、数据库外推
- 请求属性 - 请求属性、捕获的属性、请求 ID 聚合
- 实体查询 - 高级节点查询、基础设施关联、硬件分析
- HTTP Span 分析 - 状态码、负载分析、客户端 IP
- 数据库 Span 分析 - 外推计数、慢查询、语句分析
- 消息传递 Span 分析 - Kafka、RabbitMQ、SQS 吞吐量与延迟
- RPC Span 分析 - gRPC、SOAP、服务依赖
- 无服务器 Span 分析 - Lambda、Azure Functions、冷启动分析
- 日志关联 - 联结日志与追踪、关联模式
- 网络分析 - IP 地址、DNS 解析、通信映射