生产环境请求追踪#
SGlang 基于 OpenTelemetry Collector 导出请求追踪数据。您可以在启动服务器时通过添加 --enable-trace 参数并使用 --otlp-traces-endpoint 配置 OpenTelemetry Collector 端点来启用追踪。
您可以在 https://github.com/sgl-project/sglang/issues/8965 中找到可视化的示例截图。
设置指南#
本节介绍如何配置请求追踪并导出追踪数据。
安装所需的软件包和工具
安装 Docker 和 Docker Compose
安装依赖项
# 进入 SGLang 根目录 pip install -e "python[tracing]" # 或手动使用 pip 安装依赖项 pip install opentelemetry-sdk opentelemetry-api opentelemetry-exporter-otlp opentelemetry-exporter-otlp-proto-grpc
启动 opentelemetry collector 和 jaeger
docker compose -f examples/monitoring/tracing_compose.yaml up -d
启用追踪功能启动 SGLang 服务器
# 设置环境变量 export SGLANG_OTLP_EXPORTER_SCHEDULE_DELAY_MILLIS=500 export SGLANG_OTLP_EXPORTER_MAX_EXPORT_BATCH_SIZE=64 # 启动 prefill 和 decode 服务器 python -m sglang.launch_server --enable-trace --otlp-traces-endpoint 0.0.0.0:4317 <other option> # 启动 mini lb python -m sglang_router.launch_router --enable-trace --otlp-traces-endpoint 0.0.0.0:4317 <other option>
将
0.0.0.0:4317替换为 opentelemetry collector 的实际端点。如果您使用 tracing_compose.yaml 启动 openTelemetry collector,默认接收端口为 4317。要使用 HTTP/protobuf span 导出器,请设置以下环境变量并指向 HTTP 端点,例如
http://0.0.0.0:4318/v1/traces。export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
发送一些请求
观察是否正在导出追踪数据
使用 Web 浏览器访问 Jaeger 的 16686 端口以可视化请求追踪。
OpenTelemetry Collector 还会将追踪数据以 JSON 格式导出到 /tmp/otel_trace.json。在后续的补丁中,我们将提供将此数据转换为 Perfetto 兼容格式的工具,从而能够在 Perfetto UI 中可视化请求。
如何为您感兴趣的片段添加追踪?#
我们已经在 tokenizer 和 scheduler 主线程中插入了检测点。如果您想追踪额外的请求执行段或执行更细粒度的追踪,请使用以下所描述的 tracing 包中的 API。
初始化
在初始化阶段参与追踪的每个进程应执行:
process_tracing_init(otlp_traces_endpoint, server_name)
otlp_traces_endpoint 从参数中获取,您可以自由设置 server_name,但所有进程中应保持一致。
在初始化阶段参与追踪的每个线程应执行:
trace_set_thread_info("thread label", tp_rank, dp_rank)
"thread label" 可以视为线程的名称,用于在可视化视图中区分不同的线程。
标记请求的开始和结束
trace_req_start(rid, bootstrap_room) trace_req_finish(rid)
这两个 API 必须在同一进程中调用,例如在 tokenizer 中。
为片段添加追踪
正常添加片段追踪:
trace_slice_start("slice A", rid) trace_slice_end("slice A", rid)
使用 "anonymous" 标志在片段开始时不指定片段名称,允许片段名称由 trace_slice_end 确定。
注意:匿名片段不能嵌套。trace_slice_start("", rid, anonymous = True) trace_slice_end("slice A", rid)
在 trace_slice_end 中,使用 auto_next_anon 自动创建下一个匿名片段,这可以减少所需的检测点数量。
trace_slice_start("", rid, anonymous = True) trace_slice_end("slice A", rid, auto_next_anon = True) trace_slice_end("slice B", rid, auto_next_anon = True) trace_slice_end("slice C", rid, auto_next_anon = True) trace_slice_end("slice D", rid)
线程中最后一个片段的结束必须标记为 thread_finish_flag=True;否则线程的 span 不会正确生成。
trace_slice_end("slice D", rid, thread_finish_flag = True)
当请求执行流程转移到另一个线程时,需要显式传播追踪上下文。
发送方:通过 ZMQ 将请求发送到另一个线程之前,执行以下代码
trace_context = trace_get_proc_propagate_context(rid) req.trace_context = trace_context
接收方:通过 ZMQ 接收到请求后,执行以下代码
trace_set_proc_propagate_context(rid, req.trace_context)
当请求执行流程转移到另一个节点(PD 分离)时,需要显式传播追踪上下文。
发送方:通过 http 将请求发送到节点线程之前,执行以下代码
trace_context = trace_get_remote_propagate_context(bootstrap_room_list) headers = {"trace_context": trace_context} session.post(url, headers=headers)
接收方:通过 http 接收到请求后,执行以下代码
trace_set_remote_propagate_context(request.headers['trace_context'])
如何扩展追踪框架以支持复杂的追踪场景#
当前提供的追踪包仍有进一步开发的潜力。如果您想在此基础上构建更高级的功能,首先必须理解其现有的设计原则。
追踪框架实现的核心在于 span 结构和追踪上下文的设计。为了聚合分散的片段并实现对多个请求的并发跟踪,我们设计了两级追踪上下文结构和四级 span 结构:SglangTraceReqContext、SglangTraceThreadContext。它们的关系如下:
SglangTraceReqContext (req_id="req-123")
├── SglangTraceThreadContext(thread_label="scheduler", tp_rank=0)
|
└── SglangTraceThreadContext(thread_label="scheduler", tp_rank=1)
每个被追踪的请求维护一个全局的 SglangTraceReqContext。对于处理请求的每个线程,在 SglangTraceReqContext 中记录并组合一个对应的 SglangTraceThreadContext。在每个线程中,每个当前被追踪的片段(可能是嵌套的)存储在一个列表中。
除了上述层次结构外,每个片段还通过 Span.add_link() 记录其前一个片段,这可用于追踪执行流程。
当请求执行流程转移到新线程时,需要显式传播追踪上下文。在框架中,这由 SglangTracePropagateContext 表示,它包含请求 span 的上下文和前一个 slice span 的上下文。
我们设计了四级 span 结构,包括 bootstrap_room_span、req_root_span、thread_span 和 slice_span。其中,req_root_span 和 thread_span 分别对应 SglangTraceReqContext 和 SglangTraceThreadContext,slice_span 存储在 SglangTraceThreadContext 中。bootstrap_room_span 设计用于适应 PD 分离。在不同节点上,我们可能想为 req_root_span 添加某些属性。但如果 req_root_span 在所有节点间共享,由于 OpenTelemetry 设计的限制,Prefill 和 Decode 节点将不允许添加属性。
bootstrap room span
├── router req root span
| └── router thread span
| └── slice span
├── prefill req root span
| ├── tokenizer thread span
| | └── slice span
| └── scheduler thread span
| └── slice span
└── decode req root span
├── tokenizer thread span
| └── slice span
└── scheduler thread span
└── slice span