前提条件
获取接入点信息。
- 登录链路追踪TAS控制台。
- 在左侧导航栏单击「 链路配置 」,然后在右侧页面单击「 接入点信息 」页签。
- 打开显示Token开关。
- 在客户端采集工具区域单击 OpenTelemetry 。在下方表格的相关信息列中,获取接入点信息。
背景信息
OpenTelemetry Go SDK提供了Go语言的分布式链路追踪能力,您可以直接使用OTLP gRPC或者HTTP协议向链路追踪服务端上报数据。
使用HTTP协议上报
- 添加OpenTelemetry Go依赖。
package main import ( "context" "flag" "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/baggage" "go.opentelemetry.io/otel/exporters/otlp/otlptrace" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.17.0" "go.opentelemetry.io/otel/trace" "io" "log" "net/http" "net/http/httptrace" "time" )
- 初始化OpenTelemetry Go SDK。
func initProvider() func() { ctx := context.Background() auth := map[string]string{ "x-ctg-authorization": "xxx", // 替换成token信息 } traceClient := otlptracegrpc.NewClient( otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint("xxx:port"), //替换成otel http的上报地址。 otlptracehttp.WithURLPath("/api/traces"), otlptracegrpc.WithHeaders(auth), otlptracegrpc.WithDialOption(grpc.WithBlock())) log.Println("start to connect to server") traceExp, err := otlptrace.New(ctx, traceClient) handleErr(err, "Failed to create the collector trace exporter") res, err := resource.New(ctx, resource.WithFromEnv(), resource.WithProcess(), resource.WithTelemetrySDK(), resource.WithHost(), resource.WithAttributes( // 在可观测链路 OpenTelemetry 版后端显示的服务名称。 semconv.ServiceNameKey.String("otel-go-client-demo"), semconv.HostNameKey.String(""), ), ) handleErr(err, "failed to create resource") bsp := sdktrace.NewBatchSpanProcessor(traceExp) tracerProvider := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithResource(res), sdktrace.WithSpanProcessor(bsp), ) otel.SetTracerProvider(tracerProvider) return func() { cxt, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err := traceExp.Shutdown(cxt); err != nil { otel.Handle(err) } } }
- client端上报例子。
func sendReq(url string, ctx context.Context) { // 构造一个trace client client := http.Client{ Transport: otelhttp.NewTransport( http.DefaultTransport, otelhttp.WithClientTrace(func(ctx context.Context) *httptrace.ClientTrace { return otelhttptrace.NewClientTrace(ctx) }), ), } tr := otel.Tracer("example/client") err := func(ctx context.Context) error { ctx, span := tr.Start(ctx, "say hello", trace.WithAttributes(semconv.PeerService("ExampleService"))) defer span.End() ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx)) req, _ := http.NewRequestWithContext(ctx, "GET", url, nil) res, err := client.Do(req) if err != nil { panic(err) } _, err = io.ReadAll(res.Body) _ = res.Body.Close() return err }(ctx) if err != nil { log.Fatal(err) } } func doClient(url string, ctx context.Context) { // 不断请求数据 for { sendReq(url, ctx) time.Sleep(3 * time.Second) } } func main() { shutdown := initProvider() defer shutdown() url := flag.String("server", "http://localhost:8070/hello", "server url") flag.Parse() bag, _ := baggage.Parse("username=xxx") ctx := baggage.ContextWithBaggage(context.Background(), bag) doClient(*url, ctx) }
- 服务端上报例子。
func handler() { uk := attribute.Key("username") //加点属性 helloHandler := func(w http.ResponseWriter, req *http.Request) { ctx := req.Context() //继续调用下一个服务最终效果是client-》hello-》hello1 sendReq("http://localhost:8071/hello1", ctx) } helloHandler1 := func(w http.ResponseWriter, req *http.Request) { ctx := req.Context() span := trace.SpanFromContext(ctx) bag := baggage.FromContext(ctx) span.AddEvent("handling this...", trace.WithAttributes(uk.String(bag.Member("username").Value()))) _, _ = io.WriteString(w, "Hello, world!\n") } otelHandler := otelhttp.NewHandler(http.HandlerFunc(helloHandler), "Hello") otelHandler1 := otelhttp.NewHandler(http.HandlerFunc(helloHandler1), "Hello1") http.Handle("/hello", otelHandler) http.Handle("/hello1", otelHandler1) } func main() { flag.Parse() shutdown := initProvider() defer shutdown() handler() err := http.ListenAndServe(":" + *serverPort, nil) //nolint:gosec // Ignoring G114: Use of net/http serve function that has no support for setting timeouts. if err != nil { log.Fatal(err) } }
- 通过以上步骤,最后就在链路追踪控制台的「 应用列表 」页面选择目标应用,查看链路数据。具体例子参考https://github.com/open-telemetry/opentelemetry-go-contrib/tree/main/instrumentation/net/http/httptrace/otelhttptrace/example。
使用GRPC协议上报
- 如需使用grpc上报,只需要把上面 initProvider函数改成如下:
func initProviderGrpc() func() { ctx := context.Background() auth := map[string]string{ "x-ctg-authorization": "xxx", //接入流程里面获取token信息 } traceClient := otlptracegrpc.NewClient( otlptracegrpc.WithInsecure(), otlptracegrpc.WithEndpoint("ip:port"), //替换成grpc接入信息 otlptracegrpc.WithHeaders(auth), otlptracegrpc.WithDialOption(grpc.WithBlock())) log.Println("start to connect to server") traceExp, err := otlptrace.New(ctx, traceClient) handleErr(err, "Failed to create the collector trace exporter") res, err := resource.New(ctx, resource.WithFromEnv(), resource.WithProcess(), resource.WithTelemetrySDK(), resource.WithHost(), resource.WithAttributes( // 在可观测链路 OpenTelemetry 版后端显示的服务名称。 semconv.ServiceNameKey.String("otel-go-client-demo_provider"), semconv.HostNameKey.String(""), ), ) handleErr(err, "failed to create resource") bsp := sdktrace.NewBatchSpanProcessor(traceExp) tracerProvider := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithResource(res), sdktrace.WithSpanProcessor(bsp), ) otel.SetTracerProvider(tracerProvider) return func() { cxt, cancel := context.WithTimeout(ctx, time.Second) defer cancel() if err := traceExp.Shutdown(cxt); err != nil { otel.Handle(err) } } }