分散トレース用のGolangアプリケーションインストルメンテーション

分散トレース用のGolangアプリケーションインストルメンテーション

このガイドを使用して、OpenTelemetry Protocol(OTLP)仕様を使用してトレースを発行するgolangアプリケーションを作成します。 必要なのは、golangでのアプリケーション開発の基本的な理解だけです。 

プロジェクトを初期化する

開始するには、次のコードスニペットを使用して新しいディレクトリ構造を作成し、コマンドを実行します mod initgithub.com/に移動します/ simple-go-service 同じディレクトリにあります。 「 」とgithubユーザー名を入力します。 これにより、Goがインポートを管理するために使用するgo.modファイルが作成されます。 次に、という名前の新しいファイルを作成します メイン.ゴー Goコードを配置する場所。 

mkdir -p cmd/simple-service
go mod init github.com/USERNAME/simple-go-service
touch cmd/simple-service/main.go

リソース検出器を作成する

リソースは、テレメトリ信号を生成したオブジェクトを記述します。 基本的に、これはサービスまたはアプリケーションの名前でなければなりません。 OpenTelemetry は、サービス実行環境を記述する標準を定義しました。 hostname、hostType (クラウド、コンテナー、サーバーレス)、namespace、cloud-resource-id など。これらの属性は、セマンティック規則または semcon で定義されます。 OpenTelemetry のセマンティック規則 標準化された命名パターンに合意されており、golang セマンティック規則パッケージは こちら

この例では、アプリケーションのいくつかのenvvar設定を使用してリソースを定義しています。 ユーザーは、このdetectResourceメソッドを拡張して、基盤となるリソース提供インフラストラクチャ(K8s-apiserver、cloud-api、その他の検出)に従ってリソース属性を検出できます。

func detectResource(ctx context.Context) (*resource.Resource, error) {
    res, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceNameKey.String("product-service"),
            semconv.ServiceNamespaceKey.String("US-West-1"),
            semconv.HostNameKey.String("prodsvc.us-west-1.example.com"),
            semconv.HostTypeKey.String("system"),
        ),
    )
    return res, err
}

Init SpanExporter

エクスポータは、アプリケーションからリモートバックエンドへのテレメトリ信号(トレース)のエクスポート、ファイルへのログ記録、stdoutへのストリームのエクスポートを担当するSDKのコンポーネントです。 NS。 

この例では、で実行されているOpenTelemetryレシーバーバックエンドにトレースを送信するgRPCエクスポーターを作成しています。 localhost:55680。 おそらくOpenTelemetryコレクター。 ユーザーは、gRPCヘッダーでmTLSまたはApplication Authトークンを使用して、任意のアプリケーション認証メカニズムをサポートするように拡張できます。

func spanExporter(ctx context.Context) (*otlptrace.Exporter, error) {
    return otlptracegrpc.New(ctx, otlptracegrpc.WithInsecure(),
        otlptracegrpc.WithEndpoint("localhost:55680"),
        otlptracegrpc.WithDialOption(grpc.WithBlock()),
    )
}

InitSpanプロセッサ

スパンプロセッサは、CRUD操作、より良いQoSのための要求のバッチ処理、特定の条件に基づいたスパンデータのサンプリングを担当します。 ここでは、エクスポーターを介してネットワークにフラッシュする前に、スパンをバッチ処理するためのバッチスパンプロセッサーを作成しています。

bsp := sdktrace.NewBatchSpanProcessor(traceExporter)

TracerProviderを構築します

sdk / traceパッケージには、OpenTelemetry分散トレースのサポートが含まれています。 NewTracerProviderは、新しく構成されたTracerProviderを返します。

デフォルトでは、返されるTracerProviderは次のように構成されています。

  • ParentBased(AlwaysSample)サンプラー
  • 乱数IDGenerator
  • resource.Default()リソース
  • デフォルトのSpanLimits

渡されたoptsは、これらのデフォルト値をオーバーライドし、返されるTracerProviderを適切に構成するために使用されます。

tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(bsp),
    sdktrace.WithResource(res),
    sdktrace.WithSampler(sdktrace.AlwaysSample()),
)
defer func() { _ = tp.Shutdown(ctx) }()

初期化プロパゲーター

OpenTelemetryプロパゲーターは、アプリケーションによって交換されるメッセージからコンテキストデータを抽出してメッセージに挿入するために使用されます。 OTELでサポートされているデフォルトのプロパゲーターは W3Cトレースコンテキスト エンコードと W3C手荷物。 

propagator := propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})

トレーサーを作成する

パッケージOTelは、OpenTelemetryAPIへのグローバルアクセスを提供します。 OTelパッケージのサブパッケージは、OpenTelemetryAPIの実装を提供します。 

提供されているAPIは、コードを計測し、そのコードのパフォーマンスと操作に関するデータを測定するために使用されます。 デフォルトでは、測定データはどこにも処理または送信されません。

のようなOpenTelemetrySDKの実装 デフォルトのSDK実装、および関連するエクスポーターは、このデータを処理および転送するために使用されます–エクスポーターのセクションで説明したように。

以下の例では、プロバイダーファクトリからトレーサーを作成し、複数のサービスにまたがるスパンのコンテキスト伝播を構成しています。 最後に、トレーサーオブジェクトを作成します。 トレーサーオブジェクトは、スパンの管理と作成を担当します。

otel.Tracerは、トレーサーインターフェイスを実装する名前付きトレーサーを作成します。 名前が空の文字列の場合、プロバイダーはデフォルトの名前を使用します。

otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagator)
tracer := otel.Tracer("example.com/basictracer")

スパン属性の定義

スパンは、操作のXNUMX回の実行です。 これは、スパンタグと呼ばれることもある一連の属性によって識別されます。 アプリケーションの所有者は、スパンに必要な情報を取得できる属性を自由に選択できます。 スパンごとのスパン属性の数に制限はありません。 

この例では、サンプルアプリケーションにXNUMXつのスパン属性を定義しています。

priority := attribute.Key("business.priority")
appEnv := attribute.Key("prod.env")

スパンを作成

ここでは、実行がスコープ外になったら、スパンを開始して終了するようにトレーサーに要求しています。

var span trace.Span
ctx, span = tracer.Start(ctx, "HTTP GET /products/{id}")
defer span.End()

スパンへのイベント/ログの追加

スパンは、スパンの実行中に発生したいくつかの実行ログ/イベントで強化できます。 この情報は、それぞれのスパンに関連付けられたコンテキストログを提供するのに役立ちます。

span.AddEvent("Authentication", trace.WithAttributes(attribute.String("Username", "TestUser")))
span.AddEvent("Products", trace.WithAttributes(attribute.Int("ID", 100)))

スパン属性をスパンに追加

ここでは、前に特定したスパン属性に値を設定しています。

span.SetAttributes(appEnv.String("UAT"))
span.SetAttributes(priority.String("CRITICAL"))

それをまとめる

package main

import (
    "context"
    "log"

    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/attribute"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
    "go.opentelemetry.io/otel/propagation"
    "go.opentelemetry.io/otel/sdk/resource"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
    "go.opentelemetry.io/otel/trace"
    "google.golang.org/grpc"
)

func detectResource(ctx context.Context) (*resource.Resource, error) {
    res, err := resource.New(ctx,
        resource.WithAttributes(
            semconv.ServiceNameKey.String("product-service"),
            semconv.ServiceNamespaceKey.String("US-West-1"),
            semconv.HostNameKey.String("prodsvc.us-west-1.example.com"),
            semconv.HostTypeKey.String("system"),
        ),
    )
    return res, err
}

func spanExporter(ctx context.Context) (*otlptrace.Exporter, error) {
    return otlptracegrpc.New(ctx, otlptracegrpc.WithInsecure(),
        otlptracegrpc.WithEndpoint("localhost:55680"),
        otlptracegrpc.WithDialOption(grpc.WithBlock()),
    )
}

func main() {

    ctx := context.Background()
    traceExporter, err := spanExporter(ctx)
    if err != nil {
        log.Fatalf("failed to initialize stdouttrace export pipeline: %v", err)
    }

    res, err := detectResource(ctx)
    bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
    tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(bsp),
        sdktrace.WithResource(res),
        sdktrace.WithSampler(sdktrace.AlwaysSample()),
    )
    defer func() { _ = tp.Shutdown(ctx) }()
    otel.SetTracerProvider(tp)
    propagator := propagation.NewCompositeTextMapPropagator(propagation.Baggage{}, propagation.TraceContext{})
    otel.SetTextMapPropagator(propagator)

    tracer := otel.Tracer("example.com/basictracer")
    priority := attribute.Key("business.priority")
    appEnv := attribute.Key("prod.env")

    func(ctx context.Context) {
        var span trace.Span
        ctx, span = tracer.Start(ctx, "HTTP GET /products/{id}")
        defer span.End()
        span.AddEvent("Authentication", trace.WithAttributes(attribute.String("Username", "TestUser")))
        span.AddEvent("Products", trace.WithAttributes(attribute.Int("ID", 100)))
        span.SetAttributes(appEnv.String("UAT"))

        func(ctx context.Context) {
            var span trace.Span
            ctx, span = tracer.Start(ctx, "SELECT * from Products where pID={id}")
            defer span.End()
            span.SetAttributes(priority.String("CRITICAL"))
            span.AddEvent("Datababse", trace.WithAttributes(attribute.Int("Count", 20)))
        }(ctx)
    }(ctx)
}

アプリケーションを実行する

最後に、このインストルメント化されたアプリケーションを実行して、LogicMonitorプラットフォームでトレースを取得します。

go run main.go

LogicMonitorプラットフォームで受信したトレース。

LogicMonitorプラットフォーム内のトレースを表示するダッシュボード

スパンの詳細図。

サービス名とリソース属性は、LogicMonitorプラットフォームの[トレース]セクションで丸で囲まれて強調表示されています

構築されたトレース

Resource labels:
    -> host.name: STRING(prodsvc.us-west-1.example.com)
    -> host.type: STRING(system)
    -> service.name: STRING(product-service)
    -> service.namespace: STRING(US-West-1)
InstrumentationLibrarySpans #0
InstrumentationLibrary example.com/basictracer
Span #0
    Trace ID   : aadf3c506b66cdbb3bdca3042f531353
    Parent ID  : eaf64b7cf9d4bdb8
    ID         : 45d9025bf40903d3
    Name       : SELECT * from Products where pID={id}
    Kind       : SPAN_KIND_INTERNAL
    Start time : 2021-07-20 09:55:42.609895 +0530 IST
    End time   : 2021-07-20 09:55:42.609896037 +0530 IST
    Status code : STATUS_CODE_OK
    Status message :
Attributes:
    -> business.priority: STRING(CRITICAL)
Events:
SpanEvent #0
    -> Name: Datababse
    -> Timestamp: 1626755142609896000
    -> DroppedAttributesCount: 0
    -> Attributes:
        -> Count: INT(20)
Span #1
    Trace ID   : aadf3c506b66cdbb3bdca3042f531353
    Parent ID  :
    ID         : eaf64b7cf9d4bdb8
    Name       : HTTP GET /products/{id}
    Kind       : SPAN_KIND_INTERNAL
    Start time : 2021-07-20 09:55:42.609884 +0530 IST
    End time   : 2021-07-20 09:55:42.609900603 +0530 IST
    Status code : STATUS_CODE_OK
    Status message :
Attributes:
    -> prod.env: STRING(UAT)
Events:
SpanEvent #0
    -> Name: Authentication
    -> Timestamp: 1626755142609891000
    -> DroppedAttributesCount: 0
    -> Attributes:
        -> Username: STRING(TestUser)
SpanEvent #1
    -> Name: Products
    -> Timestamp: 1626755142609893000
    -> DroppedAttributesCount: 0
    -> Attributes:
        -> ID: INT(100)

まとめ

おめでとうございます。OpenTelemetryProtocol(OTLP)仕様を使用してトレースを発行するgolangアプリケーションを作成しました。 OTLP仕様を使用してビジネスアプリケーションのインストルメント化を開始するときに、このコードを参照として使用できます。 LogicMonitor APM仕様は、ベンダーロックインなしで100%OTLPに準拠しています。 LogicMonitorプラットフォームでトラブルシューティングするための複数のサービスのトレースを受信して​​視覚化する こちらから無料トライアルアカウントにサインアップしてください。 複数の言語にわたるOpenTelemetry標準を使用した分散トレースのアプリケーションインストルメンテーション手順をカバーするブログをチェックしてください。