OpenCensusはdeprecatedされているので本当はOpenTelemetryを使った方がいいのだけれど、やったのでメモがてらに残しておく。アプリケーションの実行環境はGAEでやった。
追記: OpenTelemetryでやる方法も書きました izumisy.work
まずはログのグルーピング(Span)をリクエスト毎に開始するためのミドルウェアを作る。
package middleware import ( "github.com/gin-gonic/gin" "go.opencensus.io/exporter/stackdriver/propagation" "go.opencensus.io/trace" ) // // ルーティングの実装ではUseを使って以下のミドルウェアを登録しておく // router.Use(Tracer()) // func Tracer() gin.HandlerFunc { return func(c *gin.Context) { ctxWithSpan, span := trace.StartSpan(c, "yourapp/trace") defer span.End() httpFormat := propagation.HTTPFormat{} if sc, ok := httpFormat.SpanContextFromRequest(c.Request); ok { ctxWithSpan, span = trace.StartSpanWithRemoteParent(c, c.FullPath(), sc) } c.Request = c.Request.WithContext(ctxWithSpan) c.Next() } }
TracerミドルウェアによってContextにグルーピングのために必要な情報がセットされるので、それを取得する関数も用意しておく。
gin.Context
の場合にはRequestからContextを取り出すようにしているのがポイント。
package log import ( "context" "github.com/gin-gonic/gin" "go.opencensus.io/trace" ) type Tracer struct { SpanID string TraceID string } func ExtractTracer(ctx context.Context) Tracer { sc := trace.SpanContext{} if ginCtx, ok := ctx.(*gin.Context); ok { sc = trace.FromContext(ginCtx.Request.Context()).SpanContext() } else { sc = trace.FromContext(ctx).SpanContext() } return Tracer{ SpanID: sc.SpanID.String(), TraceID: sc.TraceID.String(), } }
あとはCloud Loggingにデータを送信するタイミングで、上で作ったExtractTracerを用いてContextからSpanIDとTraceIDを取り出し、それぞれ適切な形でセットしてやればok
package log import ( "fmt" "os" "log" "cloud.google.com/go/logging" ) func Log(ctx context.Context, severity logging.Severity, timestamp time.Time, text string) { projectID := os.Getenv("GOOGLE_CLOUD_PROJECT") client, err := logging.NewClient(ctx, projectID) if err != nil { log.Fatalf("Failed to create client: %v", err) } tracer := ExtractTracer(ctx) logger = client.Logger("ServeHTTP") logger.Log(logging.Entry{ Timestamp: timestamp, Severity: severity, Payload: text, SpanID: tracer.SpanID, Trace: fmt.Sprintf("projects/%s/traces/%s", projectID, tracer.TraceID), }) }
DeNAが出しているaelogというパッケージを使えばこの辺全部よしなにやってくれるっぽい。
Ginでなにもせず使えるかは試してない。ミドルウェアの型定義的に gin.WrapH
とかでラップしてUseしてあげれば使えるような気もするけど、どうなんだろう
github.com
追記: 調べたらこんなのもあった。 github.com Cloud Loggingとのつなぎこみだけ自分で作ってこういうのを使うでもいいかも。