このトピックでは、OpenTelemetry SDK for Golangを使用して、GolangアプリケーションからSimple Log Serviceにトレースデータをインポートする方法について説明します。
前提条件
トレースインスタンスが作成されます。 詳細については、「トレースインスタンスの作成」をご参照ください。
Golang開発環境が設定されました。 Golangバージョンは1.13以降です。
手順
OpenTelemetryプロバイダーを初期化します。
半自動モードでデータをインポートする条件が満たされているかどうかを確認します。
条件が満たされている場合、トレースデータを半自動モードでインポートできます。
半自動モードが要件を満たさない場合は、トレースデータを手動でインポートする必要があります。
条件が満たされていない場合は、手動モードでトレースデータをインポートできます。
手順1: OpenTelemetryプロバイダーの初期化
Simple Log Serviceは、依存関係を構築し、依存関係をSimple Log Serviceにアップロードできるプロバイダーを提供します。 このプロバイダーは、OpenTelemetryプロバイダーの使用を簡素化するのに役立ちます。 詳細については、「opentelemetry-go-provider-sls」をご参照ください。
トレースを作成してメトリックを登録する前に、OpenTelemetryプロバイダーを初期化する必要があります。
コードを実行するか、環境変数を設定して、OpenTelemetryプロバイダーを初期化できます。
OpenTelemetryプロバイダーを初期化するコードを実行します。
依存関係を追加します。
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 )
初期化コードを書き込みます。
次のコードの変数を実際の値に置き換えます。 変数の詳細については、「変数」をご参照ください。
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) }