上一个博文中,我提到如何使用 OpenTelemery 的特定语言 API 来收集遥测数据,包含手动和自动的埋点技术,这很重要!但是,收集遥测数据只是解决方案的第一步。

你需要把遥测数据路由转发到其他地方,同时添加额外的元数据信息。这时就轮到 SDK 发挥作用了。

链路追踪生产者( Tracer Provider )

链路追踪生产者是 SDK 中一个关键概念。用于将通过 API 收集的遥测数据与其他组件联系起来。在 Go 语言中,TracerProvider 对象只有一个 Tracer 方法的接口,方法签名如下:

Tracer(instrumentationName string, opts ...TracerOption) Tracer

Tracer 方法返回一个实现 Tracer 接口的对象,这个接口也只有一个方法 Start,其方法签名如下:

Start(ctx context.Context, spanName string, opts ...spanStartOption) (context.Context, Span)

样例项目中通过链路追踪生产者创建了跨度( span ):

import "go.opentelemetry.io/otel"

// ...

ctx, span := otel.Tracer(telemetry.TelemetryLibrary).Start(ctx, "get_product_price")

可以发现通过otel.Tracer 查找并创建全局的链路追踪生产者最终返回 Tracer 对象,需要注意要使用链路追踪生产者,其初始化设置是不可缺少的。

Note: 在文中提及是获取‘全局’链路追踪生产者的方法。使用全局链路追踪最简单的一种方式就是调用 otel.Tracer 的 API 。不过实际使用中如果上面方案不满足,还可以通过链路追踪生产者传递给消费者以替代全局查找的方法。

Note: trace 代表整个请求的路径信息、span 代表链路中的具体节点信息

资源( Resource )

链路追踪生产者还需要配置‘资源’对象,它是元数据信息的一部分。资源是遥测数据产生描述过程或者服务的信息,描述了服务本身的元数据,有助于解析遥测数据。

这是样例项目中购物车服务的‘资源’对象定义:

import (
"go.opentelemetry.io/otel/sdk/resource"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
) // ... res, err := resource.New(
ctx,
resource.WithAttributes(
semconv.ServiceNameKey.String("cart"),
semconv.ServiceVersionKey.String("v1.0.0"),
),
)

资源对象定义的关键是设置属性参数,OpenTelemetry 已经定义了一些资源属性的键值对,可以参考这篇文档 OTel’s 资源语义约定。 例如,你可以通过上面例子看到,如何定义服务名称和版本号信息。但是可能还有更多信息你需要配置,比如服务自身依赖的资源有哪些;服务运行在云上吗?需要约定不同的属性给不同的云服务供应商;服务运行在 Kubernetes 吗?是的话,这里有份指导手册 Kubernetes 的资源语义约定

最终样例项目中, 链路追踪数据中 span 都包含这样的‘资源’数据:

Resource labels:
-> service.name: STRING(cart)
-> service.version: STRING(v1.0.0)

导出器( Exporter )

既然我们已经创建了资源对象,我们接下来定义一下遥测数据的目的地。

导出器的选择范围很广,可以根据自己的需求选择不同的导出器,不过在当前项目例子中我使用 OpenTelemetry 控制器(会在下一篇细聊),它支持 HTTP 和 gRPC 协议。我选择使用 gRPC 协议和 OTLP 导出器:

import (
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"google.golang.org/grpc"
) // ... hostIP := os.Getenv("HOST_IP")
if hostIP == "" {
return nil, fmt.Errorf("unexpected no host IP address for receiver")
}
receiverAddress := fmt.Sprintf("%s:%d", hostIP, 4317) conn, err := grpc.DialContext(
ctx,
receiverAddress,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
if err != nil {
return nil, fmt.Errorf("error creating client connection to collector: %w", err)
} otlpTraceExporter, err := otlptracegrpc.New(
ctx,
otlptracegrpc.WithGRPCConn(conn),
)

Note: 文中例子是演示的程序,使用的非安全的连接方式来获取数据,不过生产环境中你最起码应该要使用带鉴权的连接方式。

就导出器而言,有多种方式输出结果渠道供你选择,例如:控制台输出(输出到 stdout ), Jaeger (直接发送数据给它), Prometheus 等。使用 OTLP 导出器并将数据发送到 OTel Collector 的好处是,您可以创建数据副本、并行处理数据,并拥有更多控制权(将在下一篇文章中介绍)。

由于使用 OTLP 导出器非常灵活,我们可以根据需要在 Collector 中使用遥测数据(输出到 stdout、发送到 Jaeger 等)。下一篇文章将详细介绍这一点!

整合( Tying it all together )

现在我们有了资源(生成遥测数据)和导出器(遥测数据的目的地),我们将它们放在一起形成链路追踪生产者:

tp := trace.NewTracerProvider(
trace.WithSampler(trace.AlwaysSample()),
trace.WithResource(res),
trace.WithSpanProcessor(trace.NewBatchSpanProcessor(otlpTraceExporter)),
)

当链路追踪生产者创建后,我们需要将其设置为全局链路追踪生产者:

import (
"go.opentelemetry.io/otel"
) // ... otel.SetTracerProvider(tp)

接下来我们需要设置‘传播’。在后续博文中,将深入讨论传播和附加数据( baggage ,整个链路中传递业务自定义 KV 属性),但现在只需要知道‘传播’可以将 OTel 链路追踪的上下文信息跨多个服务进行传递。让‘分布式’概念在‘分布式链路追踪’中实现。

import (
"go.opentelemetry.io/otel/propagation"
) // ... otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{},
propagation.Baggage{}),
)

最后,我们需要调用 TracerProvider.Shutdown 来清理并关闭跨度处理器(在例子中,我们使用批量 span 处理器,按批次将 span 数据进行聚合和批量处理,然后将完整的批处理结果发送给导出器):

defer func() {
if err := tp.Shutdown(context.Background()); err != nil {
fmt.Printf("Error shutting down tracer provider: %v", err)
os.Exit(1)
}
}()

Note: 为了可靠性和可读性,仅通过调用 defer tp.Shutdown(context.Background()) 是不够的,需要处理函数返回的一些错误。

链路追踪生产者 Python 版( Python tracer provider )

样例项目中大部分服务都用 Go 语言来编写,用 Python 写了一个服务(定价服务)。为了完整起见,以下是如何在 Python 中创建和设置类似的链路追踪生产者的例子:

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource, SERVICE_NAME, SERVICE_VERSION
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor resource = Resource(attributes={
SERVICE_NAME: "price",
SERVICE_VERSION: "v1.0.0"
})
tracer_provider = TracerProvider(resource=resource) host_ip = os.environ.get("HOST_IP")
if host_ip is None:
print("Must pass in environment var HOST_IP")
sys.exit(1) tracer_provider.add_span_processor(span_processor=BatchSpanProcessor(
OTLPSpanExporter(endpoint=f"{host_ip}:4317", insecure=True)
))
trace.set_tracer_provider(tracer_provider)

其中资源、span 处理器和设置全局链路追踪生产者的实现与 Go 描述相同。

总结

很棒前进了一步!按照上面步骤实现了,通过 API 获取了遥测数据,并将其从当前组件中被发送到一个导出器,并向其中添加了一些元数据(资源)!接下来我们将了解如何使用 OpenTelemetry 收集器来处理这来数据。

本文翻译自:https://trstringer.com/otel-part3-export/

扩展阅读:

使用 OpenTelemetry 构建可观测性 03 - 导出的更多相关文章

  1. Maven项目构建利器03——第一个Maven工程

    1.Maven工程的结构 我们需要通过Maven进行自动化构建, 以编译为例, Maven要想自动进行编译, 那么它必须知道Java源文件保存在哪里,所以要遵守Maven的约定,也就是约定大于配置,配 ...

  2. CommonJS的模块规范

    CommonJS对模块的定义十分简单,主要分为模块引用.模块定义和模块标识. 1.模块引用 var math = require('math');//这个方法接受模块标识,以此引入一个模块的API到当 ...

  3. 【转】Private Libraries、Referenced Libraries、Dependency Libraries的区别

    一.v4.v7.v13的作用和用法 1.Android Support V4, V7, V13是什么? 本质上就是三个java library. 2.为什么要有support库?   是为了解决软件的 ...

  4. Android Studio创建JAR/AAR库

    [时间:2017-09] [状态:Open] [关键词:Android,Android Studio,gradle,jar,aar,library] 0 引言 最近在工作中遇到了升级Android S ...

  5. iOS | 使用HBuilder进行云端打包步骤

    1.先在HBuilder本地项目中的Manifest.json文件中进行项目配置,将应用的图标和启动图,按照固定的尺寸进行配置.设置应用名称,版本号, 这里的appid不需要修改,是HBuilder自 ...

  6. docker 知识点汇总

    目录 什么是 Docker Docker 简介 Docker 的特点 如何使用 Docker 镜像的常用操作 容器的常用操作 Docker 命令汇总 手工制作 java 镜像 使用 Dockerfil ...

  7. [转]Private Libraries、Referenced Libraries、Dependency Libraries的区别

    一.v4.v7.v13的作用和用法 1.Android Support V4, V7, V13是什么? 本质上就是三个java library. 2.为什么要有support库?   是为了解决软件的 ...

  8. gRPC官方文档(异步基础: C++)

    文章来自gRPC 官方文档中文版 异步基础: C++ 本教程介绍如何使用 C++ 的 gRPC 异步/非阻塞 API 去实现简单的服务器和客户端.假设你已经熟悉实现同步 gRPC 代码,如gRPC 基 ...

  9. 【转】你是不是也被Android Private Libraries、Referenced Libraries、android Dependency搞晕了~~

    一.v4.v7.v13的作用和用法 1.Android Support V4, V7, V13是什么? 本质上就是三个java library. 2.为什么要有support库?   是为了解决软件的 ...

  10. 【带着canvas去流浪(14)】Three.js中凹浮雕模型的生成方式

    目录 一. 方案1:ThreeBSP.js或ThreeCSG.js扩展库 二. 方案2:平面镂空模型拉伸 三. 方案3:Cinema 4D建模后输出模型文件 示例代码托管在:http://www.gi ...

随机推荐

  1. App隐私合规“免费”自动化检测

    简介: App隐私合规检测提供了全面的隐私合规检测报告和专家建议,从确保形式合规(隐私政策文本合规性)及实质合规(代码层合规性)的一致性,从个人信息收集.权限使用场景.超范围采集.隐私政策.三方SDK ...

  2. 业内首款云原生技术中台产品云原生 Stack 来了!

    简介: 云原生 Stack 满足了各种典型场景下客户对于线下高集成平台的诉求,让企业数字话转型不受技术约束,专注业务本身,加速企业数字化迭代. 今天,企业数字化转型依然面临很大的挑战,虽然有很多新技术 ...

  3. [Contract] Solidity 多种访问控制 (Access Control) 实现方式

    在 solidity 中控制访问,一般是通过 modifier 修饰符方法来直接做. 那么对于稍复杂的多种访问控制,通常需要一个统一操作的模块化类库. 现在已经有了这样的类库存在,我们通过一个实现功能 ...

  4. 2019-9-19-dotnet-找不到-PostAsJsonAsync-方法

    title author date CreateTime categories dotnet 找不到 PostAsJsonAsync 方法 lindexi 2019-09-19 14:53:58 +0 ...

  5. 第一讲 Cadence-于真博士课程简介

    第一讲 Cadence-于真博士课程简介 以工程化的思想来学习本课程,比较有效,从一个DSP开发板设计为例开始,做出PCB板,顺带学会Cadence软件操作,而不是仅仅学习软件. 前期准备工作,阅读配 ...

  6. Tomcat+web测试环境配置

    一.JDK安装完成 二.Tomcat安装配置 1:官网下载tomact,地址为:https://tomcat.apache.org/download-80.cgi 2:将下载后的文件解压 3:进入To ...

  7. 8、shell 脚本

    1.shell 脚本基础 作用:介于系统内核与用户之间,负责解释命令行 1.1.第一个 shell 脚本 vim first.sh cd /boot/ pwd ls -lh vml*z 赋予可执行权限 ...

  8. 如何在低代码平台中引用 JavaScript ?

    引言 在当今快速发展的数字化时代,企业对业务应用的需求日益复杂且多元.低代码开发平台作为一个创新的解决方案,以直观易用的设计理念,打破了传统的编程壁垒,让非技术人员也能轻松构建功能完备的Web应用程序 ...

  9. golang基础之结构体

    匿名结构体 在定义一些临时数据结构等场景下还可以使用匿名结构体. 在函数体内 package main import ( "fmt" ) func main() { //方法一 v ...

  10. 案例-java贪吃蛇(附源码)

    创建屏幕 开始游戏的窗口,首先引入窗口,然后在窗口画布上进行添加各类动画. JFrame frame=new JFrame("My SnakeGame"); Jframe 是个类, ...