mcpField
S
sentry-span-streaming-python

sentry-span-streaming-python

github.com/getsentry开发272472 次安装今天更新
代码仓库

关于 sentry-span-streaming-python

面向 Python 项目的迁移技能,指导将 Sentry 从事务式追踪切换为逐 Span 流式发送,并更新相关 API、属性和采样逻辑。

主要功能

  1. 01检测 Sentry 初始化与追踪用法
  2. 02配置流式 Span 生命周期
  3. 03迁移 Span 创建与装饰器
  4. 04将数据和标签改为属性
  5. 05更新追踪传播与采样规则

适用场景

  1. 01将 Python 项目切换到 Span 流式发送
  2. 02迁移旧版 start_span 和事务 API
  3. 03调整 Span 属性、采样及过滤配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/getsentry/sentry-for-ai --skill sentry-span-streaming-python

安装后在 Claude Code 中输入/sentry-span-streaming-python确认已加载。

详细介绍

所有技能 > 功能设置 > Span 流式传输(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_span API
  • 用户希望降低 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;默认为当前活动 Span
  • active — 默认为 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 字典,则会忽略返回值。

验证

请指导用户进行以下验证:

  1. 检查 Sentry 仪表板:Span 应在完成后不久出现在 Traces 视图中,无需等待整个事务结束
  2. 检查日志:确保没有关于不支持的 Span 操作的 SDK 警告
常见问题
症状原因修复方法
Span 未进行流式传输使用了旧版 sentry_sdk.start_span切换为 sentry_sdk.traces.start_span
start_child 上出现 AttributeErrorstart_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 控制面板中可见

评论

支持 Markdown