什么是OpenTelemetry?
OpenTelemetry 是一个云原生计算基金会 (CNCF)项目,是两个先前项目OpenTracing和OpenCensus合并的结果。这两个项目都是为了解决同一个问题而创建的:缺乏如何检测代码并将遥测数据发送到可观察性后端的标准。由于两个项目都无法完全独立解决问题,因此它们合并形成了 OpenTelemetry,并结合各自的优势,同时提供单一解决方案。
- 与供应商和工具无关,这意味着它可以与各种可观察性后端一起使用
- 专注于遥测数据的生成、收集、管理和导出。 OpenTelemetry 的一个主要目标是您可以轻松地检测您的应用程序或系统,无论其语言、基础设施或运行时环境如何。遥测数据的存储和可视化有意留给其他工具。
开放遥测概念
可观测性
可观察性让您可以在不知道系统内部运作的情况下提出有关系统的问题,从而从外部了解系统。此外,它还可以让您轻松排查和处理新问题,即“未知的未知数”。它还可以帮助您回答“为什么会发生这种情况?”的问题。
要询问有关您的系统的这些问题,您的应用程序必须经过适当的检测。也就是说,应用程序代码必须发出跟踪、指标和日志等信号。当开发人员不需要添加更多工具来解决问题时,应用程序就得到了正确的工具,因为他们拥有所需的所有信息。
分布式跟踪使您可以观察请求在复杂的分布式系统中传播的过程。分布式跟踪提高了应用程序或系统运行状况的可见性,并允许您调试难以在本地重现的行为。
Logs 日志
- 日志是由服务或其他组件发出的带有时间戳的消息。
- 日志不足以跟踪代码执行,因为它们通常缺乏上下文信息,例如从哪里调用它们。
Spans 跨度
- 跨度代表一个工作或操作单元。 Span 跟踪请求所进行的特定操作,描绘出执行该操作期间发生的情况。
- Span 包含名称、与时间相关的数据、结构化日志消息和其他元数据(即属性),以提供有关其跟踪的操作的信息。
Trace 跟踪
- 分布式跟踪(通常称为跟踪)记录请求(由应用程序或最终用户发出)在多服务架构(例如微服务和无服务器应用程序)中传播时所采用的路径。
- 轨迹由一个或多个跨度组成。
- 第一个跨度代表根跨度。
上下文传播
要理解上下文传播,需要理解两个独立的概念:上下文和传播。
要了解有关上下文传播的更多信息,请参阅上下文规范。
Context 上下文
上下文是一个对象,其中包含发送和接收服务或执行单元的信息,以将一个信号与另一个信号相关联。
例如,如果服务 A 调用服务 B,则服务 A 中 ID 位于上下文中的 Span 将用作服务 B 中创建的下一个 Span 的父 Span。上下文中的跟踪 ID 将用于下一个 Span。也在服务 B 中创建了 Span,这意味着该 Span 与服务 A 中的 Span 属于同一跟踪的一部分。
Propagation 传播
传播是在服务和进程之间移动上下文的机制。它序列化或反序列化上下文对象,并提供要从一个服务传播到另一服务的相关信息。
传播通常由检测库处理,并且对用户是透明的。如果您需要手动传播上下文,可以使用Propagators API 。
OpenTelemetry 维护着几个官方传播器。默认传播器使用W3C TraceContext规范指定的标头。
信号
Traces
跟踪为我们提供了向应用程序发出请求时发生的情况的总体情况。无论您的应用程序是具有单个数据库的整体应用程序还是复杂的服务网格,跟踪对于了解请求在应用程序中采用的完整“路径”都至关重要。
让我们通过三个工作单元来探讨这个问题,用Spans表示:
以下 JSON 示例不代表特定格式,尤其不代表OTLP/JSON ,后者更加冗长。
hello
跨度:{ "name": "hello", "context": { "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2", "span_id": "0x051581bf3cb55c13" }, "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 } } ] }
这是根跨度,表示整个操作的开始和结束。请注意,它有一个表示跟踪
trace_id
字段,但没有parent_id
。这就是你知道它是根跨度的方式。hello-greetings
跨度:{ "name": "hello-greetings", "context": { "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2", "span_id": "0x5fb397be34d26b51" }, "parent_id": "0x051581bf3cb55c13", "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 封装了特定的任务,比如打招呼,它的父级是
hello
Span。请注意,它与根跨度共享相同的trace_id
,表明它是同一跟踪的一部分。此外,它还有一个与hello
span 的span_id
匹配的parent_id
。hello-salutations
跨度:{ "name": "hello-salutations", "context": { "trace_id": "0x5b8aa5a2d2c872e8321cf37308d69df2", "span_id": "0x93564f51e1abe1c2" }, "parent_id": "0x051581bf3cb55c13", "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 } } ] }
该跨度代表此跟踪中的第三个操作,与前一个操作一样,它是
hello
跨度的子级。这也使得它成为hello-greetings
跨度的兄弟。这三个 JSON 块都共享相同的
trace_id
,并且parent_id
字段表示层次结构。这使它成为一个Trace!您会注意到的另一件事是每个 Span 看起来都像一个结构化日志。那是因为它有点像!思考跟踪的一种方式是,它们是具有上下文、相关性、层次结构等的结构化日志的集合。但是,这些“结构化日志”可以来自不同的流程、服务、虚拟机、数据中心等。在。这使得跟踪能够代表任何系统的端到端视图。
Tracer Provider 追踪器提供者
Tracer Provider(有时称为
TracerProvider
)是Tracer
的工厂。在大多数应用程序中,Tracer Provider 会初始化一次,并且其生命周期与应用程序的生命周期相匹配。 Tracer Provider 初始化还包括 Resource 和 Exporter 初始化。这通常是使用 OpenTelemetry 进行跟踪的第一步。在某些语言的 SDK 中,已经为您初始化了全局 Tracer Provider。Tracer 示踪剂
Tracer 创建的跨度包含有关给定操作(例如服务中的请求)所发生情况的更多信息。跟踪器是从跟踪器提供者创建的。
Trace Exporters 追踪导出器
跟踪导出器将跟踪发送给消费者。该使用者可以是用于调试和开发时的标准输出、OpenTelemetry Collector 或您选择的任何开源或供应商后端。
Spans 跨度
跨度代表一个工作或操作单元。 Span 是 Traces 的构建块。在 OpenTelemetry 中,它们包括以下信息:
- Name
- 父跨度 ID(对于根跨度为空)
- 开始和结束时间戳
样本跨度:
{ "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 Kind
Client:客户端跨度表示同步传出远程调用,例如传出 HTTP 请求或数据库调用。请注意,在这种情况下,“同步”并不是指
async/await
,而是指它不会排队等待稍后处理。Server:服务器跨度表示同步传入远程调用,例如传入 HTTP 请求或远程过程调用。
Internal:内部跨度表示不跨越流程边界的操作。诸如检测函数调用或 Express 中间件之类的事情可能会使用内部跨度。
Producer:生产者跨度表示创建一个稍后可能会异步处理的作业。它可能是一项远程作业,例如插入作业队列的作业,也可能是由事件侦听器处理的本地作业。
Consumer:消费者跨度代表对生产者创建的作业的处理,并且可能在生产者跨度已经结束之后很久才开始。
Metrics
Metrics: 在运行时捕获的测量结果。
指标是对运行时捕获的服务的测量。捕获测量的时刻称为度量事件,它不仅包括测量本身,还包括捕获测量的时间和关联的元数据。
应用程序和请求指标是可用性和性能的重要指标。自定义指标可以深入了解可用性指标如何影响用户体验或业务。收集的数据可用于发出中断警报或触发调度决策,以根据高需求自动扩展部署。
Meter Provider
Meter Provider(有时称为
MeterProvider
)是Meter
的工厂。在大多数应用程序中,Meter Provider 会初始化一次,并且其生命周期与应用程序的生命周期相匹配。 Meter Provider 初始化还包括 Resource 和 Exporter 初始化。这通常是使用 OpenTelemetry 进行计量的第一步。在某些语言的 SDK 中,已经为您初始化了全局 Meter Provider。Meter
Meter 创建度量工具,在运行时捕获有关服务的测量结果。仪表是由仪表提供商创建的。
Metric Exporter
指标导出器将指标数据发送给消费者。该使用者可以是开发期间调试的标准输出、OpenTelemetry Collector 或您选择的任何开源或供应商后端。
Metric Instruments
在 OpenTelemetry 中,测量结果由metric instruments捕获。metric instruments的定义如下:
- Name
- Kind
- Unit (optional)
- Description (optional)
名称、单位和描述由开发人员选择或通过常见的语义约定(如请求和进程指标)定义。
instruments类型是以下之一:
- 计数器:随着时间的推移而累积的值——你可以把它想象成汽车上的里程表;它只会上涨。
- 异步计数器:与Counter相同,但每次导出时收集一次。如果您无法访问连续增量,而只能访问聚合值,则可以使用。
- UpDownCounter :随时间累积的值,但也可以再次下降。一个例子是队列长度,它会随着队列中工作项的数量而增加和减少。
- 异步 UpDownCounter :与UpDownCounter相同,但每次导出时收集一次。如果您无法访问连续更改,而只能访问聚合值(例如,当前队列大小),则可以使用该方法。
- 仪表:测量读取时的当前值。一个例子是车辆中的燃油表。仪表是异步的。
- 直方图:客户端值的聚合,例如请求延迟。如果您对值统计感兴趣,直方图是一个不错的选择。例如:有多少个请求花费的时间少于 1 秒?
Aggregation 聚合
除了度量工具之外,聚合的概念也是需要理解的一个重要概念。聚合是一种将大量测量结果组合成有关时间窗口期间发生的度量事件的精确或估计统计数据的技术。 OTLP 协议传输此类聚合指标。 OpenTelemetry API 为每个仪器提供了默认聚合,可以使用视图覆盖该默认聚合。 OpenTelemetry 项目旨在提供可视化工具和遥测后端支持的默认聚合。
与旨在捕获请求生命周期并为请求的各个部分提供上下文的请求跟踪不同,指标旨在提供聚合的统计信息。指标用例的一些示例包括:
- 报告每个协议类型的服务读取的字节总数。
- 报告读取的总字节数和每个请求的字节数。
- 报告系统调用的持续时间。
- 报告请求大小以确定趋势。
- 报告进程的 CPU 或内存使用情况。
- 报告帐户的平均余额值。
- 报告当前正在处理的活动请求。
Logs
日志是带时间戳的文本记录,可以是结构化(推荐)或非结构化,具有可选的元数据。在所有遥测信号中,日志具有最大的遗产。大多数编程语言都具有内置的日志记录功能或众所周知、广泛使用的日志记录库。
OpenTelemetry 没有定义定制的 API 或 SDK 来创建日志。相反,OpenTelemetry 日志是您从日志记录框架或基础设施组件中已有的现有日志。 OpenTelemetry SDK 和自动检测利用多个组件自动将日志与跟踪关联起来。
OpenTelemetry 对日志的支持旨在与您已有的日志完全兼容,提供用附加上下文包装这些日志的功能以及一个通用工具包,用于将日志解析和操作为跨许多不同源的通用格式。
OpenTelemetry 日志
OpenTelemetry 没有定义定制的 API 或 SDK 来创建日志。相反,OpenTelemetry 日志是您从日志记录框架或基础设施组件中已有的现有日志。 OpenTelemetry SDK 和自动检测利用多个组件自动将日志与跟踪关联起来。
OpenTelemetry 对日志的支持旨在与您已有的日志完全兼容,提供用附加上下文包装这些日志的功能以及一个通用工具包,用于将日志解析和操作为跨许多不同源的通用格式。
OpenTelemetry Collector
OpenTelemetry Collector 提供了多种处理日志的工具:
- 几个从特定的、已知的日志数据源解析日志的接收器。
filelogreceiver
,它从任何文件读取日志并提供从不同格式解析日志或使用正则表达式的功能。
- 像
transformprocessor
这样的处理器可以让你解析嵌套数据、展平嵌套结构、添加/删除/更新值等等。
- 允许您以非 OpenTelemetry 格式发出日志数据的导出器。
采用 OpenTelemetry 的第一步通常涉及将收集器部署为通用日志记录代理。
应用程序的 OpenTelemetry 日志
在应用程序中,OpenTelemetry 日志是使用任何日志记录库或内置日志记录功能创建的。当您添加自动检测或激活 SDK 时,OpenTelemetry 会自动将您的现有日志与任何活动跟踪和跨度关联起来,并用它们的 ID 包装日志正文。换句话说,OpenTelemetry 自动关联您的日志和跟踪。
结构化日志是一种文本格式遵循一致的机器可读格式的日志。对于应用程序来说,最常见的格式之一是 JSON:
{ "timestamp": "2024-08-04T12:34:56.789Z", "level": "INFO", "service": "user-authentication", "environment": "production", "message": "User login successful", "context": { "userId": "12345", "username": "johndoe", "ipAddress": "192.168.1.1", "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36" }, "transactionId": "abcd-efgh-ijkl-mnop", "duration": 200, "request": { "method": "POST", "url": "/api/v1/login", "headers": { "Content-Type": "application/json", "Accept": "application/json" }, "body": { "username": "johndoe", "password": "******" } }, "response": { "statusCode": 200, "body": { "success": true, "token": "jwt-token-here" } } }
OpenTelemetry 日志记录组件
- Log Appender / Bridge:作为应用程序开发人员,您不应直接调用Logs Bridge API ,因为它是为日志库作者提供的,用于构建日志附加器/桥。相反,您只需使用您喜欢的日志记录库并将其配置为使用能够将日志发送到 OpenTelemetry LogRecordExporter 的日志附加程序(或日志桥)。
- Logger Provider (Logs Bridge API 的一部分,仅当您是日志记录库的作者时才应使用。):Logger Provider(有时称为
LoggerProvider
)是Logger
的工厂。在大多数情况下,Logger Provider 会初始化一次,并且其生命周期与应用程序的生命周期相匹配。 Logger Provider 初始化还包括 Resource 和 Exporter 初始化。
- Logger (Logs Bridge API 的一部分,仅当您是日志记录库的作者时才应使用。):记录器创建日志记录。记录器是从日志提供程序创建的。
- Log Record Exporter:日志记录导出器将日志记录发送给消费者。该使用者可以是用于调试和开发时的标准输出、OpenTelemetry Collector 或您选择的任何开源或供应商后端。
- Log Record:日志记录代表事件的记录。在 OpenTelemetry 中,日志记录包含两种字段:
顶级字段
和属性字段
顶级字段:
Timestamp | 事件发生的时间。 |
ObservedTimestamp | 观察事件的时间。 |
TraceId | 请求跟踪 ID。 |
SpanId | 请求跨度 ID。 |
TraceFlags | W3C 跟踪标志。 |
SeverityText | 严重性文本(也称为日志级别)。 |
SeverityNumber | 严重性的数值。 |
Body | 日志记录的正文。 |
Resource | 描述日志的来源。 |
InstrumentationScope | 描述发出日志的范围。 |
Attributes | 有关该活动的其他信息。 |
Baggage
信号之间传递的上下文信息。
Baggage 通常用于跟踪以跨服务传播附加数据。
例如,假设您在请求开始时有一个
clientId
,但您希望该 ID 在跟踪中的所有跨度、另一个服务中的一些指标以及整个过程中的一些日志上可用。由于跟踪可能跨越多个服务,因此您需要某种方法来传播该数据,而无需在代码库中的许多位置复制clientId
。通过使用上下文传播在这些服务之间传递行李,
clientId
可用于添加到任何其他跨度、指标或日志。此外,仪器会自动为您传播行李。OTel 行李有何用途
Baggage 最好用于包含通常仅在下游请求开始时可用的信息。例如,这可以包括帐户标识、用户 ID、产品 ID 和源 IP 等内容
使用行李传播此信息可以在后端对遥测进行更深入的分析。例如,如果您在跟踪数据库调用的跨度上包含诸如用户 ID 之类的信息,则可以更轻松地回答诸如“哪些用户遇到最慢的数据库调用?”之类的问题。您还可以记录有关下游操作的信息,并在日志数据中包含相同的用户 ID。
Instrumentation
使用 OpenTelemetry,您可以通过两种主要方式检测代码:
基于代码的解决方案使您可以从应用程序本身获得更深入的洞察和丰富的遥测数据。它们允许您使用 OpenTelemetry API 从应用程序生成遥测数据,这是对零代码解决方案生成的遥测数据的重要补充。
零代码解决方案非常适合入门,或者当您无法修改需要从中获取遥测的应用程序时。它们从您使用的库和/或应用程序运行的环境中提供丰富的遥测数据。另一种思考方式是,它们提供有关应用程序边缘发生的情况的信息。
您可以同时使用两种解决方案(例如,项目中需要定制的需求使用基于代码的,k8s集群日志可以使用零代码)
Components
Semantic Conventions
语义约定可用于跟踪、指标、日志和资源: