1.概述
OpenTelemetry是一个开源项目,旨在为分布式系统提供一致的、标准化的方式来收集、传播和分析追踪数据。它的目标是提供一个通用的API和工具集,以支持各种编程语言和框架。
github原址;GitHub – open-telemetry/opentelemetry-go: OpenTelemetry Go API and SDK
官方地址:Getting Started | OpenTelemetry
使用jaeger,我们使用两个环境变量COLLECTOR_ZIPKIN_HTTP_PORT让它兼容zipkin,使用端口为9411,jaeger使用端口为16686,COLLECTOR_OTLP_ENABLED让它为true启用OpenTelemetry,
docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -e COLLECTOR_OTLP_ENABLED=true -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 4317:4317 -p 4318:4318 -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:1.42

这样访问
localhost:16686

查看service

点击就可以看到详情


新建一个项目,删除原有的mod文件,新建目录jaeger,mod初始化
go mod init jaeger

导入
go get go.opentelemetry.io/otel

go get go.opentelemetry.io/otel/sdk

go get go.opentelemetry.io/otel/exporters/jaeger
2.代码演示
注意一下trace是go.opentelemetry.io/otel/sdk/trace包下的,不要导错包了
package main
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/sdk/resource"
"go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
"log"
)
func main() {
//定义了一个名为jaeger的命令行参数,可以通过命令行参数-jaeger来指定Jaeger的地址。
url := "http://localhost:14268/api/traces"
provider, err := tracerProvider(url)
if err != nil {
log.Fatal(err)
}
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
defer func(ctx context.Context) {
err := provider.Shutdown(ctx)
if err != nil {
log.Fatal(err)
}
}(ctx)
otel.SetTracerProvider(provider)
tracer := provider.Tracer("dreams-main")
ctx, span := tracer.Start(ctx, "foo")
defer span.End()
bar(ctx)
}
func bar(ctx context.Context) {
tracer := otel.Tracer("dreams-bar")
_, span := tracer.Start(ctx, "bar")
defer span.End()
//业务逻辑
span.SetAttributes(attribute.Key("tanjy").String("value"))
}
func tracerProvider(url string) (*trace.TracerProvider, error) {
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
log.Fatal(err)
return nil, err
}
// 创建一个包含资源信息的 Resource 实例
r := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("trace-dreams"),
semconv.ServiceVersionKey.String("1.0.0"),
// 添加其他自定义属性
attribute.String("environment", "production"),
)
// 创建一个TracerProvider实例
provider := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(r),
)
return provider, nil
}这段代码主要实现了使用 OpenTelemetry Go SDK 将数据发送到 Jaeger,并且在代码中添加了自定义属性和资源信息。
首先,在 main 函数中,代码创建了一个 TracerProvider 实例,并设置 Jaeger exporter 和资源信息。然后,它创建了一个根 Span 并将其作为父 Span 传递给 bar 函数。在 bar 函数中,代码创建了一个子 Span 并记录一些自定义属性。在这个例子中,bar 函数只是模拟了一些业务逻辑,但您可以将自己的逻辑放进去。
在 tracerProvider 函数中,代码创建了一个 Jaeger exporter 和一个包含资源信息的 resource.Resource 实例。在创建 TracerProvider 时,将这些信息传递给它。在这个例子中,resource.Resource 实例包含有关服务的名称、版本和环境等信息。
最后,代码启动了一个根 Span 并将其传递给 bar 函数。在 bar 函数中,代码创建了一个子 Span 并记录一些自定义属性。
代码解析
首先看tracerProvider 函数
将url传入tracerProvider 函数
exporter, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
log.Fatal(err)
return nil, err
}
这段代码使用了 OpenTelemetry Jaeger exporter,创建了一个 Jaeger exporter 的实例,并指定了收集器的端点。
在这段代码中,jaeger.New 函数用于创建一个 Jaeger exporter 的实例。jaeger.WithCollectorEndpoint 函数则用于设置 Jaeger 收集器的端点。
在 WithCollectorEndpoint 函数中,您可以通过传递 WithEndpoint 选项来指定收集器的端点。WithEndpoint 函数接受一个 URL 参数,用于指定 Jaeger 收集器的地址。
然后处理异常
// 创建一个包含资源信息的 Resource 实例
r := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("trace-dreams"),
semconv.ServiceVersionKey.String("1.0.0"),
// 添加其他自定义属性
attribute.String("environment", "production"),
)
// 创建一个TracerProvider实例
provider := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(r),
)trace.NewTracerProvider: 用于创建一个新的跟踪提供者的实例。该方法接受一个或多个选项参数,用于配置提供者的行为。在这里,通过 trace.WithBatcher 和 trace.WithResource 选项来配置批处理器和资源属性。
trace.WithBatcher: 该方法用于设置批处理器,用于将跟踪数据批量发送给远程后端。在这里,传入了之前创建的 Jaeger exporter 作为批处理器。
trace.WithResource: 该方法用于设置资源属性,包括服务名称、版本以及其他自定义属性等。在这里,通过 resource.NewWithAttributes 方法设置了服务名称、版本以及一个自定义的环境属性。resource.NewWithAttributes: 用于创建一个新的资源对象,并设置属性。该方法接受一个或多个键值对作为参数,其中键表示属性名称,值表示属性值。在这里,通过 semconv.ServiceName 和 semconv.ServiceVersionKey.String 方法设置了服务名称和版本属性,并通过 attribute.String 方法添加了一个自定义的环境属性。
main函数
tracerProvider 函数返回一个TracerProvider实例到main函数
main函数如下
provider, err := tracerProvider(url)
if err != nil {
log.Fatal(err)
}接下来使用 OpenTelemetry 进行跟踪
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
defer func(ctx context.Context) {
err := provider.Shutdown(ctx)
if err != nil {
log.Fatal(err)
}
}(ctx)
otel.SetTracerProvider(provider)
tracer := provider.Tracer("dreams-main")
ctx, span := tracer.Start(ctx, "foo")
defer span.End()
bar(ctx)- ctx, cancelFunc := context.WithCancel(context.Background()): 创建一个带有取消函数的上下文,以便在需要时取消操作。
- defer cancelFunc(): 使用 defer 关键字延迟调用 cancelFunc,以确保在函数执行结束时取消上下文。
- defer func(ctx context.Context) {…}(ctx): 延迟调用一个匿名函数,并在其中执行 provider 的 Shutdown 操作。这样可以确保在函数执行结束时执行 Shutdown 操作。
- otel.SetTracerProvider(provider): 设置全局的跟踪提供者,以便在整个应用程序中都能使用该提供者进行跟踪操作。
- tracer := provider.Tracer(“dreams-main”): 通过提供者创建一个具体的跟踪器实例,这里命名为 “dreams-main”。
- ctx, span := tracer.Start(ctx, “foo”): 使用跟踪器开始一个名为 “foo” 的跟踪操作,并得到一个跟踪 span 对象,同时返回一个新的上下文。
- defer span.End(): 延迟调用 span 的 End 方法,以确保在函数执行结束时结束当前的跟踪 span。
- bar(ctx): 调用另一个函数 bar,并将上下文传递给它,以确保在该函数中也能继续进行跟踪操作。
func bar(ctx context.Context) {
tracer := otel.Tracer("dreams-bar")
_, span := tracer.Start(ctx, "bar")
defer span.End()
//业务逻辑
span.SetAttributes(attribute.Key("tanjy").String("value"))
}这段代码是一个名为 bar 的函数,它接受一个上下文对象作为参数,并使用 OpenTelemetry 进行跟踪操作。
- tracer := otel.Tracer(“dreams-bar”): 创建了一个名为 “dreams-bar” 的跟踪器实例,用于在函数内部进行跟踪操作。
- _, span := tracer.Start(ctx, “bar”): 使用创建的跟踪器开始一个名为 “bar” 的跟踪操作,并得到一个跟踪 span 对象。这将会创建一个新的子 span,该子 span 会成为当前跟踪的一部分,继承父 span 的上下文信息。
- defer span.End(): 延迟调用 span 的 End 方法,以确保在函数执行结束时结束当前的跟踪 span。
- span.SetAttributes(attribute.Key(“tanjy”).String(“value”)): 在当前的跟踪 span 上设置了一个自定义的属性,键为 “tanjy”,值为 “value”。
代码使用了 OpenTelemetry 创建了一个新的子 span,然后在子 span 中设置了一个自定义的属性。
运行,产生一个新的链路

点击Find Traces

果然出现了两个链路

点击查看,这是我们定义的名称




