关于 sentry-span-streaming-python
面向 Python 项目的迁移技能,指导将 Sentry 从事务式追踪切换为逐 Span 流式发送,并更新相关 API、属性和采样逻辑。
主要功能
- 01检测 Sentry 初始化与追踪用法
- 02配置流式 Span 生命周期
- 03迁移 Span 创建与装饰器
- 04将数据和标签改为属性
- 05更新追踪传播与采样规则
适用场景
- 01将 Python 项目切换到 Span 流式发送
- 02迁移旧版 start_span 和事务 API
- 03调整 Span 属性、采样及过滤配置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ getsentry/ sentry-for-ai --skill sentry-span-streaming-python安装后在 Claude Code 中输入/sentry-span-streaming-python确认已加载。
详细介绍
Sentry Span 流式传输迁移(Python)
从默认的基于事务的跟踪生命周期(static)迁移到 Span 流式传输(stream):Span 完成后会逐个发送,而不是在最后批量发送到一个事务中。
此技能适用于 Python SDK。JavaScript 请参阅 Span 流式传输(JavaScript)。Dart/Flutter 请参阅 Span 流式传输(Dart)。
在以下情况下调用此技能
- 用户要求在 Python 项目中“启用 Span 流式传输”或“迁移到 Span 流式传输”
- 用户想从基于事务的 Span 交付切换为流式交付
- 用户提到
trace_lifecycle、sentry_sdk.traces或新的start_spanAPI - 用户希望降低 Span 交付延迟或逐个处理 Span
检测环境
# Find sentry_sdk.init calls
grep -rn "sentry_sdk\.init\|sentry_sdk\.init(" --include="*.py" -l 2>/dev/null | head -20
# Find existing start_span / start_transaction / start_child usage
grep -rn "start_span\|start_transaction\|start_child" --include="*.py" -l 2>/dev/null | head -20
# Find trace decorator usage
grep -rn "@trace\|from sentry_sdk import trace\|from sentry_sdk.tracing import trace" --include="*.py" -l 2>/dev/null | head -20
# Find continue_trace usage
grep -rn "continue_trace" --include="*.py" -l 2>/dev/null | head -20
# Find before_send_transaction usage
grep -rn "before_send_transaction" --include="*.py" -l 2>/dev/null | head -20
# Find direct Span / Transaction class imports
grep -rn "from sentry_sdk.tracing import\|from sentry_sdk import.*Span\|from sentry_sdk import.*Transaction" --include="*.py" -l 2>/dev/null | head -20
# Find set_data / set_tag / set_context on spans
grep -rn "set_data\|set_tag\|set_context" --include="*.py" -l 2>/dev/null | head -20
# Find scope.span / scope.transaction / containing_transaction access
grep -rn "scope\.span\|scope\.transaction\|containing_transaction" --include="*.py" -l 2>/dev/null | head -20
# Find get_trace_context usage
grep -rn "get_trace_context" --include="*.py" -l 2>/dev/null | head -20
并行处理
检测环境后,评估需要修改多少个文件。如果代码库中有许多文件需要迁移(例如,数十个文件中使用了 start_span、start_transaction、set_data 等),请启动子 Agent 并行处理互不相关的迁移任务——例如,每个子 Agent 负责一个迁移类别(创建 Span、Span 数据、跟踪传播),或一个模块/软件包。每个子 Agent 都应收到此技能中相关的迁移规则,并处理不同的文件集。
启用 Span 流式传输
前提条件: sentry-sdk >=2.66.0,并且已启用跟踪(配置了 traces_sample_rate 或 traces_sampler)。
在所有 sentry_sdk.init() 调用中添加顶层 trace_lifecycle 选项:
import sentry_sdk
# Before
sentry_sdk.init(
dsn="...",
traces_sample_rate=1.0,
)
# After
sentry_sdk.init(
dsn="...",
traces_sample_rate=1.0,
trace_lifecycle="stream",
)
Span 流式传输要求使用新的 sentry_sdk.traces.start_span API。旧版 sentry_sdk.start_span 和 sentry_sdk.start_transaction API 不会以流式方式传输 Span。
迁移 Span 创建方式
start_span
将 sentry_sdk.start_span() 替换为 sentry_sdk.traces.start_span():
import sentry_sdk
# Before
with sentry_sdk.start_span(name="flow.checkout") as span:
...
# After
with sentry_sdk.traces.start_span(name="flow.checkout") as span:
...
或者更改导入语句:
# Before
from sentry_sdk import start_span
# After
from sentry_sdk.traces import start_span
新 API 接受以下参数:
name(必需)attributes— 键值对(请参阅下文的迁移 Span 数据)parent_span— 显式指定父 Span;默认为当前活动 Spanactive— 默认为True;如果为False,该 Span 不会自动成为其他 Span 的父 Span
description 参数已不存在,请改用 name。不再支持 op 参数,请改用 sentry.op 属性:
# Before
with sentry_sdk.start_span(op="http.client", description="GET /api/users") as span:
...
# After
with sentry_sdk.traces.start_span(
name="GET /api/users",
attributes={"sentry.op": "http.client"},
) as span:
...
start_transaction
将 sentry_sdk.start_transaction() 替换为 sentry_sdk.traces.start_span():
import sentry_sdk
# Before
with sentry_sdk.start_transaction(name="flow.checkout") as transaction:
...
# After
with sentry_sdk.traces.start_span(name="flow.checkout", parent_span=None) as span:
...
设置 parent_span=None 会强制该 Span 成为根 Span,这相当于在旧版 API 中启动一个事务。
start_child
span.start_child() 已不存在。请在父 Span 处于活动状态时启动一个新 Span,它会自动成为子 Span:
import sentry_sdk
# Before
with sentry_sdk.start_span(name="outer") as parent:
with parent.start_child(op="db", description="SELECT") as child:
...
# After
with sentry_sdk.traces.start_span(name="outer") as parent:
with sentry_sdk.traces.start_span(
name="SELECT",
attributes={"sentry.op": "db"},
):
...
如需显式控制父子关系(例如,让一个 Span 成为同级 Span 而非子 Span),请使用 parent_span:
with sentry_sdk.traces.start_span(name="outer") as span:
with sentry_sdk.traces.start_span(name="child 1"):
with sentry_sdk.traces.start_span(name="child 2", parent_span=span):
# "child 2" is a sibling of "child 1", not its child
...
@trace 装饰器
将 sentry_sdk.trace 替换为 sentry_sdk.traces.trace:
# Before
from sentry_sdk import trace
@trace
def checkout():
...
# After — just change the import
from sentry_sdk.traces import trace
@trace
def checkout():
...
新装饰器还接受可选的 name(默认为函数名)、attributes 和 active 参数:
from sentry_sdk.traces import trace
@trace(name="checkout", attributes={"flow.pipeline": "legacy"})
def checkout():
...
get_current_span
将 sentry_sdk.get_current_span() 替换为 sentry_sdk.traces.get_current_span():
# Before
from sentry_sdk import get_current_span
span = get_current_span()
# After
from sentry_sdk.traces import get_current_span
span = get_current_span()
scope.span 和 scope.transaction
如果代码通过 scope 对象访问当前 Span 或事务,请迁移到新的 API:
import sentry_sdk
# Before
scope = sentry_sdk.get_current_scope()
current_span = scope.span
# After
current_span = sentry_sdk.traces.get_current_span()
import sentry_sdk
# Before
scope = sentry_sdk.get_current_scope()
transaction = scope.transaction
# After
root_span = sentry_sdk.traces.get_current_span()._segment
_segment 会返回当前跟踪的根 Span(相当于以前的事务)。这是一个私有 API;请尽可能调整代码结构,避免需要使用根 Span。
span.containing_transaction
span.containing_transaction 已不存在。请使用 span._segment 获取根 Span:
# Before
transaction = span.containing_transaction
# After
root_span = span._segment
Span 和 Transaction 类
如果代码直接导入 Span 或 Transaction(例如用于类型注解),请将两者都替换为 StreamedSpan:
# Before
from sentry_sdk.tracing import Span, Transaction
def process(span: Span) -> None:
...
# After
from sentry_sdk.traces import StreamedSpan
def process(span: StreamedSpan) -> None:
...
get_trace_context
流式 span 上不再提供 span.get_trace_context()。请根据实际需要从 trace context 中获取的内容进行迁移:
如果代码只读取特定字段(例如 trace_id、span_id 或 parent_span_id),请直接将其作为 span 属性访问:
# Before
ctx = span.get_trace_context()
trace_id = ctx["trace_id"]
span_id = ctx["span_id"]
# After
trace_id = span.trace_id
span_id = span.span_id
如果代码确实需要完整的 trace context 字典(例如传递给外部系统或进行序列化),请使用私有方法 span._get_trace_context():
# Before
ctx = span.get_trace_context()
# After
ctx = span._get_trace_context()
尽可能优先直接访问属性——_get_trace_context() 是私有 API,可能会发生变化。
迁移 Span 数据
在 span 流式模式下,span 没有上下文、数据或标签。一切都是 span 属性。属性键为字符串;值必须是 int、bool、str、float 或这些类型的数组。不支持 None。
**重要:**与旧版 set_data / set_tag API 不同,set_attribute 只支持基元类型。非基元值必须转换为字符串,或拆分为多个基元类型的属性:
# Before — set_data accepted any type
span.set_data("request", {"method": "POST", "path": "/api/checkout"})
span.set_data("response_headers", response.headers)
# After — flatten dicts into separate attributes
span.set_attributes({
"request.method": "POST",
"request.path": "/api/checkout",
})
# After — stringify objects that can't be flattened
span.set_attribute("response_headers", str(response.headers))
替换 set_data
# Before
span.set_data("flow.step", "submit_payment")
# After
span.set_attribute("flow.step", "submit_payment")
替换 set_tag
# Before
span.set_tag("http.status_code", 201)
# After
span.set_attribute("http.response.status_code", 201)
迁移多个连续的 set_data / set_tag 调用时,请将它们合并为一次 set_attributes() 调用:
# Before
span.set_data("flow.step", "submit_payment")
span.set_data("flow.version", "0.35")
span.set_tag("http.status_code", 201)
# After
span.set_attributes({
"flow.step": "submit_payment",
"flow.version": "0.35",
"http.response.status_code": 201,
})
替换 set_context
属性值不支持字典。请将其展开为独立属性:
# Before
span.set_context("flow", {"id": "123456789", "pipeline": "legacy"})
# After
span.set_attributes({"flow.id": "123456789", "flow.pipeline": "legacy"})
作用域级标签
通过 sentry_sdk.set_tag() 在作用域上设置的标签不会应用于流式 span。请使用 sentry_sdk.set_attribute() 将数据应用于 span:
import sentry_sdk
sentry_sdk.set_tag("region", "Europe") # applied to errors and other tag-supporting telemetry
sentry_sdk.set_attribute("region", "Europe") # applied to spans, logs, metrics
批量操作
with sentry_sdk.traces.start_span(name="flow.checkout") as span:
span.set_attribute("flow.version", "0.35")
span.set_attributes({"flow.conversion": 1.0, "flow.use_new_pipeline": True})
span.remove_attribute("flow.conversion")
Span 状态
状态只能为 ok(默认值)或 error:
from sentry_sdk.traces import start_span
with start_span(name="process") as span:
try:
...
except Exception:
span.status = "error"
迁移 Trace 传播
sentry_sdk.traces.continue_trace() 替代了旧版 sentry_sdk.continue_trace()。它不再是上下文管理器——它会设置传播上下文,下一个 span 会自动获取该上下文:
import sentry_sdk
headers = {
"sentry-trace": "4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-1",
"baggage": "sentry-trace_id=...",
}
# Before
with sentry_sdk.continue_trace(headers) as transaction:
...
# After
sentry_sdk.traces.continue_trace(headers)
with sentry_sdk.traces.start_span(name="handle request"):
...
要开始全新的 trace,请使用 sentry_sdk.traces.new_trace():
import sentry_sdk
with sentry_sdk.traces.start_span(name="span in trace 1"):
...
sentry_sdk.traces.new_trace()
with sentry_sdk.traces.start_span(name="span in trace 2"):
# This span is the root of a new, separate trace
...
迁移采样
如果使用 traces_sample_rate,则无需更改——其工作方式保持不变。
如果使用自定义 traces_sampler,采样上下文在流式模式下的结构有所不同:
def traces_sampler(sampling_context):
# sampling_context["span_context"] contains:
# name, trace_id, parent_span_id, parent_sampled, attributes
if sampling_context["span_context"]["name"] in IGNORED_SPAN_NAMES:
return 0.0
return 1.0
sentry_sdk.init(
traces_sampler=traces_sampler,
trace_lifecycle="stream",
)
调用 start_span() 时会做出采样决策。除非被 ignore_spans 过滤,否则子 span 会继承父 span 的采样决策。
自定义采样上下文
自定义采样上下文不再是 start_span 的参数。请在启动 span 前将其设置到作用域上:
import sentry_sdk
def traces_sampler(sampling_context):
if sampling_context["asgi_scope"].method not in ("GET", "POST"):
return 0.0
return 1.0
# Before
with sentry_sdk.start_span(
name="handle request",
custom_sampling_context={"asgi_scope": asgi_scope},
):
...
# After
sentry_sdk.Scope.set_custom_sampling_context({"asgi_scope": asgi_scope})
with sentry_sdk.traces.start_span(name="handle request"):
...
必须在 continue_trace(会重置传播上下文)之后、start_span(此时进行采样)之前设置自定义采样上下文。
配置 ignore_spans(可选)
ignore_spans 会在创建 span 时对其进行过滤。规则可以是字符串、已编译的正则表达式,或包含 name 和/或 attributes 条件的字典:
import re
import sentry_sdk
sentry_sdk.init(
trace_lifecycle="stream",
ignore_spans=[
# String match against span name
"/health",
# Regex match against span name
re.compile(r"/flow/.*"),
# Match by attributes (all must match)
{
"attributes": {
"service.id": "15def9a",
"flow.pipeline": "legacy",
}
},
# Match by name and attributes
{
"name": re.compile(r"/flow/.*"),
"attributes": {
"service.id": re.compile(r".*\.facade"),
},
},
],
)
只有启用 trace_lifecycle="stream" 时,ignore_spans 才会生效。如果设置了该选项但未启用 span 流式模式,SDK 会发出警告并忽略该选项。
匹配时只能使用 span 名称和创建时设置的属性——span 生命周期后续添加的属性不会纳入考虑。
如果被忽略的 span 是顶层 span,其整个子树也会被忽略。如果被忽略的 span 不是顶层 span,则其子 span 不会自动被忽略,除非它们本身也匹配某条规则。
迁移 before_send_transaction
before_send_transaction 在流式模式下不起作用。Span 完成后会单独发送,而不是批量发送为事务。
重要: 在旧版基于事务的模型中,before_send_transaction 会在整个事务结束后运行,因此可以访问 Span 生命周期内设置的所有数据(例如 HTTP 状态码、响应大小、最终结果)。在流式模式下,替代方案(ignore_spans 和 before_send_span)只能处理 Span 开始时设置的属性。Span 生命周期内稍后添加的属性(例如 http.response.status_code、响应正文大小或其他延后设置的数据)不可用。
如果你的 before_send_transaction 逻辑依赖于 Span 开始时未设置的属性,则该过滤逻辑无法在流式模式下复现,必须移除。可以考虑将此类过滤移至服务端机制(例如 Sentry 入站数据过滤器或 Relay 规则)。
| 使用场景 | 流式模式替代方案 |
|---|---|
| 按名称/路由丢弃 Span | 使用 ignore_spans |
| 按延后设置的属性(例如 HTTP 状态码)丢弃/过滤 | 无法复现 — 移除该逻辑或使用服务端过滤 |
| 发送前修改 Span 数据 | 使用 before_send_span |
| 按事务名称过滤 | 使用带有字符串/正则表达式模式的 ignore_spans |
迁移其逻辑后,从 sentry_sdk.init() 中移除 before_send_transaction 选项。
配置 before_send_span(可选)
before_send_span 允许你在 Span 离开 SDK 前对其进行修改,例如清理敏感值。它接收 span 和 hint 参数,并且必须返回一个 Span:
import sentry_sdk
def postprocess_span(span, hint):
attributes_to_sanitize = [
"http.request.header.custom-auth",
"http.request.header.custom-user-id",
]
for attribute in attributes_to_sanitize:
if span["attributes"].get(attribute):
span["attributes"][attribute] = "[Sanitized]"
return span
sentry_sdk.init(
trace_lifecycle="stream",
_experiments={
"before_send_span": postprocess_span,
},
)
before_send_span 不能用于丢弃 Span — 请使用 ignore_spans。如果回调返回的不是 Span 字典,则会忽略返回值。
验证
请指导用户进行以下验证:
- 检查 Sentry 仪表板:Span 应在完成后不久出现在 Traces 视图中,无需等待整个事务结束
- 检查日志:确保没有关于不支持的 Span 操作的 SDK 警告
常见问题
| 症状 | 原因 | 修复方法 |
|---|---|---|
| Span 未进行流式传输 | 使用了旧版 sentry_sdk.start_span | 切换为 sentry_sdk.traces.start_span |
start_child 上出现 AttributeError | start_child 在流式模式下已移除 | 父 Span 处于活动状态时使用 sentry_sdk.traces.start_span |
None 属性值被拒绝 | None 不支持作为属性值 | 移除该属性或使用哨兵字符串 |
set_data/set_tag 对 Span 没有作用 | 这些方法不适用于流式 Span | 使用 span.set_attribute() |
| Span 缺少 Scope 标签 | set_tag 不适用于流式 Span | 使用 sentry_sdk.set_attribute() |
traces_sampler 中无法获取自定义采样上下文 | 在 start_span 之后或 continue_trace 之前设置 | 在 continue_trace 之后、start_span 之前在 Scope 上设置 |
scope.span 返回了错误类型或 None | 在流式模式下,基于 Scope 的 Span 访问不可靠 | 使用 sentry_sdk.traces.get_current_span() |
containing_transaction 上出现 AttributeError | 该属性在流式模式下已移除 | 使用 span._segment |
get_trace_context 上出现 AttributeError | 该方法在流式模式下已移除 | 直接使用 span.trace_id / span.span_id,或使用 span._get_trace_context() |
未调用 before_send_transaction | 流式模式下属于预期行为 | 将逻辑迁移到 before_send_span 或 ignore_spans |
警告:ignore_spans 仅在 trace_lifecycle 设为 stream 时生效 | 设置了 ignore_spans,但未启用 Span 流式传输 | 使用 trace_lifecycle="stream" 启用流式传输,或移除 ignore_spans |
before_send_transaction 逻辑依赖于延后设置的属性(例如状态码) | 创建 Span 时无法获取这些属性 | 移除该逻辑或使用服务端过滤(Sentry 入站过滤器/Relay 规则) |
快速参考
最简设置
import sentry_sdk
sentry_sdk.init(
dsn="__DSN__",
traces_sample_rate=1.0,
trace_lifecycle="stream",
)
创建 Span
from sentry_sdk.traces import start_span
with start_span(name="my operation", attributes={"sentry.op": "task"}) as span:
span.set_attribute("result.count", 42)
迁移清单
- SDK 版本为
>=2.66.0 - 在
sentry_sdk.init()中添加顶层配置trace_lifecycle="stream" - 将
sentry_sdk.start_span()迁移到sentry_sdk.traces.start_span() - 将
sentry_sdk.start_transaction()迁移到sentry_sdk.traces.start_span() - 将
span.start_child()迁移到sentry_sdk.traces.start_span() - 将
sentry_sdk.get_current_span()迁移到sentry_sdk.traces.get_current_span() - 将
scope.span替换为sentry_sdk.traces.get_current_span() - 将
scope.transaction替换为sentry_sdk.traces.get_current_span()._segment - 将
span.containing_transaction替换为span._segment - 将
@sentry_sdk.trace迁移到@sentry_sdk.traces.trace - 将
Span/Transaction类导入替换为StreamedSpan - 将
span.get_trace_context()替换为直接属性(span.trace_id等)或span._get_trace_context() - 将
description替换为name - 将
op替换为sentry.op属性 - 将
set_data()/set_tag()/set_context()替换为set_attribute() - 在需要时,为作用域级的
set_tag()补充set_attribute() - 将
continue_trace迁移到非上下文管理器形式的sentry_sdk.traces.continue_trace() - 将
custom_sampling_context迁移到Scope.set_custom_sampling_context() - (如果使用
traces_sampler)更新逻辑以处理新的sampling_context结构 - 将
before_send_transaction逻辑迁移到before_send_span或ignore_spans - 移除或迁移依赖后期设置属性(例如 HTTP 状态码)的
before_send_transaction逻辑,将其放到服务器端过滤 - 从配置中移除
before_send_transaction - Span 在 Sentry 控制面板中可见