このトピックでは、OpenTelemetry Golang ソフトウェア開発キット (SDK) を使用して、Golang アプリケーションから Simple Log Service にトレースデータを取り込む方法について説明します。
Simple Log Service は、Go 言語向けの商用プロプライエタリプローブを提供しています。これにより、非侵入型のイベントトラッキング、追加機能、および高い安定性が実現します。詳細については、「Go アプリケーションのモニタリング開始」をご参照ください。
前提条件
バージョン 1.13 以降の Golang 開発環境がインストールされていること。
取り込みフロー
OpenTelemetry プロバイダーを初期化します。
半自動取り込みの条件が満たされているかどうかを判断します。
条件が満たされている場合は、半自動メソッドを使用してトレースデータを取り込みます。
半自動メソッドでカバーされていないシナリオでは、手動メソッドを使用します。
条件が満たされていない場合は、手動メソッドを使用してトレースデータを取り込みます。
ステップ 1: OpenTelemetry プロバイダーの初期化
OpenTelemetry プロバイダーの使用を簡素化するために、Simple Log Service は SLS Provider を提供しています。これにより、必要な依存関係を迅速に構築し、Simple Log Service にアップロードできます。
トレースを作成したり、メトリックを登録したりする前に、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}")) // 初期化に失敗した場合、panic() を使用してプログラムを終了します。他のエラー処理メソッドも使用できます。 if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) // ビジネスロジックコードを追加します。 ... }表 1. 変数の説明
変数
説明
例
${service}
サービス名。シナリオに基づいてこのパラメーターを設定します。
payment
${service.namespace}
サービスが属する名前空間。
order
${version}
サービスバージョン番号。va.b.c フォーマットを使用します。
v0.1.2
${endpoint}
Simple Log Service プロジェクトのエンドポイント。フォーマットは ${project}.${region-endpoint}:Port です。パラメーターは次のように説明されます。
${project}:Simple Log Service プロジェクトの名前。
${region-endpoint}: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}
Trace インスタンスの ID。詳細については、「Trace インスタンスの作成」をご参照ください。
test-traces
${access-key-id}
ご利用の Alibaba Cloud アカウントの AccessKey ID。
Simple Log Service プロジェクトに対する書き込み権限のみを持つ Resource Access Management (RAM) ユーザーの AccessKey ペア (AccessKey ID と AccessKey Secret) を使用してください。RAM ユーザーに指定されたプロジェクトへのデータ書き込み権限を付与する方法の詳細については、「承認」をご参照ください。AccessKey ペアの取得方法の詳細については、「AccessKey ペア」をご参照ください。
なし
${access-key-secret}
ご利用の Alibaba Cloud アカウントの AccessKey Secret。
Simple Log Service プロジェクトに対する書き込み権限のみを持つ RAM ユーザーの AccessKey ペアを使用してください。
なし
環境変数を設定して初期化
設定メソッド
環境変数
必須
説明
デフォルト値
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 のリソース情報。プロジェクト名、Trace インスタンス名、およびプロジェクトに対する書き込み専用権限を持つユーザーの AccessKey ID と AccessKey Secret が含まれます。RAM ユーザーに指定されたプロジェクトへのデータ書き込み権限を付与する方法の詳細については、「承認」をご参照ください。AccessKey ペアの取得方法の詳細については、「AccessKey ペア」をご参照ください。
なし
WithTraceExporterEndpoint
SLS_OTEL_TRACE_ENDPOINT
いいえ
Simple Log Service プロジェクトのエンドポイント。フォーマットは ${project}.${region-endpoint}:Port です。パラメーターは次のように説明されます。
${project}:Simple Log Service プロジェクトの名前。
${region-endpoint}:Simple Log Service プロジェクトが存在するリージョンのエンドポイント。内部ネットワークおよびパブリックネットワークアクセスがサポートされています。詳細については、「エンドポイント」をご参照ください。
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}:Port です。パラメーターは次のように説明されます。
${project}:Simple Log Service プロジェクトの名前。
${region-endpoint}:Simple Log Service プロジェクトが存在するリージョンのエンドポイント。内部ネットワークおよびパブリックネットワークアクセスがサポートされています。詳細については、「エンドポイント」をご参照ください。
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 秒の値を設定します。
30s
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 は、多くの一般的なライブラリに対して自動イベントトラッキングを提供します。アプリケーションがこれらのライブラリを使用している場合、対応する自動イベントトラッキングソリューションを使用してデータを取り込むことができます。サポートされているライブラリのリストについては、「Golang 自動イベントトラッキングソリューション」をご参照ください。
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}")) // 初期化に失敗した場合、panic() を使用してプログラムを終了します。他のエラー処理メソッドも使用できます。 if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) // アプリケーションのメトリックデータを分析するには、関連するメトリックを登録します。 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 { // イベントを記録するには、コンテキストからスパンを取得し、イベントを追加します。 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...) } } // otel net/http の自動インジェクションを使用するには、http.Handler を 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) } }mux の取り込み
次の例は、go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.37.0 に基づいています。API は後のバージョンで変更される可能性があります。最新の例については、「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}")) // 初期化に失敗した場合、panic() を使用してプログラムを終了します。他のエラー処理メソッドも使用できます。 if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) // アプリケーションのメトリックデータを分析するには、関連するメトリックを登録します。 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" } // イベントを記録するには、コンテキストからスパンを取得し、イベントを追加します。 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")) // 初期化に失敗した場合、panic() を使用してプログラムを終了します。他のエラー処理メソッドも使用できます。 if err != nil { panic(err) } if err := provider.Start(slsConfig); err != nil { panic(err) } defer provider.Shutdown(slsConfig) mockTrace() mockMetrics() } func mockMetrics() { // タグ情報を追加します。 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) } // getting the current span の例 // 現在のスパンを取得します。 func getSpan(ctx context.Context) { span := trace.SpanFromContext(ctx) fmt.Printf("current span: %v\n", span) } // adding an attribute to a span の例 // スパンに属性を追加します。 func addAttribute(ctx context.Context) { span := trace.SpanFromContext(ctx) span.SetAttributes(attribute.KeyValue{ Key: "label-key-1", Value: attribute.StringValue("label-value-1")}) } // adding an event to a 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))) } // recording an exception の例 // スパンの結果とエラーメッセージを記録します。 func recordException(ctx context.Context) { span := trace.SpanFromContext(ctx) span.RecordError(errors.New("exception has occurred")) span.SetStatus(codes.Error, "internal error") } // creating 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) }