すべてのプロダクト
Search
ドキュメントセンター

Simple Log Service:OpenTelemetry SDK for Golangを使用して、GolangアプリケーションからSimple Log Serviceにトレースデータをインポートする

最終更新日:Sep 06, 2024

このトピックでは、OpenTelemetry SDK for Golangを使用して、GolangアプリケーションからSimple Log Serviceにトレースデータをインポートする方法について説明します。

前提条件

  • トレースインスタンスが作成されます。 詳細については、「トレースインスタンスの作成」をご参照ください。

  • Golang開発環境が設定されました。 Golangバージョンは1.13以降です。

手順

  1. OpenTelemetryプロバイダーを初期化します。

  2. 半自動モードでデータをインポートする条件が満たされているかどうかを確認します。

    • 条件が満たされている場合、トレースデータを半自動モードでインポートできます。

      半自動モードが要件を満たさない場合は、トレースデータを手動でインポートする必要があります。

    • 条件が満たされていない場合は、手動モードでトレースデータをインポートできます。

手順1: OpenTelemetryプロバイダーの初期化

Simple Log Serviceは、依存関係を構築し、依存関係をSimple Log Serviceにアップロードできるプロバイダーを提供します。 このプロバイダーは、OpenTelemetryプロバイダーの使用を簡素化するのに役立ちます。 詳細については、「opentelemetry-go-provider-sls」をご参照ください。

重要

トレースを作成してメトリックを登録する前に、OpenTelemetryプロバイダーを初期化する必要があります。

コードを実行するか、環境変数を設定して、OpenTelemetryプロバイダーを初期化できます。

  • OpenTelemetryプロバイダーを初期化するコードを実行します。

    1. 依存関係を追加します。

      module opentelemetry-golang-sample
      
      go 1.13
      
      require (
          github.com/aliyun-sls/opentelemetry-go-provider-sls v0.10.0
          go.opentelemetry.io/otel v1.16.0
          go.opentelemetry.io/otel/metric v1.16.0
          go.opentelemetry.io/otel/trace v1.16.0
      )
    2. 初期化コードを書き込みます。

      次のコードの変数を実際の値に置き換えます。 変数の詳細については、「変数」をご参照ください。

      package main
      
      import (
          "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider"
      )
      
      func main() {
      
          slsConfig, err := provider.NewConfig(provider.WithServiceName("${service}"),
              provider.WithServiceNamespace("${service.namespace}"),
              provider.WithServiceVersion("${version}"),
              provider.WithTraceExporterEndpoint("${endpoint}"),
              provider.WithMetricExporterEndpoint("${endpoint}"),
              provider.WithSLSConfig("${project}", "${instance}", "${access-key-id}", "${access-key-secret}"))
          // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. You can also use other error handling methods. 
          if err != nil {
              panic(err)
          }
          if err := provider.Start(slsConfig); err != nil {
              panic(err)
          }
          defer provider.Shutdown(slsConfig)
          
          // Add business logic code. 
          ...
      }

      表 1. 変数

      変数

      説明

      ${service}

      サービスの名前です。 ビジネス要件に基づいて値を指定します。

      payment

      ${service.nameスペース}

      サービスが属する名前空間。

      order

      ${version}

      サービスのバージョンです。 va.b.c形式でバージョンを指定することを推奨します。

      v0.1.2

      ${endpoint}

      Simple Log Serviceプロジェクトのエンドポイント。 形式: ${project}.${region-endpoint}: ポート

      • ${project}: Simple Log Serviceプロジェクトの名前。

      • ${region-endpoint}: プロジェクトが存在するリージョンのSimple Log Serviceエンドポイント。 Simple Log Serviceには、内部エンドポイントまたはパブリックエンドポイントを使用してアクセスできます。 内部エンドポイントは、クラシックネットワークまたは仮想プライベートクラウド (VPC) 経由でアクセスできます。 インターネット経由でパブリックエンドポイントにアクセスできます。 詳細については、「エンドポイント」をご参照ください。

      • Port: ポート番号。 値は10010のように固定されます。

      説明
      • 変数をstdoutに設定すると、データは標準出力に出力されます。 この場合、コード行はprovider.WithTraceExporterEndpoint("stdout") です。

      • 変数を空のままにすると、トレースデータはSimple Log Serviceにアップロードされません。

      test-project.cn-hangzhou.log.aliyuncs.com:10010

      ${project}

      Simple Log Serviceプロジェクトの名前。

      test-project

      ${instance}

      トレースインスタンスのID。 詳細については、「トレースインスタンスの作成」をご参照ください。

      テストトレース

      ${access-key-id}

      Alibaba CloudアカウントのAccessKey ID。

      Simple Log Serviceプロジェクトに対する書き込み権限のみを持つRAMユーザーのAccessKeyペアを使用することを推奨します。 AccessKey ペアは、AccessKey ID と AccessKey Secret で構成されます。 指定したプロジェクトの書き込み権限をRAMユーザーに付与する方法の詳細については、「カスタムポリシーを使用してRAMユーザーに権限を付与する」をご参照ください。 AccessKeyペアを取得する方法の詳細については、「AccessKeyペア」をご参照ください。

      なし

      ${access-key-secret}

      Alibaba CloudアカウントのAccessKeyシークレット。

      Simple Log Serviceプロジェクトに対する書き込み権限のみを持つRAMユーザーのAccessKeyペアを使用することを推奨します。

      なし

  • OpenTelemetryプロバイダーを初期化するための環境変数を設定します。

    設定方法

    環境変数

    必須

    説明

    デフォルト値

    WithServiceName

    SLS_OTEL_SERVICE_NAME

    あり

    サービスの名前。 ビジネス要件に基づいて値を指定します。

    なし

    WithServiceNamespace

    SLS_OTEL_SERVICE-NAMESPACE

    なし

    サービスが属する名前空間。

    order

    WithServiceVersion

    SLS_OTEL_SERVICE_VERSION

    あり

    サービスのバージョンです。 va.b.c形式でバージョンを指定することを推奨します。

    v0.1.0

    WithSLSConfig

    SLS_OTEL_PROJECT、SLS_OTEL_INSTANCE_ID、SLS_OTEL_ACCESS_KEY_ID、およびSLS_OTEL_ACCESS_KEY_SECRET

    なし

    Simple Log Serviceリソースに関する情報。 情報には、プロジェクトの名前、トレースインスタンスの名前、プロジェクトに対する書き込み権限のみを持つアカウントのAccessKey ID、およびアカウントのAccessKey secretが含まれます。 指定したプロジェクトの書き込み権限をRAMユーザーに付与する方法の詳細については、「カスタムポリシーを使用してRAMユーザーに権限を付与する」をご参照ください。 AccessKeyペアを取得する方法の詳細については、「AccessKeyペア」をご参照ください。

    なし

    WithTraceExporterEndpoint

    SLS_OTEL_TRACE_ENDPOINT

    なし

    Simple Log Serviceプロジェクトのエンドポイント。 形式: ${project}.${region-endpoint}: ポート

    • ${project}: Simple Log Serviceプロジェクトの名前。

    • ${region-endpoint}: プロジェクトが存在するリージョンのSimple Log Serviceエンドポイント。 Simple Log Serviceには、内部エンドポイントまたはパブリックエンドポイントを使用してアクセスできます。 内部エンドポイントは、クラシックネットワークまたはVPC経由でアクセスできます。 インターネット経由でパブリックエンドポイントにアクセスできます。 詳細については、「エンドポイント」をご参照ください。

    • Port: ポート番号。 値は10010のように固定されます。

    説明
    • 変数をstdoutに設定すると、データは標準出力に出力されます。

    • 変数を空のままにすると、トレースデータはSimple Log Serviceにアップロードされません。

    stdout

    WithTraceExporterInsecure

    SLS_OTEL_TRACE_INSECURE

    なし

    セキュアでない方法でデータを転送するかどうかを指定します。 有効な値:

    • true

    • false

    説明

    Simple Log Serviceにデータを直接転送する場合は、変数をfalseに設定する必要があります。

    false

    WithMetricExporterEndpoint

    SLS_OTEL_METRIC_ENDPOINT

    なし

    Simple Log Serviceプロジェクトのエンドポイント。 形式: ${project}.${region-endpoint}: ポート

    • ${project}: Simple Log Serviceプロジェクトの名前。

    • ${region-endpoint}: プロジェクトが存在するリージョンのSimple Log Serviceエンドポイント。 Simple Log Serviceには、内部エンドポイントまたはパブリックエンドポイントを使用してアクセスできます。 内部エンドポイントは、クラシックネットワークまたはVPC経由でアクセスできます。 インターネット経由でパブリックエンドポイントにアクセスできます。 詳細については、「エンドポイント」をご参照ください。

    • Port: ポート番号。 値は10010のように固定されます。

    説明
    • 変数をstdoutに設定すると、データは標準出力に出力されます。

    • 変数を空のままにすると、メトリックデータはSimple Log Serviceにアップロードされません。

    stdout

    WithMetricExporterInsecure

    SLS_OTEL_METRIC_INSECURE

    なし

    セキュアでない方法でデータを転送するかどうかを指定します。 有効な値:

    • true

    • false

    説明

    Simple Log Serviceにデータを直接転送する場合は、変数をfalseに設定する必要があります。

    false

    WithResourceAttributes

    なし

    なし

    環境やゾーンなどの追加のタグ情報。

    なし

    WithResource

    OTEL_RESOURCE_ATTRIBUTES

    なし

    環境やゾーンなどの追加のタグ情報。 形式: key1=value1,key2=value2

    なし

    WithMetricReportingPeriod

    SLS_OTEL_METRIC_EXPORT_PERIOD

    なし

    測定データを報告する間隔。 間隔を15秒から60秒の値に設定することを推奨します。

    30 秒

    WithErrorHandler

    なし

    なし

    エラー処理機能。 内部SDKエラーが発生した場合、システムはこの関数を呼び出します。 この関数は、WithErrorHandlerFunc関数と同等です。

    なし

    WithErrorHandlerFunc

    なし

    なし

    エラー処理機能。

    なし

    なし

    SLS_OTEL_ATTRIBUTES_ENV_KEYS

    なし

    環境やゾーンなどの追加のタグ情報。 この変数は、OTEL_RESOURCE_ATTRIBUTESに似ています。 ただし、SLS_OTEL_ATTRIBUTES_ENV_keys変数に定義されている属性キーの値は、他の環境変数から読み込まれます。

    Kubernetesクラスターでは、一部のテンプレート値を指定された環境変数にパディングするためによく使用されます。 形式: env-key-1 | env-key-2 | env-key-3

    なし

ステップ2: データのインポート

  • 半自動モード: 推奨

    OpenTelemetryは、さまざまな基本ライブラリの自動計測ソリューションを提供します。 ビジネスがこれらのライブラリに依存している場合、自動計測ソリューションを使用してデータをインポートできます。 基本ライブラリの詳細については、「インストルメンテーション」をご参照ください。

    • を使用します。データをインポートするためのNETまたはHTTPフレームワーク

      次のサンプルコードは、go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.37.0に基づいて作成されています。 詳細については、「otel-http-example」をご参照ください。

      次のコードの変数を実際の値に置き換えます。 変数の詳細については、「変数」をご参照ください。

      package main
      
      import (
          "fmt"
          "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
          "go.opentelemetry.io/otel/attribute"
          "go.opentelemetry.io/otel/metric/global"
          "io"
          "net/http"
          "time"
      
          "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider"
      
          "go.opentelemetry.io/otel/trace"
      )
      
      func main() {
      
          slsConfig, err := provider.NewConfig(provider.WithServiceName("${service}"),
              provider.WithServiceNamespace("${service.namespace}"),
              provider.WithServiceVersion("${version}"),
              provider.WithTraceExporterEndpoint("${endpoint}"),
              provider.WithMetricExporterEndpoint("${endpoint}"),
              provider.WithSLSConfig("${project}", "${instance}", "${access-key-id}", "${access-key-secret}"))
          // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. You can also use other error handling methods. 
          if err != nil {
              panic(err)
          }
          if err := provider.Start(slsConfig); err != nil {
              panic(err)
          }
          defer provider.Shutdown(slsConfig)
      
          // If you want to analyze metric data in the application, you can register the metrics. 
          labels := []attribute.KeyValue{
              attribute.String("label1", "value1"),
          }
          meter := global.Meter("aliyun.sls")
          sayDavidCount, _ := meter.Int64Counter("say_david_count")
      
          helloHandler := func(w http.ResponseWriter, req *http.Request) {
              if time.Now().Unix()%10 == 0 {
                  _, _ = io.WriteString(w, "Hello, world!\n")
              } else {
                  // If you want to record some events, you can obtain the span in the context and add events. 
                  ctx := req.Context()
                  span := trace.SpanFromContext(ctx)
                  span.AddEvent("say : Hello, I am david", trace.WithAttributes(attribute.KeyValue{
                      Key:   "label-key-1",
                      Value: attribute.StringValue("label-value-1"),
                  }))
      
                  _, _ = io.WriteString(w, "Hello, I am david!\n")
                  sayDavidCount.Add(req.Context(), 1, labels...)
              }
          }
      
          // To use the automatic instrumentation solution for otel net/http, you need to only wrap http.Handler with otelhttp.NewHandler. 
          otelHandler := otelhttp.NewHandler(http.HandlerFunc(helloHandler), "Hello")
      
          http.Handle("/hello", otelHandler)
          fmt.Println("Now listen port 8080, you can visit 127.0.0.1:8080/hello .")
          err = http.ListenAndServe(":8080", nil)
          if err != nil {
              panic(err)
          }
      }
                                  
    • Gorilla Muxフレームワークを使用したデータのインポート

      次のサンプルコードは、go.opentelemetry.io/contrib/instrumentatio n/github.com/gorilla/mux/otelmux v0.37.0に基づいて作成されています。 インターフェイスは、後のバージョンで変更される場合があります。 最新のサンプルコードの詳細については、「otel-mux-example」をご参照ください。

      次のコードの変数を実際の値に置き換えます。 変数の詳細については、「変数」をご参照ください。

      package main
      
      import (
          "context"
          "fmt"
          "go.opentelemetry.io/otel/attribute"
          "go.opentelemetry.io/otel/metric/global"
          "net/http"
      
          "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider"
      
          "github.com/gorilla/mux"
          "go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux"
          "go.opentelemetry.io/otel/trace"
      )
      
      func main() {
      
          slsConfig, err := provider.NewConfig(provider.WithServiceName("${service}"),
              provider.WithServiceNamespace("${service.namespace}"),
              provider.WithServiceVersion("${version}"),
              provider.WithTraceExporterEndpoint("${endpoint}"),
              provider.WithMetricExporterEndpoint("${endpoint}"),
              provider.WithSLSConfig("${project}", "${instance}", "${access-key-id}", "${access-key-secret}"))
          // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. You can also use other error handling methods. 
          if err != nil {
              panic(err)
          }
          if err := provider.Start(slsConfig); err != nil {
              panic(err)
          }
          defer provider.Shutdown(slsConfig)
      
          // If you want to analyze metric data in the application, you can register the metrics. 
          labels := []attribute.KeyValue{
              attribute.String("label1", "value1"),
          }
          meter := global.Meter("aliyun.sls")
          callUsersCount, _ := meter.Int64Counter("call_users_count")
      
          r := mux.NewRouter()
          r.Use(otelmux.Middleware("my-server"))
          r.HandleFunc("/users/{id:[0-9]+}", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
              vars := mux.Vars(r)
              id := vars["id"]
              callUsersCount.Add(r.Context(), 1, labels...)
              name := getUser(r.Context(), id)
              reply := fmt.Sprintf("user %s (id %s)\n", name, id)
              _, _ = w.Write(([]byte)(reply))
          }))
          http.Handle("/", r)
          fmt.Println("Now listen port 8080, you can visit 127.0.0.1:8080/users/xxx .")
          _ = http.ListenAndServe(":8080", nil)
      }
      
      func getUser(ctx context.Context, id string) string {
          if id == "123" {
              return "otelmux tester"
          }
          // If you want to record some events, you can obtain the span in the context and add events. 
          span := trace.SpanFromContext(ctx)
          span.AddEvent("unknown user id : "+id, trace.WithAttributes(attribute.KeyValue{
              Key:   "label-key-1",
              Value: attribute.StringValue("label-value-1"),
          }))
          return "unknown"
      }
                                  
  • 手動モード

    次のコードの変数を実際の値に置き換えます。 変数の詳細については、「変数」をご参照ください。

    // Copyright The AliyunSLS Authors
    //
    // Licensed under the Apache License, Version 2.0 (the "License");
    // you may not use this file except in compliance with the License.
    // You may obtain a copy of the License at
    //
    //     http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an "AS IS" BASIS,
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    // See the License for the specific language governing permissions and
    // limitations under the License.
    
    package main
    
    import (
        "context"
        "errors"
        "fmt"
        "go.opentelemetry.io/otel/attribute"
        "go.opentelemetry.io/otel/metric/global"
        "go.opentelemetry.io/otel/metric/instrument"
        "math/rand"
        "time"
    
        "github.com/aliyun-sls/opentelemetry-go-provider-sls/provider"
    
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/codes"
        "go.opentelemetry.io/otel/trace"
    )
    
    func main() {
        slsConfig, err := provider.NewConfig(provider.WithServiceName("payment"),
            provider.WithServiceVersion("v0.1.0"),
            provider.WithTraceExporterEndpoint("stdout"),
            provider.WithMetricExporterEndpoint("stdout"),
            provider.WithSLSConfig("test-project", "test-otel", "access-key-id", "access-key-secret"))
        // Invoke the panic() function. If the initialization fails, the OpenTelemetry provider exits. You can also use other error handling methods. 
        if err != nil {
            panic(err)
        }
        if err := provider.Start(slsConfig); err != nil {
            panic(err)
        }
        defer provider.Shutdown(slsConfig)
    
        mockTrace()
        mockMetrics()
    }
    
    func mockMetrics() {
        // Add the label information. 
        labels := []attribute.KeyValue{
            attribute.String("label1", "value1"),
        }
    
        meter := global.Meter("ex.com/basic")
    
        meter.Float64ObservableCounter("randval", instrument.WithFloat64Callback(func(ctx context.Context, observer instrument.Float64Observer) error {
            observer.Observe(rand.Float64(), labels...)
            return nil
        }))
    
        temperature, _ := meter.Float64Counter("temperature")
        interrupts, _ := meter.Int64Counter("interrupts")
    
        ctx := context.Background()
    
        for {
            temperature.Add(ctx, 100+10*rand.NormFloat64(), labels...)
            interrupts.Add(ctx, int64(rand.Intn(100)), labels...)
    
            time.Sleep(time.Second * time.Duration(rand.Intn(10)))
        }
    }
    
    func mockTrace() {
    
        tracer := otel.Tracer("ex.com/basic")
    
        ctx0 := context.Background()
    
        ctx1, finish1 := tracer.Start(ctx0, "foo")
        defer finish1.End()
    
        ctx2, finish2 := tracer.Start(ctx1, "bar")
        defer finish2.End()
    
        ctx3, finish3 := tracer.Start(ctx2, "baz")
        defer finish3.End()
    
        ctx := ctx3
        getSpan(ctx)
        addAttribute(ctx)
        addEvent(ctx)
        recordException(ctx)
        createChild(ctx, tracer)
    }
    
    // example of getting the current span
    // Obtain the current span. 
    func getSpan(ctx context.Context) {
        span := trace.SpanFromContext(ctx)
        fmt.Printf("current span: %v\n", span)
    }
    
    // example of adding an attribute to a span
    // Add an attribute value to the span. 
    func addAttribute(ctx context.Context) {
        span := trace.SpanFromContext(ctx)
        span.SetAttributes(attribute.KeyValue{
            Key:   "label-key-1",
            Value: attribute.StringValue("label-value-1")})
    }
    
    // example of adding an event to a span
    // Add an event to the span. 
    func addEvent(ctx context.Context) {
        span := trace.SpanFromContext(ctx)
        span.AddEvent("event1", trace.WithAttributes(
            attribute.String("event-attr1", "event-string1"),
            attribute.Int64("event-attr2", 10)))
    }
    
    // example of recording an exception
    // Record the result of the span and the error information. 
    func recordException(ctx context.Context) {
        span := trace.SpanFromContext(ctx)
        span.RecordError(errors.New("exception has occurred"))
        span.SetStatus(codes.Error, "internal error")
    }
    
    // example of creating a child span
    // Create a child span. 
    func createChild(ctx context.Context, tracer trace.Tracer) {
        // span := trace.SpanFromContext(ctx)
        _, childSpan := tracer.Start(ctx, "child")
        defer childSpan.End()
        fmt.Printf("child span: %v\n", childSpan)
    }
                        

次のステップ