OpenCensusのノリで、エクスポーターを複数追加したら、なぜかJaegerにトレースが出ないぞ、ということで、もしかしてこれはバグなのでは?コミットチャンスでは?と思い調べたりしていました。
うまくいかなかったコードはこちら。サンプルのjaeger出力のコードに対して、タコの絵文字の部分を追加しました。標準出力エクスポーターですね。
package main
import (
// (略)
"go.opentelemetry.io/otel/exporters/stdout" // 🐙
)
func initTracer() func() {
// (略)
// begin: 🐙
_, err = stdout.InstallNewPipeline(nil, nil)
if err != nil {
log.Fatal(err)
}
// end: 🐙
return func() {
flush()
}
}
これを実行すると、標準出力には出るが、Jaegerには出ないぞ、というのが発端。
globalには1つのProviderしか登録できないっぽい
OpenTelemetryはよくあるGoのAPI設計のように、詳細設定ができるAPIがまず提供されており、それをラップする形で、簡単に扱えるAPIがあります。上記のInstall-
なメソッドは簡単メソッドです。どのエクスポーターでも、NewExport
な関数があり、それをラップする形で提供されています。この関数では最後にglobal.SetTraceProvider()
を呼んでいます。
func InstallNewPipeline(endpointOption EndpointOption, opts ...Option) (func(), error) {
tp, flushFn, err := NewExportPipeline(endpointOption, opts...)
if err != nil {
return nil, err
}
global.SetTracerProvider(tp)
return flushFn, nil
}
この関数、中をみてみると、どうも、atomic.Value
な感じ。それにSetしているので1つしかエクスポーターが設定でいない感じです。グローバルで複数やったら最初の設定が上書きされちゃうってわかりにくいし、プログラムの稼働中にプロバイダーを動的に差し替えたりもしないので、2回目の実行はpanicにするか、エラーを返すかして欲しい気持ち・・・
最終的な解決策
自分で複数の出力するプロバイダーを作ります。
ymotongpoo氏に教えてもらったのがsdktrace.WithSyncer()
でした。OpenTelemetryの簡単初期化関数の中でもこれを使ってsdktrace.NewProvider()
にエクスポーターは登録していました。これを参考に複数登録すれば良さそうです。
jexp, err := jaeger.NewRawExporter(
jaeger.WithCollectorEndpoint("http://localhost:14268/api/traces"),
jaeger.WithProcess(
jaeger.Process{
ServiceName: "ot-demo",
},
),
jaeger.WithSDK(&sdktrace.Config{DefaultSampler: sdktrace.AlwaysSample()}))
if err != nil {
panic(err)
}
defer jexp.Flush()
stdexp, err := stdout.NewExporter()
if err != nil {
panic(err)
}
tp, err := sdktrace.NewProvider(
sdktrace.WithSyncer(stdexp),
sdktrace.WithSyncer(jexp),
)
if err != nil {
panic(err)
}
global.SetTraceProvider(tp)
これで無事、Jaegerにも、標準出力にも出るようになりました。まあ、複数出したい、という要件自体があまりないと思うのですが、記録のためにQiitaに書いておきます。