这是解说 OpenTelemetry 系列博客的第二篇。在上一篇博客中,咱们介绍了 OpenTelemetry 是什么以及由什么组成。当初咱们将探讨如何应用 OTel 精确收集遥测数据和链路追踪数据。
手动埋点
咱们这里议论“埋点”(代码插桩),是指通过技术手段采集链路追踪数据的行为。通常有两种形式:手动和主动(上面探讨)。顾名思义,手动埋点须要在软件中显式的抉择要裸露哪些数据。
手动埋点被认为是更高级和定制的遥测办法。手动和主动埋点别离有各自的应用场景,咱们将在下文介绍。
一个申请进入零碎并通过多个后端服务时,OpenTelemetry 可能记录该申请在零碎中调用流程和通过的残缺门路,这个门路被称为 链路追踪(trace)。申请可能触发多个操作,每个操作都被记录在一个 跨度(span)中,示意具体操作的实例。
每个跨度都有一个父跨度,除非它是链路追踪中的第一个跨度,在这种状况下,其父跨度 ID 为零(造成树状构造)。
注:示例应用程序次要是用 Go 和一些 Python 编写的。我将应用 Go 语言展现代码示例,但其中原理和概念同样实用于 OTel 反对的其余编程语言。
咱们能够通过 API 将跨度增加到现有链路追踪中(或启动新链路追踪)。对于 Go 语言,这意味着咱们将援用 go.opentelemetry.io/otel
库,它蕴含了手动埋点所需的所有函数办法。咱们能够通过函数调用,应用全局链路追踪生产者来创立跨度:
import "go.opentelemetry.io/otel"
// ... other code ...
ctx, span := otel.Tracer("my-telemetry-library").Start(r.Context(), "get_user_cart")
defer span.End()
这里有几点须要留神。首先,咱们先获取全局链路追踪的实例,应用这个实例创立一个新的跨度。
咱们将在下一篇博客文章中更深刻地探讨链路追踪生产者,它是 SDK 的一个组件,负责决定和治理这些遥测数据的流向和传输方式。
链路追踪生产者既能够通过调用 otel.Tracer
也能够显式地应用参数传递。此示例应用程序依赖于全局跟踪器提供程序。当咱们调用 otel.Tracer
时,咱们传入埋点对象名称,该名称通常是解决埋点库名。在示例利用中,它被设置为“github.com/trstringer/otel-shopping-cart”。
一旦咱们失去了链路追踪生产者,就能够调用 Start
函数并向其传递两个参数:上下文对象(context,容许咱们在不同的执行环境中共享数据,并且能够跨多函数调用、申请解决或线程之间)和跨度的名称。上下文对象能够被新建(例如 context.Background()
)或从它的父上下文传递(在本例中我应用的是 HTTP 申请上下文)。跨度名称能够是任何字符串,但在这个我的项目中,应用了一种标准化的命名形式,即抉择形容标识符来命名并且应用下划线将不同标识符分隔。
Start
函数的返回值之一是上下文对象,咱们能够把它传递给代码不同执行分支或门路(例如创立子跨度),以满足那些须要应用雷同上下文的调用;而另一个返回值跨度对象,能够用来解决其余操作。
正如在此示例中所示,首先是通过 defer 关键字申明对函数 span.End
的调用,以便能够将此跨度标记为实现。咱们还能够为 span
对象增加属性。
还须要留神的是,跨度是能够被嵌套应用的。通常一个新跨度是进入了一个代码执行分支或门路并且蕴含一个父跨度。这样就造成了跨度的嵌套关系,精确地反映了申请所经验的代码调用门路。
属性
在链路追踪零碎中,咱们采集各种与零碎行为相干的数据,并将这些数据与特定的跨度进行关联,以便更好地了解零碎行为。通过利用具备多样取值的高基数数据,咱们可能获取更加具体和全面的上下文信息,从而更好地观测和剖析零碎的运行状况。
能够像上面给跨度设置属性:
span.SetAttributes(attribute.String("user.name", userName))
创立了一个名为 user.name
的字符串类型的属性并赋值。跨度的记录就会变成上面这样:
Span #4
Trace ID : d6b58718e2d607f2a881e55200b387d5
Parent ID : ef6c51753d66f227
ID : 95dcb2657f5bca91
Name : get_user_cart
Kind : SPAN_KIND_INTERNAL
Start time : 2022-08-07 16:37:51.184919236 +0000 UTC
End time : 2022-08-07 16:37:51.231164398 +0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Attributes:
-> user.name: STRING(tlasagna)
太棒了!当初名为 get_user_cart
的跨度就蕴含这个新属性 user.name
。还能够在 Jaeger 中同样看到这个属性:
事件
在许多状况下,当应用链路追踪时,您可能心愿记录一些文本或产生在跨度期间的事件。通过调用 span.AddEvent
,能够实现这一点:
span.AddEvent(
"Successfully retrieved rows from database",
trace.WithAttributes(attribute.Int("row.count", rowCount)),
)
记录的事件中还能够设置属性变量,如下例所示:
Span #1
Trace ID : 2d77674bf5bee80afcaf0df064f961ed
Parent ID : 5989852864910844
ID : f47e44dd5e23f016
Name : db_get_cart
Kind : SPAN_KIND_INTERNAL
Start time : 2022-08-07 18:37:39.167046809 +0000 UTC
End time : 2022-08-07 18:37:39.168098188 +0000 UTC
Status code : STATUS_CODE_UNSET
Status message :
Events:
SpanEvent #0
-> Name: Successfully retrieved rows from database
-> Timestamp: 2022-08-07 18:37:39.16803511 +0000 UTC
-> DroppedAttributesCount: 0
-> Attributes:
-> row.count: INT(2)
主动埋点
在后面的例子中,咱们展现了如何手动在跨度中进行埋点操作。然而,OpenTelemetry 具备一个十分弱小的个性,即反对宽泛的主动埋点。
主动埋点实用于以下状况:
- 对于 OpenTelemetry (OTel) 的老手,他们心愿可能疾速利用 OTel 收集与应用程序性能相干的指标和日志信息。
- 在现有代码库的根底上尝试集成和应用 OTel 的性能。
- 对于一些罕用的组件或服务,在对遥测数据没有特殊要求的状况下,应用默认的主动埋点机制可能主动解决数据收集。
在购物利用的示例程序中,在 Python 服务(定价服务)中应用主动埋点来解决了两个事件:
- Flask web 服务
- MySQL 连贯服务
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.mysql import MySQLInstrumentor
app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)
MySQLInstrumentor().instrument()
主动埋点的神奇之处就在于,它所须要的仅仅是启用主动埋点性能!而后,不须要任何额定的工作或编写代码,就可能取得一些对于 Flask 路由和 MySQL 查问的十分有用的数据。这是 Flask 框架主动埋点的跨度:
记录中能够看到大量与申请相干的信息,例如 http.target、net.peer.ip、http.method 等等。
MySQL 主动埋点有很多有价值的信息:
这太棒了。通过零代码开发,仅主动获取跨度,它就曾经通知我一个要害的数据:查问的持续时间。此外,还能够看到运行中的查问以及运行该查问的用户。
这些数据提供了足够的信息,用于对慢查问进行故障排除,并帮忙咱们辨认可能产生在数据库侧的意外状况。这一切都是因为一行代码启用了 MySQL 主动埋点!
总结
埋点是 OpenTelemetry 的外围。它定义了如何去收集哪些遥测数据,咱们既能够抉择手动埋点还能够利用现成的主动埋点代码库。在下一篇博文中,咱们将理解 OTel SDK 是如何解决这些数据!
本文翻译自:https://trstringer.com/otel-part2-instrumentation/
扩大浏览:
- 方法论:面向故障解决的可观测性体系建设
- 白皮书:事件 OnCall 核心建设办法
- 好工具:FlashDuty – 一站式告警解决平台:告警降噪、排班 OnCall