追踪

请求在您的应用程序中的路径。

Traces 为我们提供了应用程序收到请求时发生情况的全景。无论您的应用程序是拥有单个数据库的单体应用,还是复杂的服务网格,Trace都对于理解请求在您的应用程序中的完整“路径”至关重要。

让我们用三个工作单元来探索这个概念,它们表示为 Spans

hello span

{
  "name": "hello",
  "context": {
    "trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
    "span_id": "051581bf3cb55c13"
  },
  "parent_id": null,
  "start_time": "2022-04-29T18:52:58.114201Z",
  "end_time": "2022-04-29T18:52:58.114687Z",
  "attributes": {
    "http.route": "some_route1"
  },
  "events": [
    {
      "name": "Guten Tag!",
      "timestamp": "2022-04-29T18:52:58.114561Z",
      "attributes": {
        "event_attributes": 1
      }
    }
  ]
}

这是根 span,表示整个操作的开始和结束。请注意,它具有 `trace_id` 字段来指示 trace,但没有 `parent_id`。这就是您知道它是根 span 的方式。

hello-greetings span

{
  "name": "hello-greetings",
  "context": {
    "trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
    "span_id": "5fb397be34d26b51"
  },
  "parent_id": "051581bf3cb55c13",
  "start_time": "2022-04-29T18:52:58.114304Z",
  "end_time": "2022-04-29T22:52:58.114561Z",
  "attributes": {
    "http.route": "some_route2"
  },
  "events": [
    {
      "name": "hey there!",
      "timestamp": "2022-04-29T18:52:58.114561Z",
      "attributes": {
        "event_attributes": 1
      }
    },
    {
      "name": "bye now!",
      "timestamp": "2022-04-29T18:52:58.114585Z",
      "attributes": {
        "event_attributes": 1
      }
    }
  ]
}

此 span 封装了特定任务,例如打招呼,其父 span 是 `hello` span。请注意,它与根 span 共享相同的 `trace_id`,表示它是同一 trace 的一部分。此外,它有一个 `parent_id`,该 `parent_id` 与 `hello` span 的 `span_id` 匹配。

hello-salutations span

{
  "name": "hello-salutations",
  "context": {
    "trace_id": "5b8aa5a2d2c872e8321cf37308d69df2",
    "span_id": "93564f51e1abe1c2"
  },
  "parent_id": "051581bf3cb55c13",
  "start_time": "2022-04-29T18:52:58.114492Z",
  "end_time": "2022-04-29T18:52:58.114631Z",
  "attributes": {
    "http.route": "some_route3"
  },
  "events": [
    {
      "name": "hey there!",
      "timestamp": "2022-04-29T18:52:58.114561Z",
      "attributes": {
        "event_attributes": 1
      }
    }
  ]
}

此 span 表示此 trace 中的第三个操作,与前一个 span 一样,它是 `hello` span 的子 span。这也使其成为 `hello-greetings` span 的兄弟 span。

这三个 JSON 块共享相同的 `trace_id`,而 `parent_id` 字段表示层级关系。这就是 Trace!

您还会注意到每个 Span 都看起来像一个结构化日志。这是因为它们确实是!一种思考 Trace 的方式是,它们是带有上下文、关联、层级结构等的结构化日志集合。然而,这些“结构化日志”可以来自不同的进程、服务、虚拟机、数据中心等。这就是 tracing 能够表示任何系统的端到端视图的原因。

为了理解 OpenTelemetry 中的 tracing 如何工作,让我们看看将在代码仪器化中发挥作用的组件列表。

Tracer Provider

Tracer Provider(有时称为 `TracerProvider`)是 `Tracer` 的工厂。在大多数应用程序中,Tracer Provider 只初始化一次,其生命周期与应用程序的生命周期相匹配。Tracer Provider 的初始化还包括 Resource 和 Exporter 的初始化。这通常是使用 OpenTelemetry 进行 tracing 的第一步。在某些语言 SDK 中,已经为您初始化了一个全局 Tracer Provider。

Tracer

Tracer 会创建 Span,其中包含有关给定操作(例如服务中的请求)发生情况的更多信息。Tracers 是从 Tracer Providers 创建的。

Trace Exporters

Trace Exporters 将 trace 发送到消费者。此消费者可以是用于调试和开发的标准输出、OpenTelemetry Collector,或者您选择的任何开源或供应商后端。

Context Propagation

Context Propagation 是实现分布式跟踪的核心概念。通过 Context Propagation,Span 可以相互关联,并组合成一个 trace,无论 Span 在哪里生成。要了解有关此主题的更多信息,请参阅关于 Context Propagation 的概念页面。

Span

Span 代表一个工作单元或操作。Span 是 Trace 的构建块。在 OpenTelemetry 中,它们包含以下信息:

示例 Span

{
  "name": "/v1/sys/health",
  "context": {
    "trace_id": "7bba9f33312b3dbb8b2c2c62bb7abe2d",
    "span_id": "086e83747d0e381e"
  },
  "parent_id": "",
  "start_time": "2021-10-22 16:04:01.209458162 +0000 UTC",
  "end_time": "2021-10-22 16:04:01.209514132 +0000 UTC",
  "status_code": "STATUS_CODE_OK",
  "status_message": "",
  "attributes": {
    "net.transport": "IP.TCP",
    "net.peer.ip": "172.17.0.1",
    "net.peer.port": "51820",
    "net.host.ip": "10.177.2.152",
    "net.host.port": "26040",
    "http.method": "GET",
    "http.target": "/v1/sys/health",
    "http.server_name": "mortar-gateway",
    "http.route": "/v1/sys/health",
    "http.user_agent": "Consul Health Check",
    "http.scheme": "http",
    "http.host": "10.177.2.152:26040",
    "http.flavor": "1.1"
  },
  "events": [
    {
      "name": "",
      "message": "OK",
      "timestamp": "2021-10-22 16:04:01.209512872 +0000 UTC"
    }
  ]
}

Span 可以嵌套,正如父 Span ID 的存在所暗示的那样:子 Span 代表子操作。这使得 Span 能够更准确地捕获应用程序中完成的工作。

Span Context

Span Context 是每个 Span 上一个不可变的对象,包含以下内容:

  • 表示 Span 所属 Trace 的 Trace ID
  • Span 的 Span ID
  • Trace Flags,一个包含有关 Trace 信息的二进制编码
  • Trace State,一个键值对列表,可以携带供应商特定的 trace 信息

Span Context 是 Span 的一部分,它与 Distributed ContextBaggage 一起被序列化和传播。

由于 Span Context 包含 Trace ID,因此在创建 Span Links 时会用到它。

Attributes

Attributes 是键值对,包含元数据,您可以使用它们来注释 Span,以携带有关其跟踪的操作的信息。

例如,如果一个 span 跟踪一个在电子商务系统中将商品添加到用户购物车的操作,您可以捕获用户的 ID、要添加到购物车的商品 ID 以及购物车 ID。

您可以在 Span 创建期间或之后向 Span 添加 Attributes。在 Span 创建时添加 Attributes 可以使 Attributes 可用于 SDK 采样。如果您必须在 Span 创建后添加值,请使用该值更新 Span。

Attributes 有以下规则,每种语言 SDK 都会实现这些规则:

  • 键必须是不可为空的字符串值
  • 值必须是不可为空的字符串、布尔值、浮点值、整数或这些值的数组

此外,还有 Semantic Attributes,它们是常见操作中通常存在的元数据的已知命名约定。尽可能使用语义 Attribute 命名很有帮助,这样常见的元数据类型在不同系统之间就会标准化。

Span Events

Span Event 可以被视为 Span 上的结构化日志消息(或注释),通常用于表示 Span 持续期间有意义的单个时间点。

例如,考虑 Web 浏览器中的两种场景:

  1. 跟踪页面加载
  2. 表示页面何时变得可交互

Span 最适合跟踪第一种场景,因为它是一个具有开始和结束的操作。

Span Event 最适合跟踪第二种场景,因为它代表了一个有意义的、单一的时间点。

何时使用 Span Events 而不是 Span Attributes

由于 Span Events 也包含 Attributes,因此何时使用 Events 而不是 Attributes 的问题可能并不总是显而易见的。为了帮助您做出决定,请考虑特定时间戳是否有意义。

例如,当您跟踪一个 Span 的操作并且该操作完成时,您可能希望将操作中的数据添加到您的遥测数据中。

  • 如果操作完成的时间戳有意义或相关,请将数据附加到 Span Event。
  • 如果时间戳没有意义,请将数据作为 Span Attributes 附加。

Links 的存在是为了让您可以将一个 Span 与一个或多个 Span 关联起来,暗示因果关系。例如,假设我们有一个分布式系统,其中一些操作由 trace 跟踪。

作为对其中一些操作的响应,会排队执行一个额外的操作,但其执行是异步的。我们也可以用 trace 来跟踪后续的操作。

我们希望将后续操作的 trace 与第一个 trace 关联起来,但我们无法预测后续操作何时开始。我们需要关联这两个 trace,因此我们将使用 span link。

您可以将第一个 trace 的最后一个 span 链接到第二个 trace 的第一个 span。现在,它们在因果关系上是关联的。

Links 是可选的,但它们是关联 trace spans 的一种好方法。

有关更多信息,请参阅 Span Links

Span Status

每个 Span 都有一个状态。三个可能的值是:

  • Unset
  • Error
  • Ok

默认值为 `Unset`。状态为 `Unset` 的 Span 表示它跟踪的操作已成功完成,没有错误。

当 Span 状态为 `Error` 时,这意味着它跟踪的操作中发生了一些错误。例如,这可能是由于处理请求的服务器出现 HTTP 500 错误。

当 Span 状态为 `Ok` 时,这意味着该 Span 已被应用程序开发人员明确标记为无错误。虽然这可能令人费解,但在开发人员知道 Span 已成功完成时,并不要求将 Span 状态设置为 `Ok`,因为这种情况由 `Unset` 覆盖。`Ok` 所做的是代表由用户明确设置的 Span 状态的明确的“最终调用”。这在任何开发人员希望对 Span 有除“成功”之外的任何其他解释的情况下都很有用。

重申:`Unset` 表示一个没有发生错误的 Span。`Ok` 表示开发人员明确将 Span 标记为成功。在大多数情况下,无需显式将 Span 标记为 `Ok`。

Span Kind

创建 Span 时,它会是 `Client`、`Server`、`Internal`、`Producer` 或 `Consumer` 之一。此 Span Kind 为 tracing 后端提供了关于如何组合 trace 的提示。根据 OpenTelemetry 规范,服务器 Span 的父级通常是远程客户端 Span,而客户端 Span 的子级通常是服务器 Span。类似地,消费者 Span 的父级始终是生产者,而生产者 Span 的子级始终是消费者。如果未提供,Span Kind 将默认为 Internal。

有关 SpanKind 的更多信息,请参阅 SpanKind

Client

客户端 Span 代表同步的远程出站调用,例如出站 HTTP 请求或数据库调用。请注意,在此上下文中,“同步”不是指 `async/await`,而是指它不是为稍后处理而排队的。

Server

服务器 Span 代表同步的远程入站调用,例如入站 HTTP 请求或远程过程调用。

Internal

内部 Span 表示不跨越进程边界的操作。例如,对函数调用或 Express 中间件进行仪器化可能会使用内部 Span。

Producer

生产者 Span 表示可能稍后异步处理的任务的创建。它可能是一个远程任务,例如插入到作业队列中的任务,或者由事件监听器处理的本地任务。

Consumer

消费者 Span 表示由生产者创建的任务的处理,并且可能在生产者 Span 已经结束很久之后才开始。

规范

有关更多信息,请参阅 tracing 信号


最后修改于 2025 年 10 月 9 日: Update traces.md (#8042) (1ce35dff)