You need to enable JavaScript to run this app.
导航

通过 OpenTelemetry SDK 写入数据

最近更新时间2023.11.14 20:08:14

首次发布时间2023.05.30 17:38:56

日志服务支持通过 OpenTelemetry SDK 直接写入 Trace 数据到日志服务。本文档以 Go SDK 为例,演示 Golang 应用接入 Trace 服务的操作步骤,适用于业务系统首次使用全链路追踪服务的场景。

前提条件

  • 已配置 Golang 开发环境,请参考 Go 官网下载和安装 Go 编译运行环境。
  • Go 版本为 1.19 及后续版本,您可以执行 go version 检查当前 Go 的版本信息。

背景信息

在微服务、分布式等涉及多个服务交互的架构或系统中,可以使用全链路追踪服务进行服务调用链路分析或架构性能监控。日志服务通过 Trace 服务提供基于 OpenTelemetry 标准协议的分布式链路追踪能力,您可以参考本文档,在业务系统中部署 Trace 服务,在数据源上启用 Trace 事件,采集并发送日志数据到火山引擎日志服务中。

操作步骤

1 创建 Trace 实例

在日志服务控制台中创建一个 Trace 实例,用于配合业务系统采集并发送、管理 Trace 数据。日志服务会同步创建一个日志主题用于存储 Trace 数据。详细操作步骤请参考创建 Trace 实例

2 添加相关依赖

在本地 Go 项目中执行以下命令添加对应依赖。

# 添加otel协议依赖
go get go.opentelemetry.io/otel
go get go.opentelemetry.io/otel/trace
go get go.opentelemetry.io/otel/bridge/opentracing

# 添加otel exporter的依赖(用户需要通过exporter向日志服务发送trace数据)
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace

# 添加http exporter依赖(如果配置的是日志服务的http端口,需要添加这个依赖)
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp

# 添加grpc exporter依赖(如果配置的是日志服务的grpc端口,需要添加这个依赖)
go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc

3 初始化 Provider

添加依赖后,您需要通过执行代码完成 OpenTelemetry Provider 的初始化。您需要指定要使用的数据传输协议,以便 OpenTelemetry 将数据从您的应用程序传输到 Trace 实例。在 Tracer 类的构造函数中,可以使用协议参数来指定该协议。
初始化时应配置日志服务接入点和鉴权相关参数,详细信息请参考参数说明。日志服务支持通过以下方式配置鉴权信息:

  • 在 gRPC协议、HTTPS 协议的请求头(Header)中配置。
  • 在 OpenTelemetry 协议的 Resource 字段中配置鉴权信息。

HTTP 协议初始化示例

通过 HTTP 协议方式接入 Trace 服务时,可参考以下示例代码完成 OpenTelemetry Provider 的初始化。日志服务提供两种鉴权参数的配置方式,您可以按需选择对应的示例代码进行接入。详细参数信息请参考参数说明

  • 通过 OpenTelemetry 协议的 Resource 参数配置鉴权参数:

    import (
        "context"
    
        "go.opentelemetry.io/otel/attribute"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
        "go.opentelemetry.io/otel/sdk/resource"
        "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
    )
    
    // 初始化endpoint为日志服务http open telemetry协议端口并且在trace数据的body中添加鉴权信息的TracerProvider
    func InitTlsOtelTracerWithHttpResource(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
        traceTopicKey := attribute.Key("tls.otel.tracetopic")
        akKey := attribute.Key("tls.otel.ak")
        skKey := attribute.Key("tls.otel.sk")
        regionKey := attribute.Key("tls.otel.region")
    
        opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(endPoint + ":4318")} // endpoint为http 【参数配置】
        exporter, _ := otlptrace.New(
           context.Background(),
           otlptracehttp.NewClient(opts...),
        )
    
        tp := trace.NewTracerProvider(
           trace.WithBatcher(exporter),
           trace.WithResource(resource.NewWithAttributes(
              semconv.SchemaURL,
              semconv.ServiceNameKey.String("your_service_name"),
              // 在otel请求body中添加TLS需要的鉴权字段,参考【参数配置】
              traceTopicKey.String(topicId),
              akKey.String(ak),
              skKey.String(sk),
              regionKey.String(region),
           )),
        )
        return tp
    }
    
  • 在 HTTPS 协议的请求头(Header)中配置鉴权参数:

    import (
        "context"
    
        "go.opentelemetry.io/otel/attribute"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
        "go.opentelemetry.io/otel/sdk/resource"
        "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
    )
    
    // 初始化endpoint为日志服务http open telemetry协议端口并且在http请求的hader中添加鉴权信息的TracerProvider
    func InitTlsOtelTracerWithHttpHeader(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
        // 在http header中添加TLS需要的鉴权字段,参考【参数配置】
        opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(endPoint + ":4318"), // endpoint参考【参数配置】
           otlptracehttp.WithHeaders(map[string]string{"x-tls-otel-tracetopic": topicId, "x-tls-otel-ak": ak, "x-tls-otel-sk": sk, "x-tls-otel-region": region})}
        exporter, _ := otlptrace.New(
           context.Background(),
           otlptracehttp.NewClient(opts...),
        )
    
        tp := trace.NewTracerProvider(
           trace.WithBatcher(exporter),
           trace.WithResource(resource.NewWithAttributes(
              semconv.SchemaURL,
              semconv.ServiceNameKey.String("your_service_name"),
           )),
        )
        return tp
    }
    

gRPC 协议初始化示例

通过 gRPC 协议方式接入 Trace 服务时,可参考以下示例代码完成 OpenTelemetry Provider 的初始化。日志服务提供两种鉴权参数的配置方式,您可以按需选择对应的示例代码进行接入。详细参数信息请参考参数说明

  • 通过 OpenTelemetry 协议的 Resource 参数配置鉴权参数:

    import (
        "context"
    
        "go.opentelemetry.io/otel/attribute"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
        "go.opentelemetry.io/otel/sdk/resource"
        "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "google.golang.org/grpc/credentials/insecure"
    )
    
    // 初始化endpoint为日志服务grpc open telemetry协议端口并且在trace数据的body中添加鉴权信息的TracerProvider
    func InitTlsOtelTracerWithGrpcResource(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
        exporter, _ := otlptrace.New(
           context.Background(), otlptracegrpc.NewClient(otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()), otlptracegrpc.WithEndpoint(endPoint+":4317")), // endpoint为grpc 【参数配置】
        )
        traceTopicKey := attribute.Key("tls.otel.tracetopic")
        akKey := attribute.Key("tls.otel.ak")
        skKey := attribute.Key("tls.otel.sk")
        regionKey := attribute.Key("tls.otel.region")
        tp := trace.NewTracerProvider(
           trace.WithBatcher(exporter),
           trace.WithResource(resource.NewWithAttributes(
              semconv.SchemaURL,
              semconv.ServiceNameKey.String("your_service_name"),
              // 在otel请求body中添加TLS需要的鉴权字段,参考【参数配置】
              traceTopicKey.String(topicId),
              akKey.String(ak),
              skKey.String(sk),
              regionKey.String(region),
           )),
        )
        return tp
    }
    
  • 在 gRPC 协议的请求头(Header)中配置鉴权参数:

    import (
        "context"
    
        "go.opentelemetry.io/otel/attribute"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
        "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
        "go.opentelemetry.io/otel/sdk/resource"
        "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "google.golang.org/grpc/credentials/insecure"
    )
    
    // 初始化endpoint为日志服务grpc open telemetry协议端口并且在grpc请求的hader中添加鉴权信息的TracerProvider
    func InitTlsOtelTracerWithGrpcHeader(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
        // 在grpc header中添加TLS需要的鉴权字段,参考【参数配置】
        exporter, _ := otlptrace.New(
           context.Background(),
           // otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()) 配置tls方式访问
           otlptracegrpc.NewClient(otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()), otlptracegrpc.WithEndpoint(endPoint+":4317"), // endpoint为grpc 【参数配置】
              otlptracegrpc.WithHeaders(map[string]string{"x-tls-otel-tracetopic": topicId, "x-tls-otel-ak": ak, "x-tls-otel-sk": sk, "x-tls-otel-region": region})),
        )
        tp := trace.NewTracerProvider(
           trace.WithBatcher(exporter),
           trace.WithResource(resource.NewWithAttributes(
              semconv.SchemaURL,
              semconv.ServiceNameKey.String("your_service_name"),
           )),
        )
        return tp
    }
    

参数说明

初始化 Provider 时,需要在初始化代码中配置接入点和鉴权相关的参数,参数说明如下。

  • 接入点相关参数
    endpoint 为日志服务的接入点信息,由服务地址和端口号组成,不同协议类型的端口号不同。接入点格式为 ${tls_endpoint}:端口号,其中 ${tls_endpoint} 为日志服务的服务地址,获取方式请参考服务地址

    • HTTPS 协议的接入点为 ${tls_endpoint}:4318。例如 https://tls-cn-beijing.ivolces.com:4318
    • gRPC 协议的接入点为 ${tls_endpoint}:4317,例如 https://tls-cn-beijing.ivolces.com:4317
      为保证信息传输的安全性,使用 gRPC 协议时必须开启 TLS。
  • 鉴权相关参数: 您可以在初始化时通过两种方式配置鉴权信息,即在 gRPC 协议、HTTPS 协议的 Header 中或 OpenTelemetry 协议的 Resource 字段中配置。不同配置方式的参数说明如下。

    Header key

    Resource key

    示例

    说明

    x-tls-otel-tracetopic

    tls.otel.tracetopic

    39f0bce1-e37e-4dc5-ab06-801d310b****

    Trace 实例对应的 Trace 数据日志主题 ID。详细说明请参考创建 Trace 实例

    x-tls-otel-ak

    tls.otel.ak

    AKKTNTA1OTlmOWQ1NTM5DNk5OIWwZGNmMTg0ZWUxNmU****

    火山引擎主账号或子账号的 Access Key ID。 在火山引擎控制台密钥管理页面,根据页面提示查看并复制 Access Key ID。

    x-tls-otel-sk

    tls.otel.sk

    T0RPLS5EUTFZV1JrTmpAKE5EaGCVRGsxTjJVMk5UTTBNV1UzKODJME1U****

    火山引擎主账号或子账号的 Secret Access Key。 在火山引擎控制台密钥管理页面,根据页面提示查看并复制 Secret Access Key。

    x-tls-otel-region

    tls.otel.region

    cn-beijing

    Trace 实例所在的地域 ID(Region)。您可以在服务地址中查看指定地域的 ID。

4 使用 tlsOtelTracer 发送 Trace 数据

参考以下示例代码,通过 tlsOtelTracer 发送 Trace 数据到火山引擎日志服务。详细说明请参考 OpenTelemetry 官方文档

说明

  • 在使用 OpenTelemetry 收集和发送 Trace 数据时,请确保数据的准确性和完整性。为了保证数据的可靠性,建议您在应用程序中对数据进行验证和错误处理。
  • 您也可以启用 Trace 事件,在数据源上使用 OpenTelemetry 的 Config 类设置相应的事件处理程序,自动发送 Trace 数据到日志服务。详细说明请参考 OpenTelemetry 官方文档
package main

import (
    "context"
    "fmt"
    "math/rand"
    "strconv"
    "time"

    "github.com/google/uuid"
    "github.com/opentracing/opentracing-go"
    "github.com/opentracing/opentracing-go/ext"
    "go.opentelemetry.io/otel/attribute"
    otbridge "go.opentelemetry.io/otel/bridge/opentracing"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/resource"
    "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
    "google.golang.org/grpc/credentials/insecure"
)

func main() {
    SendTrace(1000)
    time.Sleep(time.Minute)
}

func SendTrace(traceCnt int) {
    endPoint := "tls-cn-beijing.volces.com" // 别的region请更换相关域名,私网请使用私网域名
    topicId := "${topicId}"
    ak := "${AK}"
    sk := "${SK}"
    region := "cn-shanghai" // 别的region请更换相关region
    // 选择不同的方式初始化trace provider
    httpHeaderProvider := InitTlsOtelTracerWithHttpHeader(endPoint, topicId, ak, sk, region)
    // httpResourceProvider := InitTlsOtelTracerWithHttpResource(endPoint, topicId, ak, sk, region)
    // grpcHeaderProvider := InitTlsOtelTracerWithGrpcHeader(endPoint, topicId, ak, sk, region)
    // grpcResourceProvider := InitTlsOtelTracerWithGrpcResource(endPoint, topicId, ak, sk, region)

    tracer, _ := otbridge.NewTracerPair(httpHeaderProvider.Tracer("my_trace"))
    ctx := context.TODO()
    for i := 0; i < traceCnt; i++ {
       sendTrace(ctx, tracer)
    }
}

func sendTrace(ctx context.Context, tracer *otbridge.BridgeTracer) {
    var OperationName []string
    OperationName = append(OperationName, "Operation_1", "Operation_2", "Operation_3", "Operation_4", "Operation_5")
    i := 0
    var last_span opentracing.Span
    var span opentracing.Span
    for {
       uid, _ := uuid.NewUUID()
       index := strconv.Itoa(i)
       if i == 0 {
          span = spanInit(true, ctx, "test.span."+index, tracer, nil)
       } else {
          span = spanInit(false, ctx, "test.span"+index, tracer, last_span.Context())
       }
       ext.SpanKindRPCClient.Set(span)
       ext.PeerService.Set(span, "tls-test-service")
       // // // #nosec
       span.LogKV(
          "event", "soft error",
          "type", "cache timeout",
          "waited.millis", 1500)
       span.SetTag("uuid", uid.String())
       span.SetTag("host", "")
       span.SetBaggageItem("BaggageItem", "TLS test")
       span.SetOperationName(OperationName[rand.Intn(4)])
       span.FinishWithOptions(opentracing.FinishOptions{FinishTime: time.Now()})
       last_span = span
       fmt.Println("successful", i)
       i++
       if i > 100 {
          break
       }
    }
}

func spanInit(isRoot bool, context context.Context, span_name string, otTracer *otbridge.BridgeTracer, childOfSpanContext opentracing.SpanContext) opentracing.Span {
    var span opentracing.Span
    if isRoot {
       span, _ = opentracing.StartSpanFromContext(context, span_name+".root.span")
    } else {
       span = otTracer.StartSpan("TLS test", opentracing.ChildOf(childOfSpanContext))
    }
    return span
}

// 初始化endpoint为日志服务http open telemetry协议端口并且在http请求的hader中添加鉴权信息的TracerProvider
func InitTlsOtelTracerWithHttpHeader(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
    // 在http header中添加TLS需要的鉴权字段,参考【参数配置】
    opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(endPoint + ":4318"), // endpoint参考【参数配置】
       otlptracehttp.WithHeaders(map[string]string{"x-tls-otel-tracetopic": topicId, "x-tls-otel-ak": ak, "x-tls-otel-sk": sk, "x-tls-otel-region": region})}
    exporter, _ := otlptrace.New(
       context.Background(),
       otlptracehttp.NewClient(opts...),
    )

    tp := trace.NewTracerProvider(
       trace.WithBatcher(exporter),
       trace.WithResource(resource.NewWithAttributes(
          semconv.SchemaURL,
          semconv.ServiceNameKey.String("your_service_name"),
       )),
    )
    return tp
}

// 初始化endpoint为日志服务http open telemetry协议端口并且在trace数据的body中添加鉴权信息的TracerProvider
func InitTlsOtelTracerWithHttpResource(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
    traceTopicKey := attribute.Key("tls.otel.tracetopic")
    akKey := attribute.Key("tls.otel.ak")
    skKey := attribute.Key("tls.otel.sk")
    regionKey := attribute.Key("tls.otel.region")

    opts := []otlptracehttp.Option{otlptracehttp.WithEndpoint(endPoint + ":4318")} // endpoint为http 【参数配置】
    exporter, _ := otlptrace.New(
       context.Background(),
       otlptracehttp.NewClient(opts...),
    )

    tp := trace.NewTracerProvider(
       trace.WithBatcher(exporter),
       trace.WithResource(resource.NewWithAttributes(
          semconv.SchemaURL,
          semconv.ServiceNameKey.String("your_service_name"),
          // 在otel请求body中添加TLS需要的鉴权字段,参考【参数配置】
          traceTopicKey.String(topicId),
          akKey.String(ak),
          skKey.String(sk),
          regionKey.String(region),
       )),
    )
    return tp
}

// 初始化endpoint为日志服务grpc open telemetry协议端口并且在grpc请求的hader中添加鉴权信息的TracerProvider
func InitTlsOtelTracerWithGrpcHeader(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
    // 在grpc header中添加TLS需要的鉴权字段,参考【参数配置】
    exporter, _ := otlptrace.New(
       context.Background(),
       // otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()) 配置tls方式访问
       otlptracegrpc.NewClient(otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()), otlptracegrpc.WithEndpoint(endPoint+":4317"), // endpoint为grpc 【参数配置】
          otlptracegrpc.WithHeaders(map[string]string{"x-tls-otel-tracetopic": topicId, "x-tls-otel-ak": ak, "x-tls-otel-sk": sk, "x-tls-otel-region": region})),
    )
    tp := trace.NewTracerProvider(
       trace.WithBatcher(exporter),
       trace.WithResource(resource.NewWithAttributes(
          semconv.SchemaURL,
          semconv.ServiceNameKey.String("your_service_name"),
       )),
    )
    return tp
}

// 初始化endpoint为日志服务grpc open telemetry协议端口并且在trace数据的body中添加鉴权信息的TracerProvider
func InitTlsOtelTracerWithGrpcResource(endPoint string, topicId string, ak string, sk string, region string) *trace.TracerProvider {
    exporter, _ := otlptrace.New(
       context.Background(), otlptracegrpc.NewClient(otlptracegrpc.WithTLSCredentials(insecure.NewCredentials()), otlptracegrpc.WithEndpoint(endPoint+":4317")), // endpoint为grpc 【参数配置】
    )
    traceTopicKey := attribute.Key("tls.otel.tracetopic")
    akKey := attribute.Key("tls.otel.ak")
    skKey := attribute.Key("tls.otel.sk")
    regionKey := attribute.Key("tls.otel.region")
    tp := trace.NewTracerProvider(
       trace.WithBatcher(exporter),
       trace.WithResource(resource.NewWithAttributes(
          semconv.SchemaURL,
          semconv.ServiceNameKey.String("your_service_name"),
          // 在otel请求body中添加TLS需要的鉴权字段,参考【参数配置】
          traceTopicKey.String(topicId),
          akKey.String(ak),
          skKey.String(sk),
          regionKey.String(region),
       )),
    )
    return tp
}

验证接入结果

成功接入 Trace 数据后,您可以通过日志服务的检索分析功能,在线查看采集到的 Trace 数据。如果 Trace 实例对应的日志主题中可检索到您上传的 Trace 数据,表示接入成功。检索 Trace 数据的方式,请参考检索日志数据
例如,您可以通过以下 SQL 语法检索指定 TraceID 对应的 Trace 数据详情。

* | select * where TraceID = '877247fa95024d2f3a1234216010****'