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()
}

results matching ""

    No results matching ""