mirror of
https://github.com/Fishwaldo/huma.git
synced 2025-03-15 19:31:27 +00:00
feat: add OpenTracing middleware
This commit is contained in:
parent
f976f54c6c
commit
69d47ad239
6 changed files with 77 additions and 0 deletions
25
README.md
25
README.md
|
@ -26,6 +26,7 @@ Features include:
|
|||
- Automatic recovery from panics with traceback & request logging
|
||||
- Structured logging middleware using [Zap](https://github.com/uber-go/zap)
|
||||
- Automatic handling of `Prefer: return=minimal` from [RFC 7240](https://tools.ietf.org/html/rfc7240#section-4.2)
|
||||
- [OpenTracing](https://opentracing.io/) for requests and errors
|
||||
- Per-operation request size limits & timeouts with sane defaults
|
||||
- [Content negotiation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation) between server and client
|
||||
- Support for gzip ([RFC 1952](https://tools.ietf.org/html/rfc1952)) & Brotli ([RFC 7932](https://tools.ietf.org/html/rfc7932)) content encoding via the `Accept-Encoding` header.
|
||||
|
@ -480,6 +481,30 @@ app.Middleware(func(next http.Handler) http.Handler {
|
|||
|
||||
When using the `cli.NewRouter` convenience method, a set of default middleware is added for you. See `middleware.DefaultChain` for more info.
|
||||
|
||||
### Enabling OpenTracing
|
||||
|
||||
[OpenTracing](https://opentracing.io/) support is built-in, but you have to tell the global tracer where to send the information, otherwise it acts as a no-op. For example, if you use [DataDog APM](https://www.datadoghq.com/blog/opentracing-datadog-cncf/) and have the agent configured wherever you deploy your service:
|
||||
|
||||
```go
|
||||
import (
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer"
|
||||
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := opentracer.New(tracer.WithAgentAddr("host:port"))
|
||||
defer tracer.Stop()
|
||||
|
||||
// Set it as a Global Tracer
|
||||
opentracing.SetGlobalTracer(t)
|
||||
|
||||
app := cli.NewRouter("My Cool Service", "1.0.0")
|
||||
// register routes here
|
||||
app.Run()
|
||||
}
|
||||
```
|
||||
|
||||
### Timeouts, Deadlines, Cancellation & Limits
|
||||
|
||||
Huma provides utilities to prevent long-running handlers and issues with huge request bodies and slow clients with sane defaults out of the box.
|
||||
|
|
1
go.mod
1
go.mod
|
@ -13,6 +13,7 @@ require (
|
|||
github.com/magiconair/properties v1.8.2 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
github.com/mitchellh/mapstructure v1.3.3 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/pelletier/go-toml v1.8.0 // indirect
|
||||
github.com/spf13/afero v1.3.4 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -171,6 +171,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
|
|
|
@ -23,6 +23,7 @@ func DefaultChain(next http.Handler) http.Handler {
|
|||
// Note: logger goes before recovery so that recovery can use it. We don't
|
||||
// expect the logger to cause panics.
|
||||
return chi.Chain(
|
||||
OpenTracing,
|
||||
Logger,
|
||||
Recovery,
|
||||
ContentEncoding,
|
||||
|
|
41
middleware/opentracing.go
Normal file
41
middleware/opentracing.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
)
|
||||
|
||||
// OpenTracing provides a middleware for cross-service tracing support.
|
||||
func OpenTracing(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
tracer := opentracing.GlobalTracer()
|
||||
|
||||
// Get any incoming tracing context via HTTP headers & create the span.
|
||||
ctx, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
|
||||
span := tracer.StartSpan("http.request", ext.RPCServerOption(ctx))
|
||||
defer span.Finish()
|
||||
|
||||
// Set basic HTTP info
|
||||
ext.HTTPMethod.Set(span, r.Method)
|
||||
ext.HTTPUrl.Set(span, r.URL.String())
|
||||
ext.Component.Set(span, "huma")
|
||||
span.SetTag("span.type", "web")
|
||||
|
||||
// Update context & continue the middleware chain.
|
||||
r = r.WithContext(opentracing.ContextWithSpan(r.Context(), span))
|
||||
ws := statusRecorder{ResponseWriter: w}
|
||||
next.ServeHTTP(&ws, r)
|
||||
|
||||
// If we have a Chi route template, save it
|
||||
if chictx := chi.RouteContext(r.Context()); chictx != nil {
|
||||
span.SetTag("resource.name", chictx.RoutePattern())
|
||||
span.SetOperationName(r.Method + " " + chictx.RoutePattern())
|
||||
}
|
||||
|
||||
// Save the status code
|
||||
ext.HTTPStatusCode.Set(span, uint16(ws.status))
|
||||
})
|
||||
}
|
|
@ -12,6 +12,8 @@ import (
|
|||
|
||||
"github.com/danielgtaylor/huma"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -110,6 +112,11 @@ func Recovery(next http.Handler) http.Handler {
|
|||
fmt.Printf("Caught panic: %v\n%s\n\nFrom request:\n%s", err, debug.Stack(), string(request))
|
||||
}
|
||||
|
||||
// If OpenTracing is enabled, augment the span with error info
|
||||
if span := opentracing.SpanFromContext(r.Context()); span != nil {
|
||||
span.SetTag(string(ext.Error), err)
|
||||
}
|
||||
|
||||
ctx := huma.ContextFromRequest(w, r)
|
||||
ctx.WriteError(http.StatusInternalServerError, "Unrecoverable internal server error")
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue