forked from goadesign/clue
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp.go
More file actions
90 lines (83 loc) · 3.13 KB
/
http.go
File metadata and controls
90 lines (83 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package trace
import (
"context"
"net/http"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"goa.design/goa/v3/middleware"
)
// Message printed by panic when using a method with a non-initialized context.
const errContextMissing = "context not initialized for tracing, use trace.Context to set it up"
// HTTP returns a tracing middleware that uses a parent based sampler (i.e.
// traces if the parent request traces) and an adaptive root sampler (i.e. when
// there is no parent uses a target number of requests per second to trace).
// The implementation leverages the OpenTelemetry SDK and can thus be configured
// to send traces to an OpenTelemetry remote collector. It is aware of the Goa
// RequestID middleware and will use it to propagate the request ID to the
// trace. HTTP panics if the context hasn't been initialized with Context.
//
// Example:
//
// // Connect to remote trace collector.
// conn, err := grpc.DialContext(ctx, collectorAddr,
// grpc.WithTransportCrendentials(insecure.Credentials()))
// if err != nil {
// log.Error(ctx, err)
// os.Exit(1)
// }
// // Initialize context for tracing
// ctx := trace.Context(ctx, svcgen.ServiceName, trace.WithGRPCExporter(conn))
// // Mount middleware
// handler := trace.HTTP(ctx)(mux)
//
func HTTP(ctx context.Context) func(http.Handler) http.Handler {
s := ctx.Value(stateKey)
if s == nil {
panic(errContextMissing)
}
return func(h http.Handler) http.Handler {
h = initTracingContext(ctx, h)
h = addRequestIDHTTP(h)
return otelhttp.NewHandler(h, s.(*stateBag).svc,
otelhttp.WithTracerProvider(s.(*stateBag).provider),
otelhttp.WithPropagators(s.(*stateBag).propagator))
}
}
// Client returns a roundtripper that wraps t and creates spans for each request.
// It panics if the context hasn't been initialized with Context.
func Client(ctx context.Context, t http.RoundTripper, opts ...otelhttp.Option) http.RoundTripper {
s := ctx.Value(stateKey)
if s == nil {
panic(errContextMissing)
}
opts = append(opts,
otelhttp.WithTracerProvider(s.(*stateBag).provider),
otelhttp.WithPropagators(s.(*stateBag).propagator))
return otelhttp.NewTransport(t, opts...)
}
// addRequestIDHTTP is a middleware that adds the request ID to the current span
// attributes.
func addRequestIDHTTP(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
requestID := req.Context().Value(middleware.RequestIDKey)
if requestID == nil {
h.ServeHTTP(w, req)
return
}
span := trace.SpanFromContext(req.Context())
span.SetAttributes(attribute.String(AttributeRequestID, requestID.(string)))
h.ServeHTTP(w, req)
})
}
// initTracingContext is a middleware that adds the tracing state to the request
// context.
func initTracingContext(traceCtx context.Context, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if IsTraced(req.Context()) {
ctx := withTracing(traceCtx, req.Context())
req = req.WithContext(ctx)
}
h.ServeHTTP(w, req)
})
}