使用 OpenTelemetry 构建可观测性 02 - 埋点
这是讲解 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/
扩展阅读:
使用 OpenTelemetry 构建可观测性 02 - 埋点的更多相关文章
- Maven项目构建利器02——安装Maven核心程序
1.Maven也是用Java编写的工具,同样运行在JVM上,所以我们在安装Maven之前需要确保已经安装了JDK,首先来检查电脑上是否安装JDK. 2.如上图说明电脑中已经安装了JDK,下面就要去官网 ...
- 使用 DolphinScheduler 调度 Kylin 构建
本文章经授权转载 Apache Kylin 上游通常有复杂的数据 ETL 过程,如 Hive 入库.数据清洗等:下游有报表刷新,邮件分发等.集成 Apache DolphinScheduler 后,K ...
- ThinkPHP项目CMS内容管理系统开发视频教程【20课】(3.02GB)
ThinkPHP背景介绍: ThinkPHP是一个免费开源的,快速.简单的面向对象的轻量级PHP开发框架,遵循Apache2开源协议发布,是为了敏捷WEB应用开发和简化企业级应用开发而诞生的. ...
- [源码分析] OpenTracing之跟踪Redis
[源码分析] OpenTracing之跟踪Redis 目录 [源码分析] OpenTracing之跟踪Redis 0x00 摘要 0x01 总体逻辑 1.1 相关概念 1.2 埋点插件 1.3 总体逻 ...
- .NET静态代码织入——肉夹馍(Rougamo)
肉夹馍是什么 肉夹馍通过静态代码织入方式实现AOP的组件..NET常用的AOP有Castle DynamicProxy.AspectCore等,以上两种AOP组件都是通过运行时生成一个代理类执行AOP ...
- Grafana 系列文章(一):基于 Grafana 的全栈可观察性 Demo
️Reference: https://github.com/grafana/intro-to-mlt 这是关于 Grafana 中可观察性的三个支柱的一系列演讲的配套资源库. 它以一个自我封闭的 D ...
- OpenTelemetry - 云原生下可观测性的新标准
CNCF 简介 CNCF(Cloud Native Computing Foundation),中文为"云原生计算基金会",CNCF是Linux基金会旗下的基金会,可以理解为一个非 ...
- [小北De编程手记] [Lesson 02] AutoFramework构建 之 Page Objects - 设计模式
写在最前面 这个系列的主旨是要跟大家分享一下关于自动化测试框架的构建的一些心得.这几年,做了一些自动化测试框架以及团队的构建的工作.过程中遇到了很多这样的同学,他们在学习了某一门语言和一些自动化测试的 ...
- Python-黑客-004 用Python构建一个SSH僵尸网络-02 手动与SSH交互
用Python构建一个SSH僵尸网络-02 手动与SSH交互 - 登录SSH服务器端的 root 用户 我的电脑(攻击者)的系统:Ubuntu14.04 : 用户名: aobosir@ubuntu:~ ...
- Maven高级:01.maven分模块构建&&02.私服的应用
IntelliJ IDEA 2018.3.6 x64 07 Maven高级:01.maven分模块构建(上) 07 Maven高级:01.maven分模块构建(中) 07 Maven高级:01.mav ...
随机推荐
- KubeVela 1.5:灵活框选 CNCF 原子能力打造独特的企业应用发布平台
简介: KubeVela 1.5 于近日正式发布.在该版本中为社区带来了更多的开箱即用的应用交付能力,包括新增系统可观测:新增 Cloud Shell 终端,将 Vela CLI 搬到了浏览器:增强的 ...
- 【走进RDS】之SQL Server性能诊断案例分析
简介: 数据库性能诊断不仅对其数据库技能要求较高,而且需要大量的前期准备工作,如收集各种性能基线.性能指标和慢SQL日志等,尤其是面对多数据库性能调优时,往往事倍功半. 客户的困扰 前几天某程序员小王 ...
- Flink 在 58 同城的应用与实践
简介: 58 同城的实时 SQL 建设以及如何从 Storm 迁移至 Flink. 本文整理自 58 同城实时计算平台负责人冯海涛在 Flink Forward Asia 2020 分享的议题< ...
- dotnet C# 在 finally 抛异常会发生什么
如果我在一个方法的 finally 里面抛出异常,而在 try 里面也抛出,那在上层拿到的是什么 如下面代码 private void F1() { try { A(); } catch (Excep ...
- WPF 解决 Skia 因为找不到字体而绘制不出中文字符
在 WPF 使用 Skia 做渲染工具,如果绘制的中文都是方块,也许是字体的问题.字体的问题是 Skia 没有找到字体,本文告诉大家如何修复 在 Skia 使用特定字体,可以使用 SkiaSharp ...
- C++ Grammar Focus
一.结构体 1.基础模板 struct Node { <变量类型1> 变量名1; <变量类型2> 变量名2; ... } <定义变量名1>,<定义变量名2&g ...
- WebKit中WTFMove实现
WTFMove定义位置: WTF/Source/wtf/StdLibExtras.h,其定义如下: #define WTFMove(value) std::move<WTF::CheckMove ...
- 泊松盘采样(Poisson Disk Sampling)生成均匀随机点
当需要生成随机点且要求随机点自然均匀的分布时,使用泊松盘采样就较为适合. 但该方法与统计学上的概念关联不大,这个只相当于点在面积上服从泊松分布, 而实现这个结果有很多做法. 最终效果: 圆形为含半径的 ...
- 通过ref返回解决C# struct结构体链式调用的问题
通常结构体不能进行链式调用,因为返回值是一个新的值,需要赋回原值.但现在通过ref关键字配合扩展方法,也能进行链式调用了. 结构体: public struct Foo { public int a; ...
- C 语言编程 — 高级数据类型 — 结构体与位域
目录 文章目录 目录 前文列表 结构体 定义结构体 初始化结构体变量 访问结构体成员 将结构体作为实参传入函数 指向结构体变量的指针 位域 定义位域 使用位域结构体的成员 前文列表 <程序编译流 ...