OpenTelemetry
目前是推荐使用Prometheus + Grafana做Metrics存储、展示,使用Jaeger做分布式追踪的存储和展示,使用Fluentd做日志存储和展示。
不过OpenTelemetry官方目前只有Trace相关的API是稳定的。Metrics相关的API当前还不稳定,所以官方文档中Metrcis相关的部分还不太完善。Logs相关部分还未开始做,优先级最低。
基于当前的这种情况,我目前只推荐用其来做trace相关的内容,等后面稳定了之后再接入此框架来做metrics和log相关的内容。本篇当前也只是介绍其trace相关内容(基于Go版本的SDK)。
后面预计还会出五篇同类型的文章:trace-collector篇、metrics-sdk使用篇、metrics-collector篇、logs-sdk使用篇、logs-collector篇。分别以sdk和collector端的角度进行讲解。
简介
OpenTelemetry(也称为 OTel)是一个开源可观测能力框架,由一系列工具、API 和 SDK 组成,使 IT 团队能够检测、生成、收集和导出远程监测数据以进行分析和了解软件性能和行为。
OpenTelemetry的核心工作目前主要集中在3个部分:
- 规范的制定和协议的统一,规范包含数据传输、API的规范,协议的统一包含:HTTP W3C的标准支持及GRPC等框架的协议标准;
- 多语言SDK的实现和集成,用户可以使用SDK进行代码自动注入和手动埋点,同时对其他三方库(Log4j、LogBack等)进行集成支持;
- 数据收集系统的实现,当前是基于OpenCensus Service的收集系统,包括Agent和Collector。
由此可见,OpenTelemetry的自身定位很明确:数据采集和标准规范的统一,对于数据如何去使用、存储、展示、告警,官方是不涉及的。
为什么要学OpenTelemetry
- 兼容性好:合并了OpenTracing和OpenCensus,可以同时兼容二者。
- 跨平台:它提供了一个与厂商无关的实现,因此可以按照自己的需求将其发送到不同的后端进行分析,切换后端只用改很少的代码,扩展性极强。jaeger-client官方也已不再维护,并且强烈推荐使用openTelemetry。
- 简化可观测性:正如OpenTelemetry所说的"高质量的观测下要求高质量的遥测"。希望看到更多的厂商转向OpenTelemetry,因为它更方便,且仅需测试单一标准。
- 未来发展好:当前已经成为了
CNCF
的孵化项目之一
OTLP协议
opentelemetry.io/docs/refere… \
OTLP(全称 OpenTelemetry Protocol )是 OpenTelemetry 原生的遥测信号传递协议,虽然在 OpenTelemetry 的项目中组件支持了Zipkin v2或Jaeger Thrift的协议格式的实现,但是都是以第三方贡献库的形式提供的。只有 OTLP 是 OpenTelemetry 官方原生支持的格式。OTLP 的数据模型定义是基于 ProtoBuf 完成的,如果你需要实现一套可以收集 OTLP 遥测数据的后端服务,那就需要了解里面的内容,对应可以参考代码仓库:opentelemetry-proto(github.com/open-teleme…)
概念
官方文档中概念颇多,很容易绕进去,我就列了几个在使用客户端SDK(openTelemetry-Go
)时遇见最多的一些概念(collector相关后面有时间看了再出一篇),某些部分会说的比较详细,相关源码就不拷贝过来了,想看的可以去github看,这里限于篇幅就只用简单的文字把部分原理叙述一下。
Signals(信号)
每个信号由四个核心组件组成:APIs、SDKs、OpenTelemetry Protocol(OTLP)、Collector。下面是官方文档中的四种信号:
-
Traces(痕迹)
- 为我们提供了用户或应用程序发出请求时会发生什么的全局图景。
- 跟踪|开放遥测 (opentelemetry.io)
- trace可视为span的有向无环图,span之间的边定义为父子关系。一个span表示一个事务中的操作。
-
Metrics(度量/指标)
- 是在运行时捕获的有关服务的度量/指标。
-
Logs(日志)
- 日志是带有时间戳的文本记录,可以是结构化(推荐)的,也可以是非结构化的,带有元数据metadata。
-
Baggage(附加信息)
- 是指span之间传递的上下文信息
- 是添加在 metrics、log、traces 中的注解信息,键值对需要唯一,无法更改。
Tracer Provider(追踪器提供者)
-
tracerProvider初始化还包括Resource、Exporter、Sampler和SpanLimit初始化,这通常是使用OpenTelemetry进行tracing的第一步。
-
大多数应用程序中,tp初始化一次,其生命周期与运用程序的生命周期匹配。
-
初始化:NewTracerProvider()方法
-
源码探究
-
// type TracerProvider struct { mu sync.Mutex // 保证map并发安全 namedTracer map[instrumentation.Scope]*tracer // 保存创建的tracer spanProcessors atomic.Value // span处理器,可以有多个 sampler Sampler // 采集器, idGenerator IDGenerator // id生成器,用于生成traceId和spanId,目前只有一个默认的 spanLimits SpanLimits // 对span的限制配置 resource *resource.Resource // 资源,标识当前客户端 } // tp有下面五个方法,基于篇幅具体源码分析就不展开讲了,想了解细节建议去翻看源码,这里就简单说一下这五个方法主要是用来干什么。 // 通过此实现了tracer接口,返回一个tracer实例,如果已创建对应name的tracer会直接返回之前的实例 func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.Tracer {} // 注册span处理器到tp,(目前使用场景来说一个tp一个exporter,然后一个exporter一般只会配一个spanProcessor,一个exporter配置多个spanProcessor的场景暂时还未遇到,所以用的比较少。。) func (p *TracerProvider) RegisterSpanProcessor(sp SpanProcessor) {} // 注销span处理器(同上) func (p *TracerProvider) UnregisterSpanProcessor(sp SpanProcessor) {} // 如果tp使用的是异步处理span的spanProcessor,比如使用WithBatcher()方式,就可以通过调用此方法主动通知exporter导出一批span到相关collector中 func (p *TracerProvider) ForceFlush(ctx context.Context) error {} // 销毁tp func (p *TracerProvider) Shutdown(ctx context.Context) error {}
-
Tracer(追踪者)
-
tracer创建span,其中包含有关给定操作(如服务中的请求)所发生情况的详细信息。tracer是通过tracerProvider调用Tracer(name)创建的,如果之前创建过会直接返回之前创建的tracer实例。
-
span 一旦span完成,它就是不可变的并且不能再被修改。
-
span是通过tracer通过调用Start(ctx, name)方法来创建的,Start()方法会在内部调用newSpan()方法创建span,并且根据tp配置的采集器来判断该span是否要被采集。
-
attributes 属性:作为元数据应用于span的键值对,可用于聚合、过滤和分组跟踪。 可以在span创建时添加,也可在span完成之前的生命周期内的任何其他时间添加。
// setting attributes at creation... ctx, span = tracer.Start(ctx, "attributesAtCreation", trace.WithAttributes(attribute.String("hello", "world"))) // ... and after creation span.SetAttributes(attribute.Bool("isTrue", true), attribute.String("stringAttr", "hi!"))
-
event 事件:是span上的人类可读信息,表示在其生命周期内正在发生的事情。例如,假设一个函数需要访问临界区资源。可以在两点创建一个事件:一次是在我们尝试访问资源时,另一次是在我们获取互斥锁时。
span.AddEvent("Acquiring lock") mutex.Lock() span.AddEvent("Got lock, doing work...") // do stuff span.AddEvent("Unlocking") mutex.Unlock()
event 的一个有用特征是它们的时间戳显示为从跨度开始的偏移量,使您可以轻松查看它们之间经过了多少时间。 事件也可以有自己的属性。
-
state 状态:可以在 span 上设置状态,通常用于指定 span 正在跟踪的操作中存在错误。
result, err := operationThatCouldFail() if err != nil { span.SetStatus(codes.Error, "operationThatCouldFail failed") }
当前opentelemetry中spanState只有三种状态:Unset,Error,OK。 默认情况下,所有跨度的状态都是
Unset
。在极少数情况下,您可能还希望将状态设置为Ok
。不过,这通常不是必需的。 如果您有一个失败的操作并且您希望捕获它产生的错误,您可以记录该错误。result, err := operationThatCouldFail() if err != nil { span.SetStatus(codes.Error, "operationThatCouldFail failed") span.RecordError(err) }
官方强烈建议在使用
RecordError
时也将span的状态设为Error(因为RecordError
函数在调用时不会自动设置span状态为Error),除非你不希望将追踪时出现失败操作的span当作错误span。
-
Exporters(导出器)
不同的导出器使用示例github上面也有:opentelemetry-go/example at main · open-telemetry/opentelemetry-go · GitHub
traceExporter
- traceExporter将trace发送给consumer。
- 当前官方支持的consumer有jaeger、zipkin、otlp-collector、stdout(控制台、文件等标准输出),并且提供了相关exporter
- consumer可以是调试和开发时的标准输出、OpenTelemetry Collector或者其他任何开源或供应商后端。
metricExporter
- metricExporter将trace发送给consumer。
- 当前官方支持的metric exporter有Prometheus、otlp-collector、stdout(控制台、文件等),并且提供了相关exporter
- consumer可以是调试和开发时的标准输出、OpenTelemetry Collector或者其他任何开源或供应商后端。
Resource (资源)
resource 附加于某个 process 产生的所有 trace 的键值对,在初始化阶段指定并传递到 collector 中。(tracerProvider将resource与collector关联起来)
资源是一种特殊类型的属性,适用于流程生成的所有跨度。这些元数据应用于表示有关非临时进程的基础元数据 - 例如,进程的主机名或其实例 ID。
资源应在初始化时分配给跟踪器提供程序,并且创建与属性非常相似:
resources := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("myService"),
semconv.ServiceVersionKey.String("1.0.0"),
semconv.ServiceInstanceIDKey.String("abcdef12345"),
)
provider := sdktrace.NewTracerProvider(
...
sdktrace.WithResource(resources),
)
初始化Resource:
- 直接使用默认的Default()
- New()
- NewWithAttributes()
Merge(resource1,resource2)可以将两个资源合并成一个然后返回。
初始化resource的时候还可以配置检测器:
-
Detector 检测器 用于收集相关信息添加到resource中。
-
WithAttributes:将传入的0个或多个键值对添加到resource中。(最常用)
-
还可以通过实现自动检测资源。这些可能会发现有关当前正在运行的进程、运行该操作系统的操作系统、托管该操作系统实例的云提供程序或任何其他资源属性的信息。下面是官方已实现的自动检测器:
- WithFromEnv:将环境变量中的相关属性添加到要配置的resource中。(获取环境变量中key为
OTEL_RESOURCE_ATTRIBUTES
和OTEL_SERVICE_NAME
的值) 除了WithFromEnv调用的是NewSchemaless(),不用包含semconv.SchemaURL,,其他检测器底层调用生成resource的函数都会注入semconv.SchemaURL - WithHost:将host的属性添加到要配置的resource中。(主机名)
- WithTelemetrySDK:将SDK版本信息添加到resource中。(sdk的名称、使用的语言、版本)
- WithSchemaURL:设置Schema URL到resource中。
- WithOS:将所有操作系统属性添加到配置的resource中。相关属性请参见各个WithOS*函数以配置特定属性。(当前操作系统的类型和描述)
- WithProcess:将所有Process属性添加到配置的资源中。(进程的pid,可执行文件的名称和路径,命令行参数,所有者,运行时的名称、版本和描述) 注意!此选项将包含进程命令行参数。如果这些包含敏感信息,则将包含在导出的资源中。 相关属性请参见各个WithProcess*函数以配置特定属性。
- WithContainer:将所有容器属性添加到配置的资源中。(容器id) 相关属性请参见各个WithContainer*函数以配置特定属性。
也可自己自定义自动检测器,只需实现检测器Detector接口,然后使用WithDetectors()函数注册到resource中。resource会在程序启动时调用所有被注册的Dector的Detect()方法来收集信息
- WithFromEnv:将环境变量中的相关属性添加到要配置的resource中。(获取环境变量中key为
-
SpanProcessor(span处理器)
SDK的spanProcessor决定exporter什么时候发送span到collector。而collector的spanProcessor暂时还未看(猜测是决定什么时候写入后端存储)
openTelemetry-go SDK根据处理方式分为两种处理器
-
同步处理器
- 用WithSyncer(exporter)声明,表示用同步的方式将span发送到后端存储。 其适用于测试、调试或显示其他功能的示例,不建议生产环境用此处理方式。
-
异步处理器
- 用WithBatcher(exporter)声明,表示用异步的方式将span发送到后端。 生产环境建议使用
- 原理:采集到的span发送到本地缓冲区(一个队列),触发某些信号(达到批处理最大数量or批处理间隔时间)后通知exporter将一批span发送给collector。同时也可以配置相关参数:队列(缓冲区)大小(默认2048)、最大批处理数量(默认512)、批处理触发间隔时间(默认5s)、队列是否阻塞(默认false)、exporter导出span到collector的超时时间(默认30s)。详情参看源码。
流程图:
SpanLimit(对span进行限制)
tracerProvider初始化时配置
- span的AttributeValue的最大长度(默认-1无限制)
- span的Attribute最大数量(默认最多128个)
- span的事件最大数量(默认128)
- span的link最大数量(默认128)
- span每个事件的Attribute最大数量(默认128)
- span每个link的Attribute最大数量(默认128)
Sampler(采样器)
处理和导出数据|开放遥测 (opentelemetry.io)
为什么要采样?控制发送到可观测性后端的span,从而降低摄取成本。
sampling(采样)是限制系统生成的跟踪量的过程。应使用的确切采样器取决于您的特定需求,但通常应在跟踪开始时做出决定,并允许采样决策传播到其他服务。
客户端采样配置(opentelemetry-go)
配置跟踪器提供程序时,需要在跟踪器提供程序上设置采样器,如下所示:
provider := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
-
AlwaysSample
意味着始终采样每条trace,NeverSample()
则反之亦然。 -
TraceIDRatioBased
,它将根据提供给采样器的分数对一小部分迹线进行采样。因此,如果将其设置为 .5,则将对一半的跟踪进行采样。
采集原理:首先根据系数fraction
乘以2^63
来确定采集边界Bound
,然后根据traceID
(traceID是一个[16]byte)来判断该span
是采集还是不采集。
判断依据:截取traceID
第0
个到第7
个字节,然后根据大端方式
生成一个64位的数字,然后再取其高63位得到x,如果 x < Bound 则采集该span
,反之不采集。 部分源码:func TraceIDRatioBased(fraction float64) Sampler { ... return &traceIDRatioSampler{ traceIDUpperBound: uint64(fraction * (1 << 63)), description: fmt.Sprintf("TraceIDRatioBased{%g}", fraction), } } func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult {// 决定最终是否采样 psc := trace.SpanContextFromContext(p.ParentContext) x := binary.BigEndian.Uint64(p.TraceID[0:8]) >> 1 if x < ts.traceIDUpperBound {//采样 return SamplingResult{ Decision: RecordAndSample, Tracestate: psc.TraceState(), } } return SamplingResult{//丢弃 Decision: Drop, Tracestate: psc.TraceState(), } }
-
ParentBased
,其行为因传入采样决策而异。通常,这将对具有已采样父项的跨度进行采样,而不会对父项未采样的跨度进行采样。-
这样做有一个显着的优势:您始终可以获得完整的picture(描述)。
-
工作原理:对于作为跟踪树的第一个跨度(根跨度),我们决定是否对其进行采样。
该决策通过上下文传播通过此跟踪中的其余子跨度冒泡,使每个子级知道是否需要对其进行采样。
重要的是要了解这是一个复合采样器,这意味着它不会独立存在,而是让我们定义如何为每个用例进行采样。例如 – 您可以使用根采样器定义当我们没有父级时该怎么做。您可以定义当我们有一个具有不同采样器的远程父/本地父时该怎么做。
-
-
在性能方面 - 即使您决定使用 0% 采样,开销也很小,因为无论如何都会创建跨度但不会发送。这样做是为了传播上下文。
这是最受欢迎的采样器,也是官方文档推荐的采样器。生产环境下建议
TraceIDRatioBased
与ParentBased
一起使用 -
-
默认情况下(未设置时),采样器使用
ParentBased(AlwaysSample())
collector采样配置
不同的collector(opentelemetry-collector、jaeger等)提供的有不同的采样方式。
jaeger:www.jaegertracing.io/docs/1.39/s… opentelemetry-collector:
采样方式(补充)
-
基于头部的采样(jaeger-collector采用) 顾名思义,基于头部的采样意味着在跟踪开始时决定是否预先采样。
由于简单性,这是当今最常见的采样方式,但由于我们事先不知道所有内容,我们被迫做出任意决定(例如随机百分比的所有跨度进行采样),这可能会限制我们理解一切的能力。这是在OTEL发行版级别完成的。
基于头部的采样的一个缺点是,您无法决定只对有错误的跨度进行采样,因为您事先不知道这一点(采样或不采样的决定发生在错误发生之前)
-
基于尾部的采样(opentelemetry-collector实现了这种:github.com/open-teleme…) 与基于头部的采样相反,在这里,当我们已经收集数据时,我们在整个流程结束时做出决定。首先,为确保捕获所有跨度,请在 SDK 中使用默认采样器或 AlwaysOn 采样器。
这对于指标很有用,例如,当我们想要收集延迟时,我们必须知道无法提前完成的确切开始和结束时间。
此外,基于头部的缺点是基于尾部的优势 - 只能对有错误的跨度进行采样。
Context Propagation(上下文传播)
-
上下文传播是启用分布式跟踪的核心概念。通过上下文传播,无论span是在何处生成的,都可以相互关联并组合成trace。
-
我们通过两个子概念来定义上下文传播:上下文context和传播propagation。
- context是一个对象,其中包含发送和接收服务的信息,用于将一个span与另一个span相关联,并将其与整个trace相关联。
- propagation是在服务和进程之间移动上下文的机制。通过这样做,它会组装分布式跟踪。它序列化或反序列化跨度上下文,并提供要从一个服务传播到另一个服务的相关跟踪信息。我们现在有了所谓的 :跟踪上下文。
-
在开放遥测中还有其他形式的上下文。例如,一些上下文是关于跨度的W3C规范的实现,而在OpenTelemetry中,这称为
SpanContext
。
要阅读有关传播的更多信息,请参阅 go.opentelemetry.io/otel/propagation 和 go.opentelemetry.io/otel/baggage。
Propagators(目前源码中看似乎跟baggage强相关)
传播器,比如在多进程的调用中,开启传播器用于跨服务传播 spanContext。 opentelemetry.io/docs/refere…
-
类型 Propagators API 目前定义了一种
Propagator
类型:TextMapPropagator
是一种类型,它将字符串键/值对作为值注入载体并从载体中提取值。
Propagator
将来会添加二进制类型(参见#437)。 -
Carrier 载体
-
载体carrier是
Propagator
用来读取值和写入值的介质。每个特定Propagator
类型都定义了其预期的载体类型,例如字符串映射或字节数组。Inject中使用的载体预计是可变的。
-
-
Operations 操作 propagator必须定义Inject和Extract操作,以便分别向载体写入值和从载体读取值。每种propagator类型都必须定义特定的载体类型,并且可以定义其他参数。
-
Inject 注入:注入值到载体中,例如,写入到HTTP请求头。 必须的参数:
- 一个context。
- 一个carrier。例如,一个传出的消息或HTTP请求
-
Extract 提取:从传入的请求中提取值。例如,来自HTTP请求的标头。 必须的参数:
- 一个context。
- 一个carrier。例如,一个传出的消息或HTTP请求
返回一个由参数context派生出来的context,包含提取的值,它可以是spanContext,Baggage或另一个cross-cutting concern 上下文。
-
Collector(还未细看)
虽然 Collector 翻译过来叫接收,但它负责遥测数据源的接收、处理和导出三部分,提供了与供应商无关的实现。
collector 是个实体组件,有两个部署方案,Agent(与应用程序一起在本地运行的守护进程,各个 host 负责该 host 上的遥测数据)和collector(独立运行的服务,接收所有遥测数据)。
其中,Metrics、Logging、Trace、Baggage 叫Signal,以上的对象中除了Collector,其它几个由于没有具体部署对象,名词一多理解起来还是比较费劲,需要多翻官方文档。
终极目标
实现Metrics、Tracing、Logging的融合,作为APM的数据采集终极解决方案。
- Tracing:提供了一个请求从接收到处理完成整个生命周期的跟踪路径,一次请求通常过经过N个系统,因此也被称为分布式链路追踪
- Metrics:例如cpu、请求延迟、用户访问数等Counter、Gauge、Histogram指标
- Logging:传统的日志,提供精确的系统记录
这三者的组合可以形成大一统的APM解决方案:
- 基于Metrics告警发现异常 通过Tracing定位到具体的系统和方法
- 根据模块的日志最终定位到错误详情和根源
- 调整Metrics等设置,更精确的告警/发现问题
当前流程:Specification Status Summary | OpenTelemetry
兼容性
局限性
安装
OpenTelemetry 分为两部分:用于检测代码的 API 和实现 API 的 SDK。
-
API安装 要开始将 OpenTelemetry 集成到任何项目中,API 用于定义遥测数据的生成方式。要在您的应用程序中生成跟踪遥测数据,您将使用
go.opentelemetry.io/otel/trace
包中的 OpenTelemetry Trace API。-
go get go.opentelemetry.io/otel \ go.opentelemetry.io/otel/trace
-
-
SDK安装 OpenTelemetry 在其 OpenTelemetry API 的实现中被设计为模块化。OpenTelemetry Go 项目提供了一个 SDK 包,
go.opentelemetry.io/otel/sdk
它实现了这个 API 并遵守 OpenTelemetry 规范。-
go get go.opentelemetry.io/otel/sdk \ go.opentelemetry.io/otel/exporters/stdout/stdouttrace
-
使用
(可能跟概念部分内容有重叠)
客户端层面
quickStart:opentelemetry.io/docs/instru…
API
trace是一种telemetry,表示服务正在完成的工作。是处理交易的参与者之间的连接记录,通常通过客户端/服务器请求处理和其他形式的通信。
导入的包
-
创建trace
-
newCtx, span := otel.Tracer(name).Start(ctx, 一般为被检测的函数名称) defer span.End() do something...
跨度之间通过context去关联
Tracer()返回一个新的tracer,所有的tracer全部保存在一个map中(map[tracerName]*tracer),如果tracer已存在map中会直接返回之前创建的tracer
-
SDK
SDK 将来自 OpenTelemetry API 的遥测数据连接到导出器。
导入包
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
-
创建导出器(Exporter) 导出器是允许将遥测数据(telemetry data)发送到某处的包:发送到控制台(console)(这就是我们在这里所做的),或者发送到远程系统(remote system)或收集器(collector)以进行进一步分析和/或丰富。OpenTelemetry 通过其生态系统支持各种出口商,包括Jaeger、Zipkin和Prometheus等流行的开源工具。
参考官方示例:github.com/open-teleme…
-
控制台
func newExporter(w io.Writer) (trace.SpanExporter, error) { return stdouttrace.New( stdouttrace.WithWriter(w), // Use human-readable output. stdouttrace.WithPrettyPrint(), // Do not print timestamps for the demo. stdouttrace.WithoutTimestamps(), ) }
这将创建一个带有基本选项的新控制台导出器。稍后您将在配置 SDK 向其发送遥测数据时使用此功能,但首先您需要确保数据是可识别的。
-
jaeger 参考:github.com/open-teleme…
先下载依赖包:
go get go.opentelemetry.io/otel/exporters/jaeger
// url example = http://localhost:14268/api/traces func newExporterByJaeger(url string) (trace.SpanExporter, error) { return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url))) }
-
-
创建资源(Resource) 遥测数据(telemetry data)对于解决服务问题至关重要。问题是,您需要一种方法来识别数据来自哪个服务,甚至是哪个服务实例。OpenTelemetry 使用
Resource
来表示生成遥测的实体。-
// newResource returns a resource describing this application. func newResource() *resource.Resource { r, _ := resource.Merge( resource.Default(), resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("fib"), semconv.ServiceVersionKey.String("v0.1.0"), attribute.String("environment", "demo"), ), ) return r }
您希望与 SDK 处理的所有遥测数据相关联的任何信息都可以添加到返回的
Resource
. 这是通过向 注册 来完成Resource
的TracerProvider
。您现在可以创造的东西!
-
-
安装跟踪器提供者(Tracer Provider)
您已对您的应用程序进行检测以生成telemetry数据,并且您有一个导出器exporter将该数据发送到控制台collector,但它们是如何连接的?这是
TracerProvider
使用的地方。这是一个集中点,instrumentation将从中获取Tracer
,并将来自这些 Tracer 的遥测数据汇集到导出管道export pipelines
。接收数据并最终将数据传输给导出器exporter的管道称为
SpanProcessor
。一个TracerProvider
可以配置多个 span processors,但对于此示例,您只需要配置一个。使用以下内容更新您的main
功能main.go
。-
func main() { l := log.New(os.Stdout, "", 0) // Write telemetry data to a file. f, err := os.Create("traces.txt") if err != nil { l.Fatal(err) } defer f.Close() exp, err := newExporter(f) if err != nil { l.Fatal(err) } tp := trace.NewTracerProvider( trace.WithBatcher(exp), trace.WithResource(newResource()), ) defer func() { if err := tp.Shutdown(context.Background()); err != nil { l.Fatal(err) } }() otel.SetTracerProvider(tp) /* … */ }
注意
trace.NewTracerProvider()
中可规定发送span的方式是异步还是同步,当使用trace.WithSyncer()
时是同步发送;当使用trace.WithBatcher()
时是通过通道(默认长度2048)异步按批发送,每隔一段时间(默认批处理5s超时触发)发送 or 达到最大批处理长度(默认512)时把要批处理的span利用导出器导出(也可手动调用ForceFlush强制导出),当通道满了之后再存入span也有两种策略:丢弃这个span(默认)或者阻塞住直到队列有空位
-