MLflow 仓库内 OpenTelemetry Protobuf 定义:最小化 Vendor 机制、数据模型与更新维护指南
【免费下载链接】mlflowThe open source AI engineering platform for agents, LLMs, and ML models. MLflow enables teams of all sizes to debug, evaluate, monitor, and optimize production-quality AI applications while controlling costs and managing access to models and data.项目地址: https://gitcode.com/GitHub_Trending/ml/mlflow
本文聚焦 MLflow 开源仓库中mlflow/protos/opentelemetry/目录下的 OpenTelemetry Protocol Buffer(protobuf)规范定义。MLflow 的 AI Tracing 能力以 OpenTelemetry 数据模型为底层格式,但并未全量引入上游规范,而是只 vendor 了一个"最小够用"的子集。读完本文,你将掌握这三个 proto 文件各自承担的数据建模职责、它们如何被 MLflow 的 tracing 链路(OTLP 导出、trace 归档、Databricks 服务对接)实际消费,以及如何通过update.sh安全地升级这批定义。
为什么 MLflow 需要内置 OpenTelemetry Proto 定义
MLflow 的 Tracing 模块 采用 OpenTelemetry 的 Span 模型来记录一次 LLM/Agent 调用链路的输入、输出、耗时与中间事件。为了保证生成的 trace 能够:
- 通过标准的 OTLP(OpenTelemetry Protocol)导出到任意兼容后端;
- 在
TracesData结构上与 OpenTelemetry 生态直接互换; - 与 Databricks tracking server 的 trace 存储协议保持字节级兼容;
MLflow 的 Python 包必须在运行时拥有对应的 protobuf 消息类。这些类的定义不能依赖用户环境是否安装了完整的opentelemetry-proto包,因此 MLflow 选择将所需的最小集合直接 vendor 进仓库,随 mlflow 包本体 一起分发。
目录结构与包含内容
mlflow/protos/opentelemetry/ ├── README.md # 本文所述的说明文档 ├── update.sh # 上游同步脚本 └── proto/ ├── common/ │ └── v1/ │ └── common.proto # 通用数据类型 ├── resource/ │ └── v1/ │ └── resource.proto # 资源属性 └── trace/ └── v1/ └── trace.proto # Trace 数据模型README 明确说明这是一个刻意的最小子集:MLflow 只需要 trace(追踪)能力,因此仅引入了上表中的三个文件。对比上游opentelemetry-proto的完整集(还包括 metrics、logs、profiles、experimental 等大量目录),这套 vendor 只保留了 3 个.proto源文件,显著降低了仓库体积与生成代码量。
数据模型详解:trace.proto
trace.proto 是整个 vendor 子集的核心,声明了package opentelemetry.proto.trace.v1,并 import 了common.proto与resource.proto两个姊妹文件。
三层嵌套结构:TracesData → ResourceSpans → ScopeSpans
从源码结构看(trace.proto#L38-L83),OTLP 的 trace 数据采用三层容器嵌套:
| 消息 | 关键字段 | 语义 |
|---|---|---|
TracesData | repeated ResourceSpans resource_spans | 顶层数据包,可批量携带来自多个资源的 span 集合 |
ResourceSpans | Resource resource、repeated ScopeSpans scope_spans、string schema_url | 归属于同一 Resource 的 span 集合;schema_url标识资源数据遵循的 schema 版本 |
ScopeSpans | InstrumentationScope scope、repeated Span spans、string schema_url | 归属于同一插桩作用域(如某个 SDK/flavor)的 span 列表 |
ResourceSpans与ScopeSpans各带独立的schema_url字段,分别约束 resource 数据与 span 数据遵循的 schema 版本(见 trace.proto#L58-L64 与 L77-L82 的注释说明)。中间转发节点可将多个来源的 span 批量合并进同一个TracesData,这也是 OTLP 导出路径的天然形态。
Span 消息:一次操作的最小单元
Span消息(trace.proto#L88-L302)描述了系统中单个组件执行的一次操作,其核心字段如下:
| 字段 | 类型 | 说明 |
|---|---|---|
trace_id | bytes(16 字节) | 所属 trace 的唯一标识,同一条 trace 的所有 span 共享;全零或非 16 字节视为非法,必填 |
span_id | bytes(8 字节) | 本 span 在 trace 内的唯一标识,创建时分配,必填 |
parent_span_id | bytes(8 字节) | 父 span 的 ID;根 span 此字段必须为空 |
trace_state | string | W3C Trace Context 格式的 trace 状态 |
name | string | 操作描述(如"qualified method name + 行号"),语义上必须非空,必填 |
kind | SpanKind枚举 | span 类型(见下文) |
start_time_unix_nano/end_time_unix_nano | fixed64 | 以纳秒计的 Unix 时间戳,语义上要求end_time >= start_time |
attributes | repeated KeyValue | 键值对属性集合,键必须唯一 |
events | repeated Event | 带时间戳的注解事件(time_unix_nano+name+attributes) |
links | repeated Link | 指向同 trace 或跨 trace 其他 span 的引用(如批处理场景) |
status | Status | 最终状态(见下文) |
flags | fixed32 | 位标志字段(见下文) |
dropped_*_count | uint32 | 因超长或超量被丢弃的 attributes / events / links 计数 |
Event、Link内嵌消息同样以KeyValue携带属性,且各自维护dropped_attributes_count用于记录截断损耗——这保证了消费者能感知到数据缺失,而不会被静默误导。
SpanKind:区分调用语义
SpanKind枚举(trace.proto#L152-L178)用于在父子关系之外补充描述 span 的语义角色:
SPAN_KIND_INTERNAL(默认):应用内部操作,非边界调用;SPAN_KIND_SERVER:服务端处理某个 RPC/远程请求;SPAN_KIND_CLIENT:向远程服务发起的请求;SPAN_KIND_PRODUCER/SPAN_KIND_CONSUMER:消息队列的投递与消费,生产者与消费者之间通常不存在直接的关键路径时延关系。
Status:错误模型
Status消息(trace.proto#L306-L326)由message(人类可读错误信息)与StatusCode枚举构成:STATUS_CODE_UNSET(默认)、STATUS_CODE_OK(开发者确认成功)、STATUS_CODE_ERROR(span 包含错误)。未显式设置 status 时按UNSET处理。
SpanFlags:trace 标志位的位掩码约定
SpanFlags枚举(trace.proto#L342-L357)定义了Span.flags位字段的解读方式:
- 第 0-7 位:W3C Trace Context 的 trace flags,读取用
flags & SPAN_FLAGS_TRACE_FLAGS_MASK; - 第 8 位(
SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK):父 span/link 是否 remote 的信息是否已知; - 第 9 位(
SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK):父 span/link 是否为 remote; - 第 10-31 位:保留。
该枚举与flags字段均为 OpenTelemetry 协议 1.1 引入的增量特性,旧生产者不会设置该字段,因此消费者不能依据某标志位的缺失反推功能缺失。
common.proto:通用值类型
common.proto 定义了一组被 trace、resource 反复复用的基础类型,声明package opentelemetry.proto.common.v1:
AnyValue(common.proto#L28-L40):属性值的统一容器,通过oneof承载string_value、bool_value、int_value、double_value、array_value、kvlist_value、bytes_value七种取值,支持嵌套的数组与键值列表;ArrayValue:repeated AnyValue values,因 protobuf 的oneof不允许repeated字段,所以单独拆成消息;KeyValueList:键值对列表的包装,键必须唯一;而像Span.attributes这种高频场景直接使用repeated KeyValue以避免多余包装层(见 common.proto#L49-L53 的注释),两种方式语义等价;KeyValue:string key+AnyValue value,是 attribute 的最小单元;InstrumentationScope(common.proto#L71-L81):插桩作用域信息,含name(空名视为未知)、version及可选 attributes,用于区分不同 SDK/flavor 产生的 span;EntityRef(common.proto#L87-L115):对实体(如 service、host)的引用描述,包含type、标识性属性键id_keys与描述性属性键description_keys,目前标注为 Development 状态。
resource.proto:资源属性
resource.proto 定义了Resource消息(resource.proto#L28-L43),用于描述产生遥测数据的资源实体:
attributes:描述资源的属性集合,键必须唯一,通常承载 service.name、service.version、host 信息等全局属性;dropped_attributes_count:被丢弃的属性数量;entity_refs:参与该资源的实体引用列表(Development 状态)。
在 OTLP 层级中,Resource挂载在ResourceSpans.resource上,与ScopeSpans中的 span 数据解耦,实现"一份资源属性复用多条 span"的高效打包。
这些 Proto 在 MLflow 源码中的实际消费
vendor 进来的定义不是摆设,它们在 MLflow 的 tracing 链路上被多处直接引用:
- Databricks trace 服务协议:
databricks_tracing.proto在第 13 行import "opentelemetry/proto/trace/v1/trace.proto",并在Trace消息中直接以repeated opentelemetry.proto.trace.v1.Span spans承载 span 数据(databricks_tracing.proto#L516-L519)。这意味着与 Databricks tracking server 交互时,trace 的线上序列化格式就是 OpenTelemetry 的 Span 结构; - OTLP 导出器:otlp.py 从
opentelemetry.proto.common.v1.common_pb2与opentelemetry.proto.resource.v1.resource_pb2导入AnyValue、ArrayValue、KeyValueList与Resource(otlp.py#L7-L8),并实现_set_otel_proto_anyvalue/_decode_otel_proto_anyvalue在 Python 值与 OTel proto 值之间互转;同文件还定义了 OTLP 的/v1/traces、/v1/metrics路径及 gRPC / HTTP-protobuf 两种导出协议选择; - Trace 归档与恢复:otel_archival.py 从
opentelemetry.proto.trace.v1.trace_pb2导入TracesData(otel_archival.py#L10),将 span 编码为 OTLPTracesData落盘,读取归档时再经Span.from_otel_proto还原。
这也解释了为什么三个文件一个都不能少:trace.proto提供结构,common.proto提供值类型,resource.proto提供资源上下文,三者通过 import 关系形成完整闭环。
更新流程:update.sh逐步解析
README 给出的升级步骤为:修改update.sh中的COMMIT_SHA→ 执行脚本。结合脚本源码(update.sh),完整流程如下:
- 锁定上游版本:
COMMIT_SHA="8654ab7a5a43ca25fe8046e59dcd6935c3f76de0"(update.sh#L13)对应 opentelemetry-proto 的 v1.7.0 版本,脚本以 commit 级别的 tar 包为唯一事实来源,确保可复现; - 定位脚本目录:
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"后cd进入,保证脚本可以从仓库任意位置调用; - 清理旧文件:
rm -rf proto删除现有 vendor 目录,避免残留文件造成新旧混用; - 下载并解压:
curl -fsSL "$ARCHIVE_URL" | tar -xzf - -C "$TEMP_DIR",使用mktemp -d临时目录,并以trap ... EXIT保证退出时清理,兼容 macOS BSD tar 与 GNU tar; - 选择性拷贝:仅拷贝三个目标文件到
proto/trace/v1、proto/common/v1、proto/resource/v1(update.sh#L33-L36),其余上游文件一律不引入。
执行方式(从仓库根目录):
./mlflow/protos/opentelemetry/update.sh维护注意事项与扩展建议
- 保持最小子集原则:README 强调,若未来需要 metrics、logs 等能力,应先扩展
update.sh的提取列表再运行脚本,而不是手动复制文件——否则下次同步会被rm -rf proto清掉; - 校验 import 链:三个文件之间存在 import 依赖(
trace.proto依赖common.proto、resource.proto),新增文件时务必连同其 import 链一并提取; - 版本一致性:
COMMIT_SHA锁定的是 opentelemetry-proto 的 commit 而非发行 tag,升级后应关注SpanFlags等增量字段、schema_url语义变化及EntityRef等 Development 状态消息的演进; - 生成代码同步:
.proto仅是源定义,MLflow 包内实际加载的是编译后的_pb2类(如opentelemetry.proto.trace.v1.trace_pb2)。替换 proto 后需重新生成对应的 Python 绑定代码,并运行 tracing 相关测试 验证序列化兼容性。
相关源码指引
- 定义文件:trace.proto、common.proto、resource.proto、update.sh
- 消费方:databricks_tracing.proto、otlp.py、otel_archival.py
- 上层 tracing 模块:mlflow/tracing
- 测试:tests/tracing
【免费下载链接】mlflowThe open source AI engineering platform for agents, LLMs, and ML models. MLflow enables teams of all sizes to debug, evaluate, monitor, and optimize production-quality AI applications while controlling costs and managing access to models and data.项目地址: https://gitcode.com/GitHub_Trending/ml/mlflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考