Skip to content

Commit

Permalink
Add otelhttp.NewMiddleware for better compatiblity with third-party H…
Browse files Browse the repository at this point in the history
…TTP routers and middleware chainers

The key difference between the existing otelhttp.NewHandler and
otelhttp.NewMiddleware is that the middleware takes the `next` handler
as an argument after construction, wheres NewHandler works by wrapping
one specific http.Handler at construction time.
  • Loading branch information
alnr committed Apr 26, 2023
1 parent 830138c commit 2df70cc
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 19 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

### Added

- Add `NewMiddleware` function in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#2964)
- AWS SDK add `rpc.system` attribute in `go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws`. (#3582, #3617)
- Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108)

Expand All @@ -24,6 +25,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Prevent taking from reservoir in AWS XRay Remote Sampler when there is zero capacity in `go.opentelemetry.io/contrib/samplers/aws/xray`. (#3684)
- Fix `otelhttp.Handler` in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp` to propagate multiple `WriteHeader` calls while persisting the initial `statusCode`. (#3580)

### Removed

- Remove `Handler` type in `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`. (#2964)

## [1.16.0-rc.2/0.41.0-rc.2/0.10.0-rc.2] - 2023-03-23

### Added
Expand Down
42 changes: 23 additions & 19 deletions instrumentation/net/http/otelhttp/handler.go
Expand Up @@ -31,16 +31,10 @@ import (
"go.opentelemetry.io/otel/trace"
)

var _ http.Handler = &Handler{}

// Handler is http middleware that corresponds to the http.Handler interface and
// is designed to wrap a http.Mux (or equivalent), while individual routes on
// the mux are wrapped with WithRouteTag. A Handler will add various attributes
// to the span using the attribute.Keys defined in this package.
type Handler struct {
// middleware is an http middleware which wraps the next handler in a span.
type middleware struct {
operation string
server string
handler http.Handler

tracer trace.Tracer
meter metric.Meter
Expand All @@ -60,11 +54,16 @@ func defaultHandlerFormatter(operation string, _ *http.Request) string {
return operation
}

// NewHandler wraps the passed handler, functioning like middleware, in a span
// named after the operation and with any provided Options.
// NewHandler wraps the passed handler in a span named after the operation and
// enriches it with metrics.
func NewHandler(handler http.Handler, operation string, opts ...Option) http.Handler {
h := Handler{
handler: handler,
return NewMiddleware(operation, opts...)(handler)
}

// NewMiddleware returns a tracing and metrics middleware from the given
// operation name and options.
func NewMiddleware(operation string, opts ...Option) func(http.Handler) http.Handler {
h := middleware{
operation: operation,
}

Expand All @@ -77,10 +76,14 @@ func NewHandler(handler http.Handler, operation string, opts ...Option) http.Han
h.configure(c)
h.createMeasures()

return &h
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h.serveHTTP(w, r, next)
})
}
}

func (h *Handler) configure(c *config) {
func (h *middleware) configure(c *config) {
h.tracer = c.Tracer
h.meter = c.Meter
h.propagators = c.Propagators
Expand All @@ -100,7 +103,7 @@ func handleErr(err error) {
}
}

func (h *Handler) createMeasures() {
func (h *middleware) createMeasures() {
h.counters = make(map[string]instrument.Int64Counter)
h.valueRecorders = make(map[string]instrument.Float64Histogram)

Expand All @@ -118,13 +121,14 @@ func (h *Handler) createMeasures() {
h.valueRecorders[ServerLatency] = serverLatencyMeasure
}

// ServeHTTP serves HTTP requests (http.Handler).
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// serveHTTP sets up tracing and calls the given next http.Handler with the span
// context injected into the request context.
func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http.Handler) {
requestStartTime := time.Now()
for _, f := range h.filters {
if !f(r) {
// Simply pass through to the handler if a filter rejects the request
h.handler.ServeHTTP(w, r)
next.ServeHTTP(w, r)
return
}
}
Expand Down Expand Up @@ -210,7 +214,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
labeler := &Labeler{}
ctx = injectLabeler(ctx, labeler)

h.handler.ServeHTTP(w, r.WithContext(ctx))
next.ServeHTTP(w, r.WithContext(ctx))

setAfterServeAttributes(span, bw.read, rww.written, rww.statusCode, bw.err, rww.err)

Expand Down

0 comments on commit 2df70cc

Please sign in to comment.