🔭

OpenTelemetry Go SDKの初期化処理について、Splunk Distroを例に見ていきたいと思います!

2024/12/15に公開

ということで見ていきましょう!この記事は OpenTelemetry Advent Calendar 2024 および Splunk Advent Calendar 2024 の12/13の記事です。

OpenTelemetry SDKをセットアップすると、アプリケーションの挙動を表現する分散トレースやメトリクスなどをOpenTelemetry Collectorや分析バックエンドに送れるようになります。SDKのセットアップは言語によって多少の違いがあり、たとえばJava SDKでは起動オプションを追加するだけで、ライブラリ計装を含めたSDKが動作することになり、とても手軽に始められます。一方で、Go SDKは、Goの「書いた通りに動く」という特性上(そうですよね??)、SDKの初期化コードもまたある程度書く必要があります。

公式ドキュメントの Getting Started では、初期化コード例として、以下のような100行程度のコードを紹介しています。この中ではプロパゲーターの初期化や各データ型のプロバイダーを初期化していて、基本的なテレメトリーの送信が始められるようになっています。

OpenTelemetry Go SDKの初期化コード例

しかし、実用的な初期化処理としては、もう少しおすすめしたいものはあったりします。OpenTelemetryでは、公式リポジトリでSDKやCollectorを公開している一方で、オブザーバビリティやIaaSなどの組織が公開しているディストリビーションがあり、その中では、各社の事情に合わせた推奨設定やエクスポートの設定が簡単に出来るようになっています(OpenTelemetry版を「Upstream Distro」と呼ぶこともあります)。オブザーバビリティベンダーであるSplunkもCollectorやいくつかの言語SDKの独自ディストリビューションを公開して、公式に技術サポート対象として扱っています。

ということで、OpenTelemetry Go SDKのSplunk Distro の初期化コードを読んで、何が起こっているかを見ていきましょう。

初期化コード

Go SDKのSplunk Distroでは、ドキュメントによると、以下のコードで計装が有効化することになっています。

package main

import (
   "context"
   "github.com/signalfx/splunk-otel-go/distro"
)

func main() {
   sdk, err := distro.Run()
   if err != nil {
      panic(err)
   }
   // Flush all spans before the application exits
   defer func() {
      if err := sdk.Shutdown(context.Background()); err != nil {
         panic(err)
      }
   }()

   // ...

エージェントの起動は distro.Run() に集約されており、Upstream Distroと比較するとシンプルです。では、 Run() で何が起こっているか、otel.go の中身を見ていきましょう。

func Run(opts ...Option) (SDK, error) {
...(中略)
	res, err := newResource(ctx)
...(中略)
	shutdownFn, err := runTraces(c, res)
...(中略)
	shutdownFn, err = runMetrics(c, res)
}

Run() の主だった操作はこの3つです。それぞれ見ていきましょう。

newResource(ctx)

まず、newResouce() を見てみます。これはおそらく、リソース属性をまとめている関数でしょう。
リソース属性 とは、そのテレメトリを生成するエンティティを表すものであり、たとえば、ホスト名やポッド名などの動作している環境に関する情報や、起動コマンドなどのプロセスに関する情報です。

func newResource(ctx context.Context) (*resource.Resource, error) {
	// SDK's default resource.
	res := resource.Default()

	// Add process, Go runtime, and container information.
	procRes, err := resource.New(ctx,
		resource.WithProcess(),
		resource.WithContainer(),
	)
	if err != nil {
		return nil, err
	}
	res, err = resource.Merge(res, procRes)
	if err != nil {
		return nil, err
	}

	// Add Splunk-specific attributes.
	splunkRes := resource.NewSchemaless(attribute.String(distroVerAttr, Version()))
	res, err = resource.Merge(res, splunkRes)
	if err != nil {
		return nil, err
	}

	return res, nil
}

最初にデフォルトリソースを取得した後、プロセスの属性とコンテナの属性をマージし、さらにSplunk Distroを示す属性をマージして返しています。これにより、どんなプロセスで動いているか、どんなコンテナで動いているかが足されることになる・・・でしょう。resource が持つ関数は https://pkg.go.dev/go.opentelemetry.io/otel/sdk/resource にまとまっています。

このリソースを使って、トレースとメトリクスのプロバイダーを初期化している・・・はずです。

runTraces(c, res)

次に、トレース関連の初期化を見てみましょう。やはりというか、先ほど作成した res をパラメーターとして渡しています。

func runTraces(c *config, res *resource.Resource) (shutdownFunc, error) {
...(中略)
	o := []trace.TracerProviderOption{
		trace.WithResource(res),
		trace.WithRawSpanLimits(*c.SpanLimits),
		trace.WithSpanProcessor(trace.NewBatchSpanProcessor(exp)),
		trace.WithIDGenerator(c.IDGenerator),
	}
	if _, ok := os.LookupEnv(tracesSamplerKey); !ok {
		o = append(o, trace.WithSampler(trace.AlwaysSample()))
	}

	traceProvider := trace.NewTracerProvider(o...)
	otel.SetTracerProvider(traceProvider)

	return traceProvider.Shutdown, nil
}

プロバイダーのオプションとして、先ほど作成したリソース、スパンのリミット、バッチプロセッサーなどを設定しています。さらに追加として、サンプリングに関する情報を取得しています。それらのオプションをつかって traceProvider を作成し、セットしているのがこのコードです。

runMetrics(c, res)

では次に、メトリクスの初期化を見てみましょう。

func runMetrics(c *config, res *resource.Resource) (shutdownFunc, error) {
...(中略)
	o := []metric.Option{
		metric.WithResource(res),
		metric.WithReader(metric.NewPeriodicReader(exp)),
	}

	provider := metric.NewMeterProvider(o...)
	otel.SetMeterProvider(provider)

	// Add runtime metrics instrumentation.
	if err := runtime.Start(); err != nil {
		return nil, err
	}

	return provider.Shutdown, nil
}

トレースと同様に、こちらもリソースのオプションと、NewPeriodicReader で作ったものを渡しています。これは、定義した間隔でメトリクスを収集してエクスポートするやつです。

まとめ

いかがでしたか?

内容はかなり省略しましたが、他にも独自に設定可能にしているものがあったりするので、Upstream版を使うとしても参考になるのではないでしょうか。OpenTelemetryのさまざまな機能は、オプションを与えなくても、デフォルトでそれなりに動作するようには作っていますが、なにか気になることがあれば調整できるようにしていきましょう。

この記事は OpenTelemetry Advent Calendar 2024 および Splunk Advent Calendar 2024 の12/13の記事でした。

Happy o11y!!

Discussion