3、异常事件定义:什么是 Span 异常事件、Exception 语义约定、异常事件的属性结构

在 OpenTelemetry 体系中,异常事件(Exception Event)是 Span 上承载错误信息的关键载体。它并非直接修改 Span 状态,而是通过事件机制将异常细节附加到 Span 上,从而保留完整的错误上下文。本章将深入解析异常事件的定义、语义约定及其属性结构,为后续的路由策略打下基础。

3.1 什么是 Span 异常事件

Span 异常事件是 OpenTelemetry 规范中预定义的一种特殊事件类型,用于记录应用程序中发生的异常。与简单的日志不同,异常事件具有标准化的属性结构,能够被 Collector 和后端系统统一解析和处理。

核心要点:异常事件是 Span 上的一个“时间戳 + 结构化属性”的组合,它记录了异常发生时的快照,包括异常类型、消息、堆栈轨迹等关键信息。

异常事件与 Span 状态的关系如下表所示:

特性 Span 状态(Status) 异常事件(Exception Event)
目的 标记 Span 的成功/失败状态 记录异常发生的详细上下文
数据结构 简单枚举 + 描述字符串 结构化属性集合(类型、消息、堆栈等)
数量限制 每个 Span 只能设置一次 一个 Span 可附加多个异常事件
典型用途 表示请求处理结果 记录重试、降级、异常链等场景
实战建议:在微服务调用链中,建议同时设置 Span Status 为 ERROR 并附加异常事件,这样既能快速识别失败 Span,又能获取完整的错误详情。

3.2 Exception 语义约定

OpenTelemetry 为异常事件定义了严格的语义约定(Semantic Conventions),确保不同语言 SDK 生成的异常事件具有一致的属性名称和含义。这些约定是 Collector 进行异常路由和聚合的基础。

异常事件的事件名称固定为 exception,其核心属性包括:

属性名 类型 是否必填 说明
exception.type string 推荐 异常的全限定类名,如 java.lang.IllegalArgumentException
exception.message string 推荐 异常的描述信息,如 Invalid parameter: userId
exception.stacktrace string 推荐 完整的堆栈轨迹字符串
exception.escaped boolean 可选 异常是否逃逸出当前 Span 边界(默认 false)
注意:虽然 exception.type 和 exception.message 被标记为“推荐”,但在生产环境中强烈建议同时填充这两个属性。缺少类型或消息的异常事件,在路由和告警时可能无法被正确识别。

以下是一个符合语义约定的异常事件示例(以 JSON 格式表示):

{
  "name": "exception",
  "timeUnixNano": "1698765432123456789",
  "attributes": [
    {
      "key": "exception.type",
      "value": { "stringValue": "java.lang.NullPointerException" }
    },
    {
      "key": "exception.message",
      "value": { "stringValue": "Cannot invoke method on null object" }
    },
    {
      "key": "exception.stacktrace",
      "value": { "stringValue": "java.lang.NullPointerException\n\tat com.example.service.UserService.getUser(UserService.java:42)\n\tat ..." }
    },
    {
      "key": "exception.escaped",
      "value": { "boolValue": true }
    }
  ]
}

3.3 异常事件的属性结构

异常事件的属性结构遵循 OpenTelemetry 的通用属性模型,每个属性由 键(Key)和 值(Value)组成。值可以是字符串、布尔、整数、浮点数、数组或键值列表等类型。

在 Collector 内部,异常事件被表示为 plog.LogRecord 的一部分,其属性结构可以通过以下 SVG 图直观理解:

异常事件属性结构 Span 事件列表 (Events) 异常事件 (exception) 异常事件属性 exception.type string exception.message string exception.stacktrace string exception.escaped boolean (可选) 属性结构要点 • 每个属性由 key-value 对组成,value 支持多种类型 • 事件名称固定为 "exception",不可自定义 • 属性顺序不影响语义,Collector 按 key 解析

3.4 异常事件与 Span 的关联方式

异常事件通过 Span 的事件列表(Events)与 Span 关联。每个事件包含时间戳、名称和属性集合。Collector 在处理时,可以通过以下方式识别和提取异常事件:

  • 按事件名称过滤:筛选 name == "exception" 的事件
  • 按属性键值路由:根据 exception.type 或 exception.message 的值进行分发
  • 按时间范围聚合:利用事件的时间戳进行时序分析
关键洞察:异常事件的属性结构是 Collector 路由策略的“数据燃料”。只有充分理解属性的层级和类型,才能编写出精准的路由规则。例如,基于 exception.type 的前缀匹配可以将所有 java.lang.* 异常路由到 Java 专家团队。

下表总结了异常事件在不同场景下的典型属性组合:

场景 exception.type exception.message exception.escaped
业务参数校验失败 com.example.ValidationException Invalid email format false
数据库连接超时 java.sql.SQLTimeoutException Connection timed out after 5000ms true
第三方 API 调用失败 io.grpc.StatusRuntimeException UNAVAILABLE: Service unavailable true
内存溢出(捕获后记录) java.lang.OutOfMemoryError Java heap space false
最佳实践:在 SDK 端记录异常事件时,尽量使用标准属性名,避免自定义属性。如果确实需要附加业务上下文(如订单号、用户 ID),建议使用 exception.* 之外的命名空间,例如 business.orderId,以免与语义约定冲突。