前回の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
相当のヘッダはW3Cで traceparent
としてベンダ非依存のオープンな仕様へと標準化されたらしく、むしろGoogle側がOpenTelemetryの仕様に従うという構図になっているっぽい。
もしかするとOpenCensus時代はまだ分散トレーシングが黎明期(?)だったこともあって、むしろ先立って分散トレーシングを製品化したり大規模に使ったりしていたGoogle側の仕様をOpenCensus側が取り入れたのかもしれない。
ヘッダの仕様に関しては以下のドキュメントが参考になった。 cloud.google.com
*1:たとえばこのサンプルではCloud Traceが動かない。トレーシングをするにしてもサンプリングの頻度指定もされていない。