OpenTelemetry - 使用任何可观测性工具追踪 LLM
OpenTelemetry 是一种用于可观测性的 CNCF 标准。它可以连接到任何可观测性工具,例如 Jaeger、Zipkin、Datadog、New Relic、Traceloop、Levo AI 等。
LiteLLM Proxy 有一个更新的、可选择开启的 OpenTelemetry v2 集成,它为每个请求生成一个追踪(HTTP → 鉴权 → 防护栏 → LLM 调用 → 数据库写入),遵循官方的 GenAI 语义约定,并内置了针对 Arize、Phoenix、Langfuse、Weave 等的预设。通过 LITELLM_OTEL_V2=true 启用。
从 v1.81.0 开始,请求/响应默认设置为父级 Received Proxy Server Request 跨度(span)的属性——除非您选择开启,否则没有单独的 litellm_request 跨度。要恢复嵌套的 litellm_request 跨度,请设置 USE_OTEL_LITELLM_REQUEST_SPAN=true。参见 跨度层级 获取全貌,并参见 为什么我没看到 litellm_request 跨度? 以了解何时切换该标志。
入门
安装 OpenTelemetry SDK
uv add opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
设置环境变量(不同的提供商可能需要不同的变量)
- 记录到 Traceloop Cloud
- 记录到 OTEL HTTP 收集器
- 记录到 OTEL GRPC 收集器
- 记录到 Laminar
- Splunk Observability Cloud
OTEL_EXPORTER="otlp_http"
OTEL_ENDPOINT="https://api.traceloop.com"
OTEL_HEADERS="Authorization=Bearer%20<your-api-key>"
OTEL_EXPORTER_OTLP_ENDPOINT="http://0.0.0.0:4318"
OTEL_EXPORTER_OTLP_PROTOCOL=http/json
OTEL_EXPORTER_OTLP_HEADERS="api-key=key,other-config-value=value"
OTEL_EXPORTER_OTLP_ENDPOINT="http://0.0.0.0:4318"
OTEL_EXPORTER_OTLP_PROTOCOL=grpc
OTEL_EXPORTER_OTLP_HEADERS="api-key=key,other-config-value=value"
注意:OTLP gRPC 需要
grpcio。通过uv add "litellm[grpc]"(或grpcio)安装。
OTEL_EXPORTER="otlp_grpc"
OTEL_ENDPOINT="https://api.lmnr.ai:8443"
OTEL_HEADERS="authorization=Bearer <project-api-key>"
注意:OTLP gRPC 需要
grpcio。通过uv add "litellm[grpc]"(或grpcio)安装。
OTEL_EXPORTER_OTLP_ENDPOINT="https://ingest.<realm>.observability.splunkcloud.com/v2/trace/otlp"
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_HEADERS="X-SF-Token=<your-ingest-access-token>"
OTEL_SERVICE_NAME="litellm-proxy"
有关 LiteLLM Proxy 设置、摄入令牌模式和追踪验证,请参见 Splunk Observability Cloud (OpenTelemetry)。
仅使用 1 行代码,即可通过 OpenTelemetry 实时记录您所有提供商的 LLM 响应
litellm.callbacks = ["otel"]
跨度层级
LiteLLM Proxy 处理的每个 LLM 请求都会生成一个以 Received Proxy Server Request 为根的跨度树。以下条件跨度仅在设置了其控制标志或启用了相关功能时才会发出。
Received Proxy Server Request (SpanKind.SERVER, root)
│
├── litellm_request (INTERNAL, only when USE_OTEL_LITELLM_REQUEST_SPAN=true)
│ ├── raw_gen_ai_request (INTERNAL — provider request/response, content-capture-gated)
│ └── guardrail (INTERNAL — one per executed guardrail)
│
├── raw_gen_ai_request (INTERNAL — when litellm_request is collapsed into the root)
├── guardrail (INTERNAL — when litellm_request is collapsed into the root)
│
├── auth, router, self, proxy_pre_call, (INTERNAL — service-hook spans, see below)
│ redis, postgres, batch_write_to_db
│
└── Failed Proxy Server Request (INTERNAL — only on exception)
在 semconv 模式(OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental)下,当创建 LLM 调用跨度时,其名称变为 {operation} {model}(例如 chat gpt-4),类型为 SpanKind.CLIENT,并且 raw_gen_ai_request 被抑制。同样的 USE_OTEL_LITELLM_REQUEST_SPAN 门控决定了该跨度是否被发出。参见 选择加入最新的 GenAI 语义约定。
如果不存在父上下文,SDK(无代理)会将 litellm_request 作为根发出——在纯 SDK 使用中没有 Received Proxy Server Request 跨度。
跨度名称参考
| 跨度名称 | 跨度类型 | 父级 | 何时发出 |
|---|---|---|---|
Received Proxy Server Request | SERVER | 根(或存在的外部 traceparent) | 每个向 LiteLLM Proxy 发出的 HTTP 请求发出一次 |
litellm_request | INTERNAL | 代理根(代理)或根(SDK) | 当 USE_OTEL_LITELLM_REQUEST_SPAN=true(代理)或不存在父上下文(SDK)时。在 semconv 模式下被 {operation} {model} 替换 |
raw_gen_ai_request | INTERNAL | 如果存在则为 litellm_request,否则为代理根 | 每个上游提供商调用一次。在 llm.{provider}.* 下携带提供商原生请求/响应。在 semconv 模式下及禁用消息内容捕获时被抑制 |
guardrail | INTERNAL (OpenInference 类型 = guardrail) | 如果存在则为 litellm_request,否则为代理根 | 每次防护栏执行产生一个跨度(调用前、调用中或调用后) |
Failed Proxy Server Request | INTERNAL | 代理根 | 当代理在完成请求前抛出异常时 |
{route} (例如 /user/info, /key/info) | INTERNAL | 代理根 | 管理端点调用(非 LLM 代理路由) |
auth, router, self, proxy_pre_call, redis, postgres, batch_write_to_db, reset_budget_job, pod_lock_manager | INTERNAL | 代理根 | 服务钩子跨度——见下文 |
服务钩子跨度(又名“基础设施”跨度)
LiteLLM 有一个单独的钩子(async_service_success_hook / async_service_failure_hook),用于记录内部子系统(如路由器、鉴权检查、Redis、Postgres 和代理预调用管道)的耗时。当 OTEL 集成处于活动状态且存在父跨度上下文时,这些钩子中的每一个都会创建一个 INTERNAL 子跨度。
跨度名称是 ServiceTypes 枚举值(auth, router, self, proxy_pre_call, redis, postgres, …)。完整集合定义在 litellm/types/services.py 中。self 是 LiteLLM SDK 本身(例如 make_openai_chat_completion_request 的耗时);router 在每个请求中可能出现多次(一次用于 async_get_available_deployment,一次用于包装的 acompletion)。
每个服务钩子跨度携带
| 属性 | 值 |
|---|---|
service | 服务枚举值(例如 "router", "redis") |
call_type | 特定操作(例如 "async_get_available_deployment", "acompletion", "add_litellm_data_to_request") |
error | 仅在失败的跨度上设置 |
| (自定义 event_metadata) | 调用者附加的任何内容 |
这些跨度是操作/基础设施跨度,而不是 GenAI 语义跨度。它们对于 SRE 级别的调试(LiteLLM 内部耗时在哪里?)非常有用,但它们不携带 gen_ai.* 属性。如果您后端只需要 AI 语义跨度,请根据 gen_ai.system 的存在(或跨度名称)进行过滤。
目前没有可以禁用单个服务钩子跨度的环境变量。如果您需要过滤它们,请在 OTLP 收集器/后端层进行(例如通过按 name 删除的尾部采样)。
为什么我没看到 litellm_request 跨度?
行为在 v1.81.0 中发生了变化。默认情况下,USE_OTEL_LITELLM_REQUEST_SPAN=false,代理会将 litellm_request 跨度折叠到父级 Received Proxy Server Request 跨度中——其 gen_ai.* 属性被设置在父级上。这
- 避免了父子跨度属性重复。
- 减少了跨度数量(和存储成本),每个请求约减少 1 个跨度。
- 在已存在父上下文时保持追踪的浅层结构。
要恢复旧的嵌套行为(每个 LLM 调用作为代理根跨度的子跨度拥有自己的 litellm_request 跨度),请设置
USE_OTEL_LITELLM_REQUEST_SPAN=true
如果以下情况,这是正确的设置
- 一个 HTTP 请求进行了多次
litellm.completion调用——在默认行为下,最后一次调用的属性会覆盖共享父级上的早期属性。 - 您想要一个干净的
raw_gen_ai_request和guardrail子跨度父级,而不是 HTTP 请求跨度。 - 您的后端 UI 是围绕 AI 语义跨度名称(如
litellm_request)构建的。
这不是回归;这种变化是有意的。该标志在每次请求时重新读取,因此无需重启即可切换。
在 semconv 模式(OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental)下,同样的 USE_OTEL_LITELLM_REQUEST_SPAN 门控仍然决定是否发出 LLM 调用跨度;semconv 模式仅更改跨度名称(为 {operation} {model})、类型(为 CLIENT)和子结构。
上下文传播 (W3C traceparent)
LiteLLM 遵循 W3C Trace Context 标头。如果您的客户端(或上游网关)发送了 traceparent 标头,LiteLLM 会将 Received Proxy Server Request 跨度作为该外部追踪的子跨度创建,因此 LiteLLM 的跨度会出现在应用程序已有的分布式追踪内。
父上下文解析顺序(优先级最高在前)
- 请求
metadata中明确的litellm_parent_otel_span。 - 入站
traceparentHTTP 标头(通过TraceContextTextMapPropagator提取)。 - OTEL 全局上下文(线程本地)中的当前活跃跨度。
- 无——LiteLLM 的跨度是根。
要强制每个 LiteLLM 追踪成为其自己的根,无论入站标头或活跃上下文如何,请设置 OTEL_IGNORE_CONTEXT_PROPAGATION=true。
运行多个 OpenTelemetry 处理程序
您可以在同一进程中运行多个 OpenTelemetry 处理程序,例如一个通用的 OTLP 导出器和一个特定于后端的子类。在第一个处理程序之后的每个处理程序上设置 skip_set_global=True,以便每个处理程序获得自己的私有 TracerProvider、MeterProvider 和 LoggerProvider。跨度、指标和日志事件随后仅通过该处理程序的导出器流出。
import litellm
from litellm.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig
# Primary handler. Claims the global TracerProvider.
primary = OpenTelemetry(config=OpenTelemetryConfig(
exporter="otlp_http",
endpoint="https://your-collector/v1/traces",
))
# Secondary handler. Has its own private providers.
secondary = OpenTelemetry(config=OpenTelemetryConfig(
exporter="otlp_http",
endpoint="https://second-collector/v1/traces",
skip_set_global=True,
))
litellm.callbacks = [primary, secondary]
初始化顺序无关紧要。无论首先构建哪一个,两个处理程序都会收到它们自己的跨度。
跨收集器行为(例如 LangSmith + 通用 OTEL)
当两个发出 OTEL 的集成同时处于活动状态时——例如定制的 LangSmith OTEL 处理程序加上通用 otel 导出器——两者都遵循相同的 traceparent 传播规则和 上下文传播 中描述的相同父解析顺序。只要一个处理程序使用了 skip_set_global=True,两者都将
- 为给定的请求看到相同的
trace_id。 - 发出相同的跨度层级(
Received Proxy Server Request→litellm_request(若启用) →raw_gen_ai_request/guardrail)。 - 仅在它们将跨度运送到的导出器上有所不同。
如果配置了定制的 LangSmith OTEL 处理程序仅在请求带有 traceparent 时挂载 litellm_request(否则无操作),通用 OTEL 处理程序仍然会发出其完整的层级。这两个视图可以独立读取,因为跨度名称和属性是相同的。
捕获消息内容
LiteLLM 使用标准的 OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT 环境变量来控制是否捕获提示词和补全内容,以及捕获位置
# Do not capture message content
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=NO_CONTENT
# Capture content on span attributes only
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=SPAN_ONLY
# Capture content on event attributes only
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=EVENT_ONLY
# Capture content on both spans and events
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=SPAN_AND_EVENT
也接受布尔形式:true 映射到 EVENT_ONLY,false 映射到 NO_CONTENT。
每个处理程序的内容策略
当运行多个 OpenTelemetry 处理程序时,在每个 OpenTelemetryConfig 上设置 capture_message_content,以便处理程序可以具有不同的内容策略。例如,将完整提示词发送到调试后端,同时从以合规为中心的 OTLP 收集器中剥离内容
import litellm
from litellm.integrations.opentelemetry import OpenTelemetry, OpenTelemetryConfig
stripped = OpenTelemetry(config=OpenTelemetryConfig(
exporter="otlp_http",
endpoint="https://compliance-collector/v1/traces",
capture_message_content="NO_CONTENT",
))
verbose = OpenTelemetry(config=OpenTelemetryConfig(
exporter="otlp_http",
endpoint="https://debug-collector/v1/traces",
capture_message_content="SPAN_AND_EVENT",
skip_set_global=True,
))
litellm.callbacks = [stripped, verbose]
解析顺序(优先级最高在前)
litellm.turn_off_message_logging=True强制NO_CONTENT(动态关断开关;覆盖以下所有内容)。OpenTelemetryConfig.capture_message_content(每个处理程序的字段,在处理程序初始化时采样)。OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT环境变量(在处理程序初始化时采样)。- 遗留的每个实例
message_logging标志——默认值为True,映射到SPAN_AND_EVENT。
选择加入最新的 GenAI 语义约定
设置 OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental 以发出遵循 最新的 OpenTelemetry GenAI 语义约定 的跨度
OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental
这会更改 LLM 调用跨度的名称、类型和结构,抑制非标准的 raw_gen_ai_request 子跨度,添加 gen_ai.provider.name 属性以及 gen_ai.system,在存在时填充额外的请求和缓存令牌属性,并将每条消息的事件合并为单个 gen_ai.client.inference.operation.details 事件。参见下面的 跨度参考 和 属性参考 以了解各行的差异。
OpenTelemetryConfig.semconv_stability 是编程等价物。根据 OTEL 规范,该标志是逗号可分隔的。
从 OpenTelemetry 日志记录中编辑消息、响应内容
从所有 OpenTelemetry 日志记录中编辑消息和响应
设置 litellm.turn_off_message_logging=True。这将阻止消息和响应被记录到 OpenTelemetry,但请求元数据仍将被记录。
从特定 OpenTelemetry 日志记录中编辑消息和响应
在通常为文本补全或嵌入调用传递的元数据中,您可以设置特定键来屏蔽此调用的消息和响应。
将 mask_input 设置为 True 将屏蔽此调用的输入记录
将 mask_output 设置为 True 将屏蔽此调用的输出记录。
请注意,如果您正在继续现有的追踪,并且您设置 update_trace_keys 以包含 input 或 output,同时您设置了相应的 mask_input 或 mask_output,那么该追踪现有的输入和/或输出将被替换为已编辑的消息。
故障排除
我没看到 litellm_request 跨度
v1.81.0+ 默认值(USE_OTEL_LITELLM_REQUEST_SPAN=false)下的预期行为:代理根跨度吸收 LLM 调用属性,没有单独的 litellm_request 跨度。要恢复嵌套跨度,请设置 USE_OTEL_LITELLM_REQUEST_SPAN=true。参见 为什么我没看到 litellm_request 跨度?。
如果您处于 semconv 模式,LLM 调用跨度存在但被重命名为 {operation} {model}(例如 chat gpt-4)——请按 gen_ai.system 搜索,而不是按字面名称 litellm_request。
我只看到基础设施跨度(router, auth, redis, proxy_pre_call)
这些是 服务钩子跨度。它们与 AI 语义跨度(raw_gen_ai_request, guardrail, 以及启用的 litellm_request)一起发出,而不是替代它们。如果您在追踪中确实没看到任何 gen_ai.* 属性
- 验证
litellm.callbacks(或litellm_settings.callbacks)是否包含"otel"。 - 验证请求是否确实命中了
/chat/completions(或其他 LLM)路由——管理端点(/key/info,/user/info, …)将不会有gen_ai.*属性。 - 检查是否设置了
litellm.turn_off_message_logging=true和/或mask_input/mask_output——它们会抑制消息和原始提供商属性。 - 设置
USE_OTEL_LITELLM_REQUEST_SPAN=true,以便 LLM 属性落在名为litellm_request的跨度上,而不是与Received Proxy Server Request上的 HTTP 请求属性混杂在一起。
追踪失败请求上的 LiteLLM Proxy 用户/密钥/组织/团队信息
LiteLLM 在成功和失败的请求上都会发出 metadata.user_api_key_* 属性(密钥哈希、密钥别名、组织 ID、用户 ID、团队 ID)。它们出现在 litellm_request 跨度(若存在)上,否则出现在 Received Proxy Server Request 上。
未看到追踪到达集成端
如果您没看到追踪到达您的集成端,在 LiteLLM 环境中设置 OTEL_DEBUG="True" 并重试。
export OTEL_DEBUG="True"
这将向控制台发出任何日志记录问题。常见原因
OTEL_EXPORTER_OTLP_ENDPOINT指向 HTTPS 端点但协议是grpc(反之亦然)。OTEL_HEADERS缺少您的后端期望的认证标头。- 防火墙/Sidecar 正在丢弃 4317/4318 端口上的出站 OTLP 流量。
- 对于 gRPC,未安装
grpcio(uv add "litellm[grpc]")。
跨度被截断或丢弃
OTLP 导出器会批量处理跨度。非常大的 gen_ai.input.messages/gen_ai.output.messages(例如多兆字节的提示词)可能会超过收集器处的默认 OTLP 属性大小限制。要么
- 将大数据载荷移出追踪(设置
litellm.turn_off_message_logging=true并依赖 Spend Logs / 冷存储,通过metadata.cold_storage_object_key引用)。 - 提高收集器的
max_attribute_value_length和 OTLP 接收器的max_recv_msg_size_mib。
配置参考
除非另有说明,以下所有标志均从环境变量读取。布尔标志接受 true/false(不区分大小写)。
导出器与资源
| 变量 | 默认 | 目的 |
|---|---|---|
OTEL_EXPORTER (别名: OTEL_EXPORTER_OTLP_PROTOCOL) | console | 导出器类型。常见值:console, otlp_http, otlp_grpc, http/json, http/protobuf, grpc |
OTEL_ENDPOINT (别名: OTEL_EXPORTER_OTLP_ENDPOINT) | none | OTLP 端点 URL |
OTEL_HEADERS (别名: OTEL_EXPORTER_OTLP_HEADERS) | none | 逗号分隔的 key=value,key2=value2 标头列表 |
OTEL_SERVICE_NAME | litellm | 资源属性 service.name |
OTEL_ENVIRONMENT_NAME | production | 资源属性 deployment.environment |
OTEL_MODEL_ID | OTEL_SERVICE_NAME | 资源属性 model_id |
OTEL_TRACER_NAME | litellm | 追踪器名称 |
LITELLM_METER_NAME | litellm | 计量器名称(启用指标时) |
LITELLM_LOGGER_NAME | litellm | 记录器名称(启用事件时) |
OTEL_LOGS_EXPORTER | none | 启用事件时使用的日志导出器(例如 console) |
跨度 / 指标 / 事件切换
| 变量 | 默认 | 效果 |
|---|---|---|
USE_OTEL_LITELLM_REQUEST_SPAN | false | 强制 litellm_request 始终作为代理根跨度的子跨度发出。参见 为什么我没看到 litellm_request 跨度? |
OTEL_SEMCONV_STABILITY_OPT_IN | 未设置 | 设置为 gen_ai_latest_experimental 以切换到 最新的 GenAI 语义约定。根据 OTEL 规范,逗号可分隔 |
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT | 未设置 → 回退到旧的 message_logging(默认 True → SPAN_AND_EVENT) | NO_CONTENT / SPAN_ONLY / EVENT_ONLY / SPAN_AND_EVENT。接受布尔形式(true→EVENT_ONLY, false→NO_CONTENT) |
LITELLM_OTEL_INTEGRATION_ENABLE_METRICS | false | 启用 OTLP 指标(TTFT、TPOT、响应时长、成本、令牌使用量、操作时长) |
LITELLM_OTEL_INTEGRATION_ENABLE_EVENTS | false | 启用 OTLP 语义日志(gen_ai.content.prompt/gen_ai.content.completion,或 semconv 模式下的 gen_ai.client.inference.operation.details) |
OTEL_IGNORE_CONTEXT_PROPAGATION | false | 如果为 true,忽略入站 traceparent 标头和任何活跃跨度——每个 LiteLLM 追踪成为其自己的根 |
OTEL_DEBUG / DEBUG_OTEL | false | 将导出器和跨度创建诊断信息打印到 stderr |
litellm.turn_off_message_logging (Python 全局 / litellm_settings.turn_off_message_logging) | false | 内容捕获的关断开关。抑制 llm.{provider}.* 原始请求/响应、gen_ai.input.messages、gen_ai.output.messages 和 gen_ai.content.* 日志事件。覆盖每个处理程序的 capture_message_content |
每个请求的编辑(请求 metadata)
您可以在 metadata 中传递的每个请求的键,以在不全局禁用日志记录的情况下编辑单个调用。
| 键 | 效果 |
|---|---|
mask_input | 当 true 时,编辑此请求上的输入消息 |
mask_output | 当 true 时,编辑此请求上的输出消息 |
update_trace_keys | 控制在继续现有追踪时替换哪些追踪键(input, output) |
generation_name | 使用此值覆盖 raw_gen_ai_request 跨度的名称 |
OpenTelemetryConfig 编程等价物
| 字段 | 默认 | 目的 |
|---|---|---|
exporter | console | 同 OTEL_EXPORTER |
endpoint | none | 同 OTEL_ENDPOINT |
headers | none | 同 OTEL_HEADERS |
enable_metrics | false | 同 LITELLM_OTEL_INTEGRATION_ENABLE_METRICS |
enable_events | false | 同 LITELLM_OTEL_INTEGRATION_ENABLE_EVENTS |
capture_message_content | 环境变量 | 每个处理程序的覆盖;与 OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT 相同的值空间 |
semconv_stability | 环境变量 | 同 OTEL_SEMCONV_STABILITY_OPT_IN |
skip_set_global | false | 不要声明进程全局 TracerProvider/MeterProvider/LoggerProvider |
ignore_context_propagation | false | 同 OTEL_IGNORE_CONTEXT_PROPAGATION |
附录:跨度、指标和属性参考
此附录枚举了 LiteLLM 发出的每个跨度、指标和 AI 语义属性,包括当启用了 semconv 模式 时每个如何更改。
跨度参考
LLM 调用跨度是 AI 语义核心。它的名称、类型和支持子跨度取决于 semconv 模式是否处于活动状态。
| 跨度 | 类型 | 默认模式 | Semconv 模式 |
|---|---|---|---|
| 代理请求框架 | SERVER | Received Proxy Server Request | Received Proxy Server Request (未更改) |
| LLM 调用跨度 | INTERNAL (默认) / CLIENT (semconv) | litellm_request (仅当 USE_OTEL_LITELLM_REQUEST_SPAN=true 时;否则属性落在代理框架跨度上) | {operation} {model} (例如 chat gpt-4, embeddings text-embedding-3-small);与默认模式相同的 USE_OTEL_LITELLM_REQUEST_SPAN 门控 |
| 原始提供商载荷 | INTERNAL | raw_gen_ai_request (允许捕获消息内容时) | 未发出(数据位于 LLM 调用跨度和合并事件上) |
| 防护栏检查 | INTERNAL | 每次防护栏调用一个跨度,按防护栏命名 | 未更改 |
| 管理端点 | INTERNAL | 每个代理管理员调用一个跨度,按端点命名 | 未更改 |
semconv 模式下发出的操作名称:chat (默认), embeddings (当调用类型包含 embedding), text_completion (当调用类型包含 text_completion)。
事件参考
当配置上 enable_events=True 时,事件落在 LiteLLM 管理的 LoggerProvider 上。
| 事件 | 默认模式 | Semconv 模式 |
|---|---|---|
| 每消息提示词 | gen_ai.content.prompt (每输入消息一个事件) | 被合并事件替换 |
| 每选择补全 | gen_ai.content.completion (每个选择一个事件) | 被合并事件替换 |
| 合并推理详细信息 | 未发出 | gen_ai.client.inference.operation.details (每次调用一个事件,按规范携带 gen_ai.input.messages 和 gen_ai.output.messages 数组) |
指标参考
当在 OpenTelemetryConfig 上设置 enable_metrics=True 时,LiteLLM 发出以下直方图。指标名称符合 OTEL GenAI 语义约定。
| 指标 | 单位 | 描述 |
|---|---|---|
gen_ai.client.operation.duration | s | 包含 LiteLLM 开销的端到端操作持续时间。 |
gen_ai.client.token.usage | {token} | 令牌使用量。每次调用记录两个直方图(标签 gen_ai.token.type 为 "input" 或 "output")。 |
gen_ai.client.token.cost | USD | 计算出的请求成本。 |
gen_ai.client.response.time_to_first_token | s | 从请求开始到第一个流式传输令牌的时间(仅限流式传输请求)。 |
gen_ai.client.response.time_per_output_token | s | 平均每个输出令牌的时间(生成时间 / 补全令牌)。 |
gen_ai.client.response.duration | s | LLM API 生成时间,不包含 LiteLLM 开销。 |
每个直方图上的通用标签:gen_ai.operation.name, gen_ai.system, gen_ai.request.model, gen_ai.framework="litellm"。
| 通用指标请求 | 指标 |
|---|---|
| TTFT | gen_ai.client.response.time_to_first_token |
| TPS | 派生为 1 / gen_ai.client.response.time_per_output_token |
| 令牌使用量 | gen_ai.client.token.usage (按 gen_ai.token.type 分割) |
| 厂商/模型延迟(不包含开销) | gen_ai.client.response.duration |
| 厂商/模型延迟(包含开销) | gen_ai.client.operation.duration |
跨度 → 派生指标
即使关闭了指标,以下每个指标也可以从跨度中派生。大多数仪表板都是这样做的。
| 指标 | 如何从跨度派生 |
|---|---|
| TTFT (首字耗时) | 仅流式传输请求。使用专用的 gen_ai.client.response.time_to_first_token 指标,或通过自定义回调从请求 kwargs 捕获 completion_start_time。 |
| TPOT (单字生成耗时) | 使用 gen_ai.client.response.time_per_output_token 指标,或派生为 gen_ai.client.response.duration ÷ gen_ai.usage.output_tokens。 |
| 总响应持续时间 | gen_ai.client.response.duration 指标,或 LLM 调用跨度(或代理根跨度减去 LiteLLM 开销——参见 hidden_params.litellm_overhead_time_ms)的 end_time − start_time。 |
| 厂商(提供商)延迟 | raw_gen_ai_request 跨度的持续时间(默认模式)——纯粹等待上游提供商的时间。在 semconv 模式下,使用 gen_ai.client.response.duration。 |
| LiteLLM 开销 | 代理根跨度上的 hidden_params.litellm_overhead_time_ms。或 Received Proxy Server Request.duration − raw_gen_ai_request.duration。 |
| 令牌使用量 | LLM 跨度上的 gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.usage.total_tokens(或 gen_ai.client.token.usage 指标)。 |
| 成本 | LLM 跨度上的 gen_ai.cost.total_cost(以及 gen_ai.cost.* 的其余部分);或 gen_ai.client.token.cost 指标。 |
| 防护栏评估时间 | 每个 guardrail 跨度的持续时间。通过 guardrail_name 和 guardrail_mode 区分。 |
| 路由器 / 鉴权 / Redis / 数据库延迟 | 相应的 服务钩子跨度(router, auth, redis, postgres, …)的持续时间。 |
| 重试 / 回退次数 | 代理根跨度上的 hidden_params.x-litellm-attempted-retries 和 hidden_params.x-litellm-attempted-fallbacks。 |
| 流式传输? | llm.is_streaming 属性("True"/"False")。 |
属性参考
在 LLM 调用跨度上设置的属性。名称遵循 OTEL GenAI semconv。
| 属性 | 默认模式 | Semconv 模式 |
|---|---|---|
gen_ai.operation.name | litellm call_type (例如 acompletion) | semconv 操作(chat, embeddings, text_completion) |
gen_ai.system | 提供商名称(例如 openai) | 未更改 |
gen_ai.provider.name | 未设置 | 提供商名称(规范中重命名的必填属性) |
gen_ai.framework | litellm | litellm |
gen_ai.request.model | model | model |
gen_ai.request.max_tokens, temperature, top_p | 当在请求中设置时 | 当在请求中设置时 |
gen_ai.request.frequency_penalty, presence_penalty, top_k, seed, stop_sequences, stream, choice.count | 未设置 | 当在请求中设置时 |
gen_ai.response.model, gen_ai.response.id, gen_ai.response.finish_reasons | 当存在于响应中时 | 未更改 |
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, gen_ai.usage.total_tokens | 当存在时 | 未更改 |
gen_ai.usage.cache_creation.input_tokens, gen_ai.usage.cache_read.input_tokens | 未设置 | 当存在于响应中时 |
gen_ai.input.messages, gen_ai.output.messages, gen_ai.system_instructions | 当消息内容捕获允许时,JSON 编码的 {role, parts: [...]} 对象数组 | 未更改 |
gen_ai.cost.input_cost, output_cost, total_cost (以及相关成本分解属性) | LiteLLM 特定的成本属性 | 未更改 |
gen_ai.cost.* (成本分解,所有模式)
LiteLLM 将 standard_logging_payload["cost_breakdown"] 中的每个键展开为 gen_ai.cost.{key}。目前观察到的键
| 属性 | 含义 |
|---|---|
gen_ai.cost.input_cost | 提示词令牌成本 (USD) |
gen_ai.cost.output_cost | 补全令牌成本 (USD) |
gen_ai.cost.total_cost | 收费总额 (USD) |
gen_ai.cost.tool_usage_cost | 可归因于工具/函数调用的成本 |
gen_ai.cost.original_cost | 折扣前成本 |
gen_ai.cost.discount_percent, gen_ai.cost.discount_amount | 应用的折扣 |
gen_ai.cost.margin_percent, gen_ai.cost.margin_fixed_amount, gen_ai.cost.margin_total_amount | 利润率组件 |
litellm.* (代理根和 LLM 跨度)
| 属性 | 值 |
|---|---|
litellm.call_id | 每次 litellm.completion 调用唯一。使用此将追踪数据与 LiteLLM Spend Logs 和 LiteLLM UI 关联 |
litellm.request.type | 与 call_type 相同(例如 acompletion, aembedding, aimage_generation) |
llm.* (代理根和 LLM 跨度)
| 属性 | 值 |
|---|---|
llm.request.type | LiteLLM call_type |
llm.is_streaming | "True" 或 "False" |
llm.user | user 参数(若设置) |
llm.{provider}.* (原始提供商请求/响应,仅限默认模式)
仅在 raw_gen_ai_request 上设置,以避免属性重复。对于原始提供商请求主体中的每个键,LiteLLM 发出 llm.{provider}.{key}。对于原始响应主体也是如此。
openai 观察到的示例
llm.openai.messages
llm.openai.model
llm.openai.temperature
llm.openai.max_tokens
llm.openai.id
llm.openai.object
llm.openai.created
llm.openai.choices
llm.openai.usage
llm.openai.system_fingerprint
llm.openai.service_tier
llm.openai.extra_body
对于 Anthropic,将 openai 替换为 anthropic (llm.anthropic.messages, llm.anthropic.stop_reason 等)。对于其他每个提供商,模式相同。
当 OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=NO_CONTENT、litellm.turn_off_message_logging=true 时,或在 semconv 模式下(此时整个 raw_gen_ai_request 跨度被抑制),这些属性会被抑制。
metadata.* (代理根,有时是 LLM 跨度)
LiteLLM 遍历 standard_logging_payload["metadata"] 并将每个条目发出为 metadata.{key}。常用键(不详尽)
| 属性 | 含义 |
|---|---|
metadata.user_api_key_hash | 使用的虚拟密钥的 SHA 哈希 |
metadata.user_api_key_alias | 虚拟密钥别名 |
metadata.user_api_key_team_id, metadata.user_api_key_team_alias | 团队标识符 |
metadata.user_api_key_org_id, metadata.user_api_key_org_alias | 组织标识符 |
metadata.user_api_key_user_id, metadata.user_api_key_user_email | LiteLLM 内部用户标识符 |
metadata.user_api_key_end_user_id | 请求中传递的终端用户 |
metadata.user_api_key_project_id, metadata.user_api_key_project_alias | 项目标识符 |
metadata.user_api_key_spend, metadata.user_api_key_max_budget, metadata.user_api_key_budget_reset_at | 预算状态 |
metadata.user_api_key_request_route | 命中的路由(例如 /v1/chat/completions) |
metadata.requester_ip_address, metadata.user_agent | 客户端标识符 |
metadata.requester_metadata, metadata.requester_custom_headers | 标头和请求上下文 |
metadata.applied_guardrails | 此请求上运行的防护栏列表 |
metadata.mcp_tool_call_metadata, metadata.vector_store_request_metadata | MCP 和向量存储请求信息 |
metadata.usage_object | 完整令牌使用对象 |
metadata.spend_logs_metadata | 持久化到 Spend Logs 的自定义元数据 |
metadata.cold_storage_object_key | 当请求载荷卸载到冷存储时 |
metadata.user_api_key_auth_metadata | 额外鉴权上下文 |
外加 hidden_params——一个携带 JSON 序列化字典的单个属性,包括 litellm_overhead_time_ms, api_base, response_cost, additional_headers, model_id, x-litellm-attempted-retries, x-litellm-attempted-fallbacks 等。
防护栏跨度属性
在每个 guardrail 子跨度上设置
| 属性 | 值 |
|---|---|
openinference.span.kind | "guardrail" (按 OpenInference 约定) |
guardrail_name | 例如 "presidio-pii", "lakera", "aporia" |
guardrail_mode | "pre_call", "during_call", "post_call" 等 |
masked_entity_count | 如果防护栏屏蔽了实体 |
guardrail_response | 防护栏的响应/动作 |
跨度的 start_time/end_time 来自防护栏自己的计时,因此跨度持续时间等于防护栏评估时间。
目前没有单独的 guardrail_pre/guardrail_post 跨度名称——两者都作为 guardrail 发出,并通过 guardrail_mode 属性区分。
服务钩子跨度属性
参见 服务钩子跨度。每个携带 service, call_type, 可选的 error,以及调用者附加的任何自定义事件元数据。
异常属性
在 Failed Proxy Server Request 上(以及失败时的任何 LLM 调用跨度上)
| 属性 | 值 |
|---|---|
exception | str(original_exception) |
| 跨度状态 | StatusCode.ERROR |
资源属性(每个跨度)
| 属性 | 默认 | 覆盖 |
|---|---|---|
service.name | litellm | OTEL_SERVICE_NAME |
deployment.environment | production | OTEL_ENVIRONMENT_NAME |
model_id | 匹配 service.name | OTEL_MODEL_ID |
telemetry.sdk.{language,name,version} | 由 SDK 设置 | — |
稳定性
上述跨度名称、指标名称和属性集在 LiteLLM 补丁版本中保持稳定。LLM 调用跨度名称和类型在 默认模式和 Semconv 模式 之间更改,并通过记录的 opt-in 标志进行迁移,而不是在版本之间迁移。
支持
对于 LiteLLM OTEL 集成问题,请在 BerriAI/litellm 上提交 issue。对于 OpenLLMetry / Traceloop 语义约定问题,请参见 Slack 或发送电子邮件至 dev@traceloop.com。