bug: OTel ingestion serializes int64 as protobufjs Long internals after protobufjs 7.4 → 7.5 upgrade

用户运行 Langfuse 自托管服务(版本 ≥ 3.169.0),配合 OpenTelemetry SDK(如 Java SDK 1.25.0 或 Python SDK 3.12.0)通过 OTLP/protobuf 协议(HTTP 或 gRPC)发送 trace 数据。配置了服务端 ingest

bug: OTel ingestion serializes int64 as protobufjs Long internals after protobufjs 7.4 → 7.5 upgrade

bug: OTel ingestion serializes int64 as protobufjs Long internals after protobufjs 7.4 → 7.5 upgrade

快速结论:该报错发生在 Langfuse 自托管版本从 3.155.1 升级到 3.169.0 后,OTLP/protobuf ingestion 中所有 int64 字段(如 intValuestartTimeUnixNanoendTimeUnixNanotimeUnixNano)被序列化为 protobufjs 内部 Long 对象 {"low": <int>, "high": <int>, "unsigned": <bool>} 而非数字或字符串。优先排查 .toObject() 调用是否缺少 longs 转换选项。

问题场景

用户运行 Langfuse 自托管服务(版本 ≥ 3.169.0),配合 OpenTelemetry SDK(如 Java SDK 1.25.0 或 Python SDK 3.12.0)通过 OTLP/protobuf 协议(HTTP 或 gRPC)发送 trace 数据。配置了服务端 ingestion masking 回调(LANGFUSE_INGESTION_MASKING_CALLBACK_URL)时,回调接收到的 int64 字段变为 protobufjs 内部 Long 对象结构,而非符合 OTLP/JSON 规范的十进制字符串或数字。

报错原文

// 序列化结果示例(JSON.stringify 后):
{"v":{"low":1234567890,"high":0,"unsigned":false}}

// 期望结果(protobufjs 7.4 行为):
{"v":1234567890}

原因分析

在 Langfuse 3.169.0 版本中,PR #13232 将 protobufjs 从 7.4.0 升级到 7.5.5。关键变化:

  • protobufjs 7.4:当 long.js 无法解析时,util.Long 未初始化,int64 回退为普通 JS 数字。
  • protobufjs 7.5:当 long.js 无法解析时,内部创建一个回退 Long 类,但该类缺少 toJSON() 方法。因此 JSON.stringify 会暴露原始的可枚举属性 {low, high, unsigned}

根因是 web/src/pages/api/public/otel/v1/traces/index.ts 中的 .toObject(parsed) 调用未指定 longs 转换选项,导致 protobufjs 7.5 使用内部 Long 对象序列化 int64 字段。

环境排查

  • Langfuse 自托管版本:≥ 3.169.0(3.155.1 为最后一个正常版本)
  • protobufjs 版本:7.5.5(通过 PR #13232 引入)
  • OpenTelemetry SDK:任何使用 OTLP/protobuf 协议的 SDK(如 Java SDK 1.25.0、Python telemetry SDK 1.39.1)
  • 服务端配置:启用 LANGFUSE_INGESTION_MASKING_CALLBACK_URL 时触发问题明显

解决步骤

  1. 定位文件 web/src/pages/api/public/otel/v1/traces/index.ts 中的 .toObject(parsed) 调用(约第 96-98 行)。
  2. 可优先尝试将调用改为传递 longs 选项,推荐使用 { longs: String }(符合 OTLP/JSON 规范,将 int64 编码为十进制字符串,避免纳秒时间戳精度丢失。如果对 >53 位数值的精度丢失可接受,也可使用 { longs: Number })。
resourceSpans =
  $root.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest.toObject(
    parsed,
    { longs: String },
  ).resourceSpans;
  • 生成的 protobuf 转换代码(web/src/pages/api/public/otel/otlp-proto/generated/root.ts)已支持 longs: Stringlongs: Number 选项,适用于所有 int64 字段(包括 intValuestartTimeUnixNanoendTimeUnixNanotimeUnixNano)。
  • 验证方法

    部署修复后,发送包含 int64 字段的 OTLP/protobuf trace(如使用 OpenTelemetry Java SDK 或 Python SDK),检查 masking 回调接收到的 payload 中 int64 字段是否为十进制字符串("1234567890")或数字,而非 {"low":..., "high":..., "unsigned":...} 对象格式。也可直接使用 Issue 中提供的 Docker 命令复现并验证修复前后的序列化差异。

    参考来源

    langfuse/langfuse #13295

    GamsGo AI

    AI 工具推荐

    想把多个 AI 模型放在一个入口?

    GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

    了解 GamsGo AI

    推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

    celebrityanime
    celebrityanime
    文章: 13354

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注