Help us understand the problem. What is going on with this article?

OpenTelemetryで複数のExporterを登録する

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にするか、エラーを返すかして欲しい気持ち・・・

https://github.com/open-telemetry/opentelemetry-go/blob/master/api/global/internal/state.go

最終的な解決策

自分で複数の出力するプロバイダーを作ります。

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に書いておきます。

Screen Shot 2020-09-16 at 18.46.21.png

future
ITを武器とした課題解決型のコンサルティングサービスを提供します
http://future-architect.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした