以OpenTelemetry方式接入
 
                  更新时间 2024-10-11 14:36:42
                 
 
                    最近更新时间: 2024-10-11 14:36:42
                  
 在监控Go应用之前,您需要通过客户端将应用数据上报至APM服务端。本文介绍如何通过OpenTelemetry Go SDK上报Go应用数据。
 前提条件
完成vpce接入。
背景信息
OpenTelemetry Go SDK提供了Go语言的分布式链路追踪能力,您可以直接使用OTLP gRPC或者HTTP协议向APM服务端上报数据。
接入步骤
- 添加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"
)
- 查看接入点信息。
应用列表的接入指引会根据您所在资源池提供“通过 HTTP 上报数据”和“通过 gRPC 上报数据”的ENDPOINT(天翼云vpc网络接入点)、鉴权TOKEN信息。
- 初始化OpenTelemetry Go SDK。
(注意需将token和endpoint替换成相应地域的接入点信息。)
注意需将token和endpoint替换成相应地域的接入点信息。
方式1:使用HTTP协议上报数据。
func initProvider() func() {
  ctx := context.Background()
  auth := map[string]string{
      "x-ctg-authorization": "<token>", // 替换成token信息
  }
  path := flag.String("path", "/v1/traces", "path")
  traceClient := otlptracehttp.NewClient(
      otlptracehttp.WithEndpoint("<endpoint>"), // 替换成otel http的上报地址。
      otlptracehttp.WithURLPath(*path),
      otlptracehttp.WithHeaders(auth),
      otlptracehttp.WithInsecure())
  otlptracehttp.WithCompression(1)
  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(
            // 在APM控制台显示的服务名称。
            semconv.ServiceNameKey.String("otel-go-client-demo"),
            // 在APM控制台显示的主机名称。
            semconv.HostNameKey.String("your-host-name"),
            // 在APM控制台显示的服务端地址。
            semconv.NetSockHostAddr("service_addr"),
            // 在APM控制台显示的客户端地址。
            semconv.NetSockPeerAddr("client_addr"),
      ),
  )
  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)
      }
  }
}
方式2:使用gRPC协议上报数据。
func initProviderGrpc() func() {
  ctx := context.Background()
  auth := map[string]string{
      "x-ctg-authorization": "<token>", //接入流程里面获取token信息
  }
  traceClient := otlptracegrpc.NewClient(
      otlptracegrpc.WithInsecure(),
      otlptracegrpc.WithEndpoint("<endpoint>"),  //替换成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(
            // 在APM控制台显示的服务名称。
            semconv.ServiceNameKey.String("otel-go-client-demo_provider"),
            // 在APM控制台显示的主机名称。
            semconv.HostNameKey.String("your-host-name"),
            // 在APM控制台显示的服务端地址。
            semconv.NetSockHostAddr("service_addr"),
            // 在APM控制台显示的客户端地址。
            semconv.NetSockPeerAddr("client_addr"),
      ),
  )
  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)
    }
}
- 通过以上步骤,最后就在APM控制台的应用列表页面选择目标应用,查看监控数据。
