Featured image of post OpenTelemetry入门

OpenTelemetry入门

今天早些时候,OpenTelemetry正式进入Beta版本阶段,这标志着OpenTelemetry的基本模型已经正式确定,可以开始将OpenTelemetry集成到应用程序和客户端库中,以捕获应用程序级指标和分布式跟踪。

OpenTelemetry介绍

本章节主要介绍什么是OpenTelemetry,如果你已经了解,则可以跳过本节。

对于软件而言,可观测性是非常重要的指标之一。OpenTelemetry项目提供了一组特定于语言的API,SDK,代理和其他组件,可用于从应用程序中收集分布式跟踪,metrics和相关应用Metadata。借助OpenTelemetry,开发人员几乎可以从零开始从应用程序中捕获关键的可观察性数据。OpenTelemetry不仅仅是可以作为分布式追踪工具,也可以作为metrics收集的相关工具。支持的后端存储包括:Prometheus,Jaeger,Zipkin,Azure Monitor,Dynatrace,Google Cloud Monitoring + Trace,Honeycomb,Lightstep,New Relic和Splunk等。

OpenTelemetry项目是作为OpenTracing和OpenCensus的融合项目,进入CNCF sandbox项目。对于OpenTelemetry项目的一个目标就是,兼容OpenTracing和OpenCensus。

OpenTelemetry实战

我们这里演示最常用的追踪功能开始演示。OpenTelemetry支持多种后端,这里为了简化功能演示,我们最开始选择最为简单的标准输出作为后端输出,例子均采用Go语言实现。

假设我们已创建一个名叫demo的基于Go Module的项目。接下来创建对应的main.go:

package main

import (
	context
	log

	go.opentelemetry.io/otel/api/global
	go.opentelemetry.io/otel/exporters/trace/stdout
	sdktrace go.opentelemetry.io/otel/sdk/trace
)

func initTracer() {
	exporter, err := stdout.NewExporter(stdout.Options{PrettyPrint: true})
	if err != nil {
		log.Fatal(err)
	}
	tp, err := sdktrace.NewProvider(sdktrace.WithConfig(sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
		sdktrace.WithSyncer(exporter))
	if err != nil {
		log.Fatal(err)
	}
	// 设置全局
	global.SetTraceProvider(tp)
}

func main() {
	initTracer()
	tracer := global.Tracer(4async.com/demo)

	tracer.WithSpan(context.Background(), foo,
		func(ctx context.Context) error {
			tracer.WithSpan(ctx, bar,
				func(ctx context.Context) error {
					tracer.WithSpan(ctx, baz,
						func(ctx context.Context) error {
							return nil
						},
					)
					return nil
				},
			)
			return nil
		},
	)
}

运行上面程序之后,我们可以获取到标准输出中输出了对应的tracing信息:

{
        "SpanContext": {
                "TraceID": "9495716100dbdd51e77edda2d39d8f28",
                "SpanID": "c9e68a06fbe116e0",
                "TraceFlags": 1
        },
        "ParentSpanID": "e3e88597058566b3",
        "SpanKind": 1,
        "Name": "baz",
        "StartTime": "2020-03-31T10:53:55.497818+08:00",
        "EndTime": "2020-03-31T10:53:55.497821142+08:00",
        "Attributes": null,
        "MessageEvents": null,
        "Links": null,
        "StatusCode": 0,
        "StatusMessage": "",
        "HasRemoteParent": false,
        "DroppedAttributeCount": 0,
        "DroppedMessageEventCount": 0,
        "DroppedLinkCount": 0,
        "ChildSpanCount": 0,
        "Resource": null
}
{
        "SpanContext": {
                "TraceID": "9495716100dbdd51e77edda2d39d8f28",
                "SpanID": "e3e88597058566b3",
                "TraceFlags": 1
        },
        "ParentSpanID": "db446b6a4e579c04",
        "SpanKind": 1,
        "Name": "bar",
        "StartTime": "2020-03-31T10:53:55.497817+08:00",
        "EndTime": "2020-03-31T10:53:55.498188044+08:00",
        "Attributes": null,
        "MessageEvents": null,
        "Links": null,
        "StatusCode": 0,
        "StatusMessage": "",
        "HasRemoteParent": false,
        "DroppedAttributeCount": 0,
        "DroppedMessageEventCount": 0,
        "DroppedLinkCount": 0,
        "ChildSpanCount": 1,
        "Resource": null
}
{
        "SpanContext": {
                "TraceID": "9495716100dbdd51e77edda2d39d8f28",
                "SpanID": "db446b6a4e579c04",
                "TraceFlags": 1
        },
        "ParentSpanID": "0000000000000000",
        "SpanKind": 1,
        "Name": "foo",
        "StartTime": "2020-03-31T10:53:55.497813+08:00",
        "EndTime": "2020-03-31T10:53:55.498301313+08:00",
        "Attributes": null,
        "MessageEvents": null,
        "Links": null,
        "StatusCode": 0,
        "StatusMessage": "",
        "HasRemoteParent": false,
        "DroppedAttributeCount": 0,
        "DroppedMessageEventCount": 0,
        "DroppedLinkCount": 0,
        "ChildSpanCount": 1,
        "Resource": null
}

这里我们借助TraceIDSpanIDParentSpanID就可以快速梳理调用链路。

当然,实际上在实际项目中不可能使用这种方式进行追踪,一般来说我们会有专门的系统存储和展示追踪链路信息。因此可以通过修改上面程序中对应的go.opentelemetry.io/otel/exporters/trace/stdout内容快速替换成你需要的对应的后端存储,比如我们在线上使用的Jaeger后端,则引用go.opentelemetry.io/otel/exporters/trace/jaeger,修改initTracer()方法快速替换:

// initTracer creates a new trace provider instance and registers it as global trace provider.
func initTracer() func() {
	// Create and install Jaeger export pipeline
	_, flush, err := jaeger.NewExportPipeline(
		jaeger.WithCollectorEndpoint(http://localhost:14268/api/traces”),
		jaeger.WithProcess(jaeger.Process{
			ServiceName: trace-demo,
			Tags: []core.KeyValue{
				key.String(exporter, jaeger),
				key.Float64(float, 312.23),
			},
		}),
		jaeger.RegisterAsGlobal(),
		jaeger.WithSDK(&sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}),
	)
	if err != nil {
		log.Fatal(err)
	}

	return func() {
		flush()
	}
}

至于其他后端支持,你可以从pkg.go.dev中获取到所有的支持后端列表。

Built with Hugo
主题 StackJimmy 设计