跳至主要内容

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 中的变更

从 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

设置环境变量(不同的提供商可能需要不同的变量)

OTEL_EXPORTER="otlp_http"
OTEL_ENDPOINT="https://api.traceloop.com"
OTEL_HEADERS="Authorization=Bearer%20<your-api-key>"

仅使用 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 RequestSERVER根(或存在的外部 traceparent每个向 LiteLLM Proxy 发出的 HTTP 请求发出一次
litellm_requestINTERNAL代理根(代理)或根(SDK)USE_OTEL_LITELLM_REQUEST_SPAN=true(代理)或不存在父上下文(SDK)时。在 semconv 模式下被 {operation} {model} 替换
raw_gen_ai_requestINTERNAL如果存在则为 litellm_request,否则为代理根每个上游提供商调用一次。在 llm.{provider}.* 下携带提供商原生请求/响应。在 semconv 模式下及禁用消息内容捕获时被抑制
guardrailINTERNAL (OpenInference 类型 = guardrail)如果存在则为 litellm_request,否则为代理根每次防护栏执行产生一个跨度(调用前、调用中或调用后)
Failed Proxy Server RequestINTERNAL代理根当代理在完成请求前抛出异常时
{route} (例如 /user/info, /key/info)INTERNAL代理根管理端点调用(非 LLM 代理路由)
auth, router, self, proxy_pre_call, redis, postgres, batch_write_to_db, reset_budget_job, pod_lock_managerINTERNAL代理根服务钩子跨度——见下文

服务钩子跨度(又名“基础设施”跨度)

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_requestguardrail 子跨度父级,而不是 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 的跨度会出现在应用程序已有的分布式追踪内。

父上下文解析顺序(优先级最高在前)

  1. 请求 metadata 中明确的 litellm_parent_otel_span
  2. 入站 traceparent HTTP 标头(通过 TraceContextTextMapPropagator 提取)。
  3. OTEL 全局上下文(线程本地)中的当前活跃跨度。
  4. 无——LiteLLM 的跨度是根。

要强制每个 LiteLLM 追踪成为其自己的根,无论入站标头或活跃上下文如何,请设置 OTEL_IGNORE_CONTEXT_PROPAGATION=true

运行多个 OpenTelemetry 处理程序

您可以在同一进程中运行多个 OpenTelemetry 处理程序,例如一个通用的 OTLP 导出器和一个特定于后端的子类。在第一个处理程序之后的每个处理程序上设置 skip_set_global=True,以便每个处理程序获得自己的私有 TracerProviderMeterProviderLoggerProvider。跨度、指标和日志事件随后仅通过该处理程序的导出器流出。

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 Requestlitellm_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_ONLYfalse 映射到 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]

解析顺序(优先级最高在前)

  1. litellm.turn_off_message_logging=True 强制 NO_CONTENT(动态关断开关;覆盖以下所有内容)。
  2. OpenTelemetryConfig.capture_message_content(每个处理程序的字段,在处理程序初始化时采样)。
  3. OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT 环境变量(在处理程序初始化时采样)。
  4. 遗留的每个实例 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 以包含 inputoutput,同时您设置了相应的 mask_inputmask_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.* 属性

  1. 验证 litellm.callbacks(或 litellm_settings.callbacks)是否包含 "otel"
  2. 验证请求是否确实命中了 /chat/completions(或其他 LLM)路由——管理端点(/key/info, /user/info, …)将不会有 gen_ai.* 属性。
  3. 检查是否设置了 litellm.turn_off_message_logging=true 和/或 mask_input/mask_output——它们会抑制消息和原始提供商属性。
  4. 设置 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)noneOTLP 端点 URL
OTEL_HEADERS (别名: OTEL_EXPORTER_OTLP_HEADERS)none逗号分隔的 key=value,key2=value2 标头列表
OTEL_SERVICE_NAMElitellm资源属性 service.name
OTEL_ENVIRONMENT_NAMEproduction资源属性 deployment.environment
OTEL_MODEL_IDOTEL_SERVICE_NAME资源属性 model_id
OTEL_TRACER_NAMElitellm追踪器名称
LITELLM_METER_NAMElitellm计量器名称(启用指标时)
LITELLM_LOGGER_NAMElitellm记录器名称(启用事件时)
OTEL_LOGS_EXPORTERnone启用事件时使用的日志导出器(例如 console

跨度 / 指标 / 事件切换

变量默认效果
USE_OTEL_LITELLM_REQUEST_SPANfalse强制 litellm_request 始终作为代理根跨度的子跨度发出。参见 为什么我没看到 litellm_request 跨度?
OTEL_SEMCONV_STABILITY_OPT_IN未设置设置为 gen_ai_latest_experimental 以切换到 最新的 GenAI 语义约定。根据 OTEL 规范,逗号可分隔
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT未设置 → 回退到旧的 message_logging(默认 TrueSPAN_AND_EVENTNO_CONTENT / SPAN_ONLY / EVENT_ONLY / SPAN_AND_EVENT。接受布尔形式(trueEVENT_ONLY, falseNO_CONTENT
LITELLM_OTEL_INTEGRATION_ENABLE_METRICSfalse启用 OTLP 指标(TTFT、TPOT、响应时长、成本、令牌使用量、操作时长)
LITELLM_OTEL_INTEGRATION_ENABLE_EVENTSfalse启用 OTLP 语义日志(gen_ai.content.prompt/gen_ai.content.completion,或 semconv 模式下的 gen_ai.client.inference.operation.details
OTEL_IGNORE_CONTEXT_PROPAGATIONfalse如果为 true,忽略入站 traceparent 标头和任何活跃跨度——每个 LiteLLM 追踪成为其自己的根
OTEL_DEBUG / DEBUG_OTELfalse将导出器和跨度创建诊断信息打印到 stderr
litellm.turn_off_message_logging (Python 全局 / litellm_settings.turn_off_message_logging)false内容捕获的关断开关。抑制 llm.{provider}.* 原始请求/响应、gen_ai.input.messagesgen_ai.output.messagesgen_ai.content.* 日志事件。覆盖每个处理程序的 capture_message_content

每个请求的编辑(请求 metadata

您可以在 metadata 中传递的每个请求的键,以在不全局禁用日志记录的情况下编辑单个调用。

效果
mask_inputtrue 时,编辑此请求上的输入消息
mask_outputtrue 时,编辑此请求上的输出消息
update_trace_keys控制在继续现有追踪时替换哪些追踪键(input, output
generation_name使用此值覆盖 raw_gen_ai_request 跨度的名称

OpenTelemetryConfig 编程等价物

字段默认目的
exporterconsoleOTEL_EXPORTER
endpointnoneOTEL_ENDPOINT
headersnoneOTEL_HEADERS
enable_metricsfalseLITELLM_OTEL_INTEGRATION_ENABLE_METRICS
enable_eventsfalseLITELLM_OTEL_INTEGRATION_ENABLE_EVENTS
capture_message_content环境变量每个处理程序的覆盖;与 OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT 相同的值空间
semconv_stability环境变量OTEL_SEMCONV_STABILITY_OPT_IN
skip_set_globalfalse不要声明进程全局 TracerProvider/MeterProvider/LoggerProvider
ignore_context_propagationfalseOTEL_IGNORE_CONTEXT_PROPAGATION

附录:跨度、指标和属性参考

此附录枚举了 LiteLLM 发出的每个跨度、指标和 AI 语义属性,包括当启用了 semconv 模式 时每个如何更改。

跨度参考

LLM 调用跨度是 AI 语义核心。它的名称、类型和支持子跨度取决于 semconv 模式是否处于活动状态。

跨度类型默认模式Semconv 模式
代理请求框架SERVERReceived Proxy Server RequestReceived 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 门控
原始提供商载荷INTERNALraw_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.messagesgen_ai.output.messages 数组)

指标参考

当在 OpenTelemetryConfig 上设置 enable_metrics=True 时,LiteLLM 发出以下直方图。指标名称符合 OTEL GenAI 语义约定。

指标单位描述
gen_ai.client.operation.durations包含 LiteLLM 开销的端到端操作持续时间。
gen_ai.client.token.usage{token}令牌使用量。每次调用记录两个直方图(标签 gen_ai.token.type"input""output")。
gen_ai.client.token.costUSD计算出的请求成本。
gen_ai.client.response.time_to_first_tokens从请求开始到第一个流式传输令牌的时间(仅限流式传输请求)。
gen_ai.client.response.time_per_output_tokens平均每个输出令牌的时间(生成时间 / 补全令牌)。
gen_ai.client.response.durationsLLM API 生成时间,不包含 LiteLLM 开销。

每个直方图上的通用标签:gen_ai.operation.name, gen_ai.system, gen_ai.request.model, gen_ai.framework="litellm"

通用指标请求指标
TTFTgen_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_nameguardrail_mode 区分。
路由器 / 鉴权 / Redis / 数据库延迟相应的 服务钩子跨度router, auth, redis, postgres, …)的持续时间。
重试 / 回退次数代理根跨度上的 hidden_params.x-litellm-attempted-retrieshidden_params.x-litellm-attempted-fallbacks
流式传输?llm.is_streaming 属性("True"/"False")。

属性参考

在 LLM 调用跨度上设置的属性。名称遵循 OTEL GenAI semconv

属性默认模式Semconv 模式
gen_ai.operation.namelitellm call_type (例如 acompletion)semconv 操作(chat, embeddings, text_completion
gen_ai.system提供商名称(例如 openai未更改
gen_ai.provider.name未设置提供商名称(规范中重命名的必填属性)
gen_ai.frameworklitellmlitellm
gen_ai.request.modelmodelmodel
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.typecall_type 相同(例如 acompletion, aembedding, aimage_generation

llm.* (代理根和 LLM 跨度)

属性
llm.request.typeLiteLLM call_type
llm.is_streaming"True""False"
llm.useruser 参数(若设置)

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_CONTENTlitellm.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_emailLiteLLM 内部用户标识符
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_metadataMCP 和向量存储请求信息
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 调用跨度上)

属性
exceptionstr(original_exception)
跨度状态StatusCode.ERROR

资源属性(每个跨度)

属性默认覆盖
service.namelitellmOTEL_SERVICE_NAME
deployment.environmentproductionOTEL_ENVIRONMENT_NAME
model_id匹配 service.nameOTEL_MODEL_ID
telemetry.sdk.{language,name,version}由 SDK 设置

稳定性

上述跨度名称、指标名称和属性集在 LiteLLM 补丁版本中保持稳定。LLM 调用跨度名称和类型在 默认模式和 Semconv 模式 之间更改,并通过记录的 opt-in 标志进行迁移,而不是在版本之间迁移。

支持

对于 LiteLLM OTEL 集成问题,请在 BerriAI/litellm 上提交 issue。对于 OpenLLMetry / Traceloop 语义约定问题,请参见 Slack 或发送电子邮件至 dev@traceloop.com