OpenTelemetry 快速入门
OpenTelemetry可用于测量采集遥测数据的代码。有关更多详细信息,可以访问 OpenTelemetry
追踪(Tracing)
我们介绍如何使用OpenTelemetry API追踪代码。
首先必须获取一个Tracer,该Tracer负责创建spans和并与上下文交互。Tracer是通过使用OpenTelemetry API来获取的,该API指定了检测要监控的检查库或应用程序的库的名称和版本。可在规范章节 Obtaining a Tracer 中获得更多信息。
tracer := otel.Tracer("component-bar")
创建Span
要创建Span,只需指定Span的名称,同时还可以通过SetAttributes记录一些自定义的属性。
_, span := tracer.Start(ctx, "bar-span")
span.SetAttributes(attribute.Key("bar").String("value-bar"))
defer span.End()
创建嵌套Span
很多时候我们希望为嵌套操作关联span。OpenTelemetry支持在进程内和跨远程进程进行追踪。
func parentFunction(ctx context.Context) {
tracer := otel.Tracer("component-parent")
ctx, span := tracer.Start(ctx, "parent")
defer span.End()
// call our child function
childFunction(ctx)
}
func childFunction(ctx context.Context) {
tracer := otel.Tracer("component-child")
ctx, span := tracer.Start(ctx, "child")
defer span.End()
childFunction2(ctx)
}
Span属性
在OpenTelemetry中,可以自由创建Span,由实现者使用特定于所表示操作的属性对其进行注释。属性在Span上提供有关它追踪的特定操作的附加上下文,比如结果或操作属性。
tr := otel.Tracer("component-http")
ctx, span := tr.Start(ctx, "http", race.WithAttributes(attribute.Key("attr-http").String("hello http")))
span.SetAttributes(attribute.Key("http.method").String("GET"))
span.SetAttributes(attribute.Key("http.url").String(url))
其中一些操作表示使用众所周知的协议(如HTTP或数据库调用)的调用。 对于这些,OpenTelemetry需要设置特定的属性。完整的属性列表在跨语言规范的Semantic Conventions 中提供。
创建带事件的Span
Span 可以携带零个或多个Span属性的命名事件进行注释,每一个事件都是一个key:value键值对并自动携带相应的时间戳。
span.AddEvent("Init");
span.AddEvent("End");
指标(Metrics)
Span是获取应用程序正在执行的操作的详细信息的好方法,但是,对于更多聚合的场景哪?OpenTelemetry为指标提供支持, 一个时序数据可能表示诸如CPU利用率,HTTP服务器的请求计数或业务指标,例如交易。
所有指标都可以用标签进行注释:附加的限定有助于描述度量指标的细分。
以下是计数器用法的示例:
meter := global.Meter("ex.com/basic")
counter := metric.Must(meter).NewFloat64Counter("ex.com.three")
commonLabels := []attribute.KeyValue{lemonsKey.Int(10), attribute.String("A", "1"), attribute.String("B", "2"), attribute.String("C", "3")}
meter.RecordBatch(
ctx,
commonLabels,
valuerecorder.Measurement(2.0),
counter.Measurement(12.0),
)
采样器(Sampler)
追踪和导出应用程序中的每个用户请求并不总是可行的。 为了在可观察性和占用资源之间取得平衡,可以对追踪数据进行采样。 OpenTelemetry SDK提供了三个开箱即用的采样器:
- AlwaysSample 不管上游采样决定如何,都对每个追踪进行采样。
- NeverSample 不会对任何追踪进行采样,无论上游采样决定如何。
- TraceIDRatioBased 对上游的任何采样的追踪数据,根据配置的百分比进行采样。
可以通过实现io.opentelemetry.sdk.trace.Sampler接口来提供其他采样器。
sdktrace.WithSampler(sdktrace.AlwaysSample())
sdktrace.WithSampler(sdktrace.NeverSample())
sdktrace.WithSampler(sdktrace.TraceIDRatioBased(0.5)
Resources
Resources是一个特殊的属性类型,它能够将属性传递到当前进程中所有的span中,Resource可以记录采集的实体的信息(虚拟机、容器等),例如Metrics,如果是通过Kubernetes容器导出的,那么resource可以记录 Kubernetes集群、命名空间、Pod和容器名称等信息,还可以记录运行服务的名称、版本、实例编号。
可以放入服务名称或者实例ID之类的信息:
resources := resource.New(
label.String("service.name", "myService"),
label.String("service.version", "1.0.0"),
label.String("instance.id", "abcdef12345"),
)
provider := sdktrace.NewTracerProvider(
...
sdktrace.WithResources(resources),
)
Span处理器
OpenTelemetry提供了不同的Span处理器。SimpleSpanProcessor
立即将结束的Span转发到导出器,而BatchSpanProcessor
对它们进行批处理并批量发送它们。
sdktrace.WithSpanProcessor(sdktrace.NewSimpleSpanProcessor(...))
sdktrace.WithSpanProcessor(sdktrace.NewBatchSpanProcessor(...))
导出器(Exporter)
Span处理器由导出器初始化,该导出器负责将遥测数据发送到特定的后端。OpenTelemetry提供了多种开箱即用的导出器:
- Jaeger Exporter: 准备收集的遥测数据并将其通过gRPC发送到Jaeger后端。
- Zipkin Exporter: 准备收集的遥测数据,并通过Zipkin API将其发送到Zipkin后端。
其他导出器可以在OpenTelemetry Registry中找到。
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/trace/jaeger"
"go.opentelemetry.io/otel/exporters/metric/prometheus"
)
provider, flush, err := jaeger.NewExportPipeline(jaeger.WithCollectorEndpoint("http://s1002.lab.org:14268/api/traces"))
if err != nil {
log.Fatal(err)
}
defer flush()
otel.SetTracerProvider(provider)
//或者使用
jaeger.InstallNewPipeline()
prometheus.InstallNewPipeline(rometheus.Config{})
Demo
package main
import (
"context"
"log"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/trace/jaeger"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/semconv"
"go.opentelemetry.io/otel/trace"
)
var tracer trace.Tracer
func initTracer() func() {
// Create and install Jaeger export pipeline.
flush, err := jaeger.InstallNewPipeline(
jaeger.WithCollectorEndpoint("http://jaeger.lab.org:14268/api/traces"),
jaeger.WithSDKOptions(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(resource.NewWithAttributes(
semconv.ServiceNameKey.String("trace-demo"),
attribute.String("exporter", "jaeger"),
attribute.Float64("float", 312.23),
)),
),
)
if err != nil {
log.Fatal(err)
}
return flush
}
func main() {
ctx := context.Background()
flush := initTracer()
defer flush()
tracer = otel.Tracer("component-main")
var span trace.Span
ctx, span = tracer.Start(ctx, "helloWorld")
defer span.End()
parentFunction(ctx)
}
func parentFunction(ctx context.Context) {
tracer := otel.Tracer("component-parent")
ctx, span := tracer.Start(ctx, "parent")
defer span.End()
// call our child function
childFunction(ctx)
// do more work, when this function ends, parentSpan will complete.
}
func childFunction(ctx context.Context) {
tracer := otel.Tracer("component-child")
ctx, span := tracer.Start(ctx, "child")
defer span.End()
// do work here, when this function returns, childSpan will complete.
childFunction2(ctx)
}
func childFunction2(ctx context.Context) {
tracer := otel.Tracer("component-child2")
_, span := tracer.Start(ctx, "child2")
defer span.End()
}