Runner in the High

技術のことをかくこころみ

OpenTelemetryでもGin+GAEのログをリクエスト単位でグルーピングする

前回のOpenCensusでは比較的すんなりとGAEのログをリクエスト単位でグルーピングさせることができたが、OpenTelemetryの場合には追加でGoogleCloudPlatformのGithub Orgが用意しているpropagatorを使わねばならない。 github.com

以下がとりあえず動く超最小構成のミドルウェア

package middleware

import (
    cloudpropagator "github.com/GoogleCloudPlatform/opentelemetry-operations-go/propagator"
    "github.com/gin-gonic/gin"
    "go.opentelemetry.io/otel/propagation"
    "go.opentelemetry.io/otel/trace"
)

func Tracer() gin.HandlerFunc {
    return func(c *gin.Context) {
        ctx := c.Request.Context()

        // まずはX-Cloud-Trace-Contextからの読み取りをトライ
        // ここでエラーを拾っても何もできないので意図的にエラーは無視する
        if sc, _ := cloudpropagator.New().SpanContextFromRequest(c.Request); sc.IsValid() {
            ctx = trace.ContextWithRemoteSpanContext(ctx, sc)
        } else {
             // X-Cloud-Trace-ContextからValidな値が取れない場合には
             // traceparentヘッダからのTraceID/SpanIDのパースを試してみる
             prop := propagation.TraceContext{}
             ctx = prop.Extract(ctx, propagation.HeaderCarrier(c.Request.Header))
        }

        // 必要に応じてNewTracerProviderの中でsamplerやexporterを指定する
        traceProvider := sdktrace.NewTracerProvider()
        defer traceProvider.Shutdown(c)

        tracer := traceProvider.Tracer("yourapp/trace") // Empty: use default tracer name
        spanCtx, span := tracer.Start(ctx, c.FullPath())
        defer span.End()

        c.Request = c.Request.WithContext(spanCtx)
        c.Next()
    }
}

あとは前回の記事を参考にしてContextからSpanIDとTraceIDを取り出してロガーで送ってやればいい。まじめにやるならサンプラーやエクスポータなどもちゃんと設定してやったほうがいい*1が、そのあたりはこの記事では触れない。

OpenCensusのときはパッケージの中にStackdriver用のエクスポータが用意されていて、その中で X-Cloud-Trace-Context ヘッダから値を取り出すパーサが実装されていた。しかしOpenTelemetryからはどうやらエクスポータはインターフェイスのみを提供し、プラットフォーム固有のエクスポータは自分たちで用意してくれという方針になったように見える。Googleが使っている X-Cloud-Trace-Context 相当のヘッダはW3Ctraceparent としてベンダ非依存のオープンな仕様へと標準化されたらしく、むしろGoogle側がOpenTelemetryの仕様に従うという構図になっているっぽい。

もしかするとOpenCensus時代はまだ分散トレーシングが黎明期(?)だったこともあって、むしろ先立って分散トレーシングを製品化したり大規模に使ったりしていたGoogle側の仕様をOpenCensus側が取り入れたのかもしれない。

ヘッダの仕様に関しては以下のドキュメントが参考になった。 cloud.google.com

*1:たとえばこのサンプルではCloud Traceが動かない。トレーシングをするにしてもサンプリングの頻度指定もされていない。