package trace import ( "context" "fmt" "net/http" "reflect" "github.com/gorilla/mux" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "google.golang.org/grpc/stats" ) func NewHTTPMiddleware(opts ...otelhttp.Option) mux.MiddlewareFunc { return otelhttp.NewMiddleware("Server: %s %s", append(opts, otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string { routeStr := "" route := mux.CurrentRoute(r) if route != nil { var err error routeStr, err = route.GetPathTemplate() if err != nil { routeStr, err = route.GetPathRegexp() if err != nil { routeStr = "" } } } return fmt.Sprintf(operation, r.Method, routeStr) }))...) } type clientStatsHandlerWrapper struct { ClientStatsHandlerOptions base stats.Handler } type ClientStatsHandlerOptions struct { statsInterceptor func(ctx context.Context, rs stats.RPCStats) stats.RPCStats } type ClientStatsHandlerOption func(*ClientStatsHandlerOptions) func (o *ClientStatsHandlerOptions) apply(opts ...ClientStatsHandlerOption) { for _, op := range opts { op(o) } } // WithStatsInterceptor calls the given function to modify the rpc stats before // passing it to the stats handler during HandleRPC events. // // The interceptor MUST NOT modify the RPCStats it is given. It should instead // return a copy of the underlying object with the same type, with any // modifications made to the copy. func WithStatsInterceptor(statsInterceptor func(ctx context.Context, rs stats.RPCStats) stats.RPCStats) ClientStatsHandlerOption { return func(o *ClientStatsHandlerOptions) { o.statsInterceptor = statsInterceptor } } func NewClientStatsHandler(base stats.Handler, opts ...ClientStatsHandlerOption) stats.Handler { options := ClientStatsHandlerOptions{} options.apply(opts...) return &clientStatsHandlerWrapper{ ClientStatsHandlerOptions: options, base: base, } } // HandleConn implements stats.Handler. func (w *clientStatsHandlerWrapper) HandleConn(ctx context.Context, stats stats.ConnStats) { w.base.HandleConn(ctx, stats) } // HandleRPC implements stats.Handler. func (w *clientStatsHandlerWrapper) HandleRPC(ctx context.Context, stats stats.RPCStats) { if w.statsInterceptor != nil { modified := w.statsInterceptor(ctx, stats) if reflect.TypeOf(stats) != reflect.TypeOf(modified) { panic("bug: stats interceptor returned a message of a different type") } w.base.HandleRPC(ctx, modified) } else { w.base.HandleRPC(ctx, stats) } } // TagConn implements stats.Handler. func (w *clientStatsHandlerWrapper) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context { return w.base.TagConn(ctx, info) } // TagRPC implements stats.Handler. func (w *clientStatsHandlerWrapper) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { return w.base.TagRPC(ctx, info) }