bigywater.tech
微服务 · 65

span 的属性、事件与状态

span 能带多少属性和事件?超了会丢哪些?状态 OK、ERROR、UNSET 的设置规则是什么?异常是怎么记进 span 的?一个属性占多少字节?属性里的密码和 token 怎么统一去掉?这篇用 OpenTelemetry Python SDK 和 Collector 逐项量出来。

128(保留最后 128 个)
往一个 span 里加 200 个属性或事件时,默认保留的个数;先加的 72 个被丢掉
41.0 字节
值为 20 个字符时,每增加一个属性,序列化后的 span 增加的字节数(值 2000 字符时约 2024.5)
3 → 0
配置 attributes / transform 处理器之后,Collector 输出里泄露的敏感属性个数

数量和长度限制

实测 · 本机执行输出
  属性:保留 128 个,键编号 72~199,dropped_attributes=72;事件:保留 128 个,编号 72~199,dropped_events=72
  这一步里 SDK 打出的 warning 日志(去重后的内容 × 次数):“Attributes dict is full. Dropping the oldest key-value pair from attributes to make space …”× 72;“String attribute value exceeds max length of 20, truncating.…”× 2
  max_attribute_length=20 时:字符串 100 字符 → 保留 20 字符;字符串数组里 50 字符的元素 → 20 字符,5 字符的元素 → 5 字符;超大整数属性 → 12345678901234567890
  属性值的类型限制(保留下来的属性值):v.list=(1, 2, 3);v.map={'a': 1};v.mixed_list=(1, 'a');v.none=None;v.object=None;v.str='v';SDK 的 warning 日志:“Invalid type `<class 'object'>` for attribute value. Expected one of bool, str, None, byte…”× 1。没有抛异常

属性和事件默认各 128 个,超限时丢掉最先加的;可以用 SpanLimits 设置值的最大长度。

状态与异常

实测 · 本机执行输出
  调用顺序                    | 最终状态  | 描述  
  ------------------------+-------+-----
  什么都不设置                  | UNSET | None
  ERROR("a")              | ERROR | 'a' 
  ERROR("a") → ERROR("b") | ERROR | 'b' 
  ERROR → OK              | OK    | None
  OK → ERROR              | OK    | None
  OK → UNSET              | OK    | None
  ERROR → UNSET           | ERROR | 'a' 
  OK 带描述                  | OK    | None
  SDK 的 warning 日志(去重 × 次数):“description should only be set when status_code is set to StatusCode.ERROR…”× 1
  做法                            | 最终状态  | 状态描述               | exception 事件数 | exception 事件的属性键                                                       
  ------------------------------+-------+--------------------+---------------+------------------------------------------------------------------------
  默认(异常离开 with 块)               | ERROR | 'ValueError: boom' | 1             | exception.escaped,exception.message,exception.stacktrace,exception.type
  record_exception=False        | ERROR | 'ValueError: boom' | 0             |                                                                        
  set_status_on_exception=False | UNSET | None               | 1             | exception.escaped,exception.message,exception.stacktrace,exception.type
  两个都 False                     | UNSET | None               | 0             |                                                                        
  手动 record_exception(不设状态)     | UNSET | None               | 1             | exception.escaped,exception.message,exception.stacktrace,exception.type

OK 是终态;异常默认同时记录事件并把状态置为 ERROR;手动 record_exception 只加事件。

序列化大小

实测 · 本机执行输出
  内容                  | 序列化后的 span 字节数 | 平均每个属性/事件增加的字节数
  --------------------+----------------+----------------
  (a)0 个属性            | 289            | 0              
  (a)10 个属性,值 20 字符   | 701            | 41.2           
  (a)50 个属性,值 20 字符   | 2341           | 41.0           
  (a)128 个属性,值 20 字符  | 5539           | 41.0           
  (b)10 个属性,值 20 字符   | 701            | 41.2           
  (b)10 个属性,值 200 字符  | 2531           | 224.2          
  (b)10 个属性,值 2000 字符 | 20534          | 2024.5         
  (c)10 个事件(各 2 个属性)  | 881            | 59.2           
  OTLP 里每个属性的键和值各占字节,键名(这里是 attr.name.NNN)也要付出空间,值越长越大;个数上限由 max_attributes 控制,值的长度上限由 max_attribute_length 控制

每个属性的键和值各占字节,值越长越大,事件也有固定开销。

敏感信息

实测 · 本机执行输出
  配置                                  | user.email                                                       | password | http.url                                                    
  ------------------------------------+------------------------------------------------------------------+----------+-------------------------------------------------------------
  (a)没有处理器                            | alice@example.com                                                | hunter2  | https://api.example.com/login?user=alice&token=SECRET123&x=1
  (b)attributes/scrub + transform/url | ff8d9819fc0e12bf0d24892e45987e249a28dce836a85cad60e28eaaa8c6d976 | (已删除)    | https://api.example.com/login?user=alice&token=***&x=1      
  SDK 把属性原样放进 span,不会替你脱敏;在 Collector 里可以统一删除、哈希或者按正则替换,这样所有服务不用各自改代码
ERRORS {}

SDK 不脱敏,可以在 Collector 里统一删除、哈希或按正则替换。

分步演示

span 的属性、事件与状态
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。OpenTelemetry Python SDK 1.45.1,Collector contrib 0.163.1(attributes 与 transform 处理器,OTTL 规则 replace_pattern);“序列化大小”是 SDK 的 OTLP protobuf 编码器给出的字节数,不含 gRPC 或 HTTP 的头;限制和状态规则都是这个 SDK 版本的行为,每项各测 1 次(确定性的)
    • 没有验证:Java / Go SDK 的限制与丢弃策略(只测了 Python);环境变量 OTEL_ATTRIBUTE_COUNT_LIMIT 等的覆盖行为;语义约定(semantic conventions)里各属性的推荐名;http.status_code 与 span 状态的映射规则(只引用了约定,没有逐条测);redaction 处理器、按 allowlist 保留属性;追踪后端对大属性的存储限制
    京ICP备2026067502号-1京公网安备11010502063487号