以前の記事で、OpenTelemetryでメトリックを記録するを書きましたが、現在いくつか変更が入っています。細かい型名やメソッド名が変わったものは除いて、大きめの変更点をまとめました。
Observerの追加
以前までは、メトリックの種類は
- Measure - 複数の値を記録するもの(例: HTTPハンドラのレイテンシ)
- Gauge - 最新の値だけ分かればいいもの(例: メモリ利用率)
- Count - カウンタ(例: GC回数)
の3種類でしたが、ここからGaugeがなくなって、代わりにObserverが追加されました。ドキュメントによると、Gaugeが使われるケースはOSやインフラなどの値を取得することが多く、この処理は(単純な計算に比べて)コストが高いので、非同期に行えるようにしたようです。
使い方は今までのものと少し異なり、事前に関数を登録しておきます。
import ( "runtime" "go.opentelemetry.io/otel/api/global" "go.opentelemetry.io/otel/api/metric" "go.opentelemetry.io/otel/api/unit" ) meter := global.MeterProvider().Meter("example/ping") meter.RegisterInt64Observer("runtime.memory.alloc", func(result metric.Int64ObserverResult) { var m runtime.MemStats runtime.ReadMemStats(&m) result.Observe(int64(m.Alloc), labels...) }, metric.WithUnit(unit.Bytes))
こうしておくと、OpenTelemetryのSDKはCheckpoint*1に到達するたびに、登録しておいた関数を暗黙的に実行して、result.Observeで返した値をExporterへ渡してくれるようになります。result.Observeはラベルが異なれば別の値として扱うため、1つの関数で何回呼び出しても構いません。これらの使い方以外は、Observerは使い方以外はGaugeと同じで、最終値しか取れません。また、登録した関数を解除する方法はありません。
ところで、上で挙げた例のruntime.MemStatsは1回の取得で複数の値を持っています。例えばAlloc, HeapAllocなどメモリの値をそれぞれ別のメトリックで扱いたい場合は、メトリックの数だけReadMemStatsが呼ばれてしまって効率が悪くなります。この点について現在issueが上がっているので、おそらく近いうちにまた変更が入るでしょう。
リソースがラベルから分離
今までは、メトリックにラベルをつける場合、
counter := meter.NewInt64Counter(...)
counter.Add(ctx, 1, labels)
のように、計測するときに全て渡すことしかできませんでした。Bindで設定しておくことは可能ですが、それでも一括で設定しかできませんでした。ですが一般的に、インスタンスIDやホスト名などのラベルは、メトリックにかかわらず全て一定で変化がありません。こういった、リソースを示すためのラベルを一括して設定できるようになりました。
以下はexporters/metric/stdoutの場合ですが、リソースに対応しているExporterは、Exporterの初期化を行う前後になんらかの方法でリソースを渡す方法があるんじゃないかなと思います。
import ( "go.opentelemetry.io/otel/api/key" "go.opentelemetry.io/otel/exporters/metric/stdout" "go.opentelemetry.io/otel/sdk/metric/controller/push" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/resource/resourcekeys" ) pusher, err := stdout.InstallNewPipeline(stdout.Config{}, push.WithResource(resource.New( key.String(resourcekeys.HostKeyID, "1-2-3-4"), key.String(resourcekeys.HostKeyName, "localhost"), )))
今まで通り、メトリックを記録する時にもラベルを設定することができます。リソースまたはメトリックで設定したラベルは、Exporterには
- リソースとして扱うラベル
- メトリックに紐づくラベル
のように分かれて渡されます。これら2種類のラベルをどのように扱うかはExporterの実装依存となりますが、おそらく多くの実装ではこれらをマージして扱うんじゃないかなと思います。
*1:Exporterがバックエンドに送る周期