1
1
package middleware
2
2
3
3
import (
4
- rd "math/rand"
5
- "net/http"
6
-
7
4
"context"
5
+ "net/http"
8
6
9
7
"github.com/goadesign/goa"
10
8
"github.com/goadesign/goa/client"
@@ -25,56 +23,144 @@ type (
25
23
// tracing systems such as Zipkin or AWS X-Ray.
26
24
IDFunc func () string
27
25
26
+ // TracerOption is a constructor option that makes it possible to customize
27
+ // the middleware.
28
+ TracerOption func (* tracerOptions ) * tracerOptions
29
+
30
+ // tracerOptions is the struct storing all the options.
31
+ tracerOptions struct {
32
+ traceIDFunc IDFunc
33
+ spanIDFunc IDFunc
34
+ samplingPercent int
35
+ maxSamplingRate int
36
+ sampleSize int
37
+ }
38
+
28
39
// tracedDoer is a goa client Doer that inserts the tracing headers for
29
40
// each request it makes.
30
41
tracedDoer struct {
31
42
client.Doer
32
43
}
33
44
)
34
45
35
- // Tracer returns a middleware that initializes the trace information in the
36
- // context. The information can be retrieved using any of the ContextXXX
37
- // functions.
46
+ // TraceIDFunc is a constructor option that overrides the function used to
47
+ // compute trace IDs.
48
+ func TraceIDFunc (f IDFunc ) TracerOption {
49
+ return func (o * tracerOptions ) * tracerOptions {
50
+ if f == nil {
51
+ panic ("trace ID function cannot be nil" )
52
+ }
53
+ o .traceIDFunc = f
54
+ return o
55
+ }
56
+ }
57
+
58
+ // SpanIDFunc is a constructor option that overrides the function used to
59
+ // compute span IDs.
60
+ func SpanIDFunc (f IDFunc ) TracerOption {
61
+ return func (o * tracerOptions ) * tracerOptions {
62
+ if f == nil {
63
+ panic ("span ID function cannot be nil" )
64
+ }
65
+ o .spanIDFunc = f
66
+ return o
67
+ }
68
+ }
69
+
70
+ // SamplingPercent sets the tracing sampling rate as a percentage value.
71
+ // It panics if p is less than 0 or more than 100.
72
+ // SamplingPercent and MaxSamplingRate are mutually exclusive.
73
+ func SamplingPercent (p int ) TracerOption {
74
+ if p < 0 || p > 100 {
75
+ panic ("sampling rate must be between 0 and 100" )
76
+ }
77
+ return func (o * tracerOptions ) * tracerOptions {
78
+ o .samplingPercent = p
79
+ return o
80
+ }
81
+ }
82
+
83
+ // MaxSamplingRate sets a target sampling rate in requests per second. Setting a
84
+ // max sampling rate causes the middleware to adjust the sampling percent
85
+ // dynamically.
86
+ // SamplingPercent and MaxSamplingRate are mutually exclusive.
87
+ func MaxSamplingRate (r int ) TracerOption {
88
+ if r <= 0 {
89
+ panic ("max sampling rate must be greater than 0" )
90
+ }
91
+ return func (o * tracerOptions ) * tracerOptions {
92
+ o .maxSamplingRate = r
93
+ return o
94
+ }
95
+ }
96
+
97
+ // SampleSize sets the number of requests between two adjustments of the sampling
98
+ // rate when MaxSamplingRate is set. Defaults to 1,000.
99
+ func SampleSize (s int ) TracerOption {
100
+ if s <= 0 {
101
+ panic ("sample size must be greater than 0" )
102
+ }
103
+ return func (o * tracerOptions ) * tracerOptions {
104
+ o .sampleSize = s
105
+ return o
106
+ }
107
+ }
108
+
109
+ // NewTracer returns a trace middleware that initializes the trace information
110
+ // in the request context. The information can be retrieved using any of the
111
+ // ContextXXX functions.
38
112
//
39
- // sampleRate must be a value between 0 and 100. It represents the percentage of
40
- // requests that should be traced.
113
+ // samplingPercent must be a value between 0 and 100. It represents the percentage
114
+ // of requests that should be traced. If the incoming request has a Trace ID
115
+ // header then the sampling rate is disregarded and the tracing is enabled.
41
116
//
42
117
// spanIDFunc and traceIDFunc are the functions used to create Span and Trace
43
118
// IDs respectively. This is configurable so that the created IDs are compatible
44
119
// with the various backend tracing systems. The xray package provides
45
120
// implementations that produce AWS X-Ray compatible IDs.
46
- //
47
- // If the incoming request has a TraceIDHeader header then the sample rate is
48
- // disregarded and the tracing is enabled.
49
- func Tracer (sampleRate int , spanIDFunc , traceIDFunc IDFunc ) goa.Middleware {
50
- if sampleRate < 0 || sampleRate > 100 {
51
- panic ("tracing: sample rate must be between 0 and 100" )
121
+ func NewTracer (opts ... TracerOption ) goa.Middleware {
122
+ o := & tracerOptions {
123
+ traceIDFunc : shortID ,
124
+ spanIDFunc : shortID ,
125
+ samplingPercent : 100 ,
126
+ sampleSize : 1000 , // only applies if maxSamplingRate is set
127
+ }
128
+ for _ , opt := range opts {
129
+ o = opt (o )
130
+ }
131
+ var sampler Sampler
132
+ if o .maxSamplingRate > 0 {
133
+ sampler = NewAdaptiveSampler (o .maxSamplingRate , o .sampleSize )
134
+ } else {
135
+ sampler = NewFixedSampler (o .samplingPercent )
52
136
}
53
137
return func (h goa.Handler ) goa.Handler {
54
138
return func (ctx context.Context , rw http.ResponseWriter , req * http.Request ) error {
55
- // Compute trace info.
56
- var (
57
- traceID = req .Header .Get (TraceIDHeader )
58
- parentID = req .Header .Get (ParentSpanIDHeader )
59
- spanID = spanIDFunc ()
60
- )
139
+ // insert a new trace ID only if not already being traced.
140
+ traceID := req .Header .Get (TraceIDHeader )
61
141
if traceID == "" {
62
- // Avoid computing a random value if unnecessary.
63
- if sampleRate == 0 || rd .Intn (100 ) > sampleRate {
142
+ // insert tracing only within sample.
143
+ if sampler .Sample () {
144
+ traceID = o .traceIDFunc ()
145
+ } else {
64
146
return h (ctx , rw , req )
65
147
}
66
- traceID = traceIDFunc ()
67
148
}
68
149
69
- // Setup context.
150
+ // insert IDs into context to enable tracing.
151
+ spanID := o .spanIDFunc ()
152
+ parentID := req .Header .Get (ParentSpanIDHeader )
70
153
ctx = WithTrace (ctx , traceID , spanID , parentID )
71
-
72
- // Call next handler.
73
154
return h (ctx , rw , req )
74
155
}
75
156
}
76
157
}
77
158
159
+ // Tracer is deprecated in favor of NewTracer.
160
+ func Tracer (sampleRate int , spanIDFunc , traceIDFunc IDFunc ) goa.Middleware {
161
+ return NewTracer (SamplingPercent (sampleRate ), SpanIDFunc (spanIDFunc ), TraceIDFunc (traceIDFunc ))
162
+ }
163
+
78
164
// TraceDoer wraps a goa client Doer and sets the trace headers so that the
79
165
// downstream service may properly retrieve the parent span ID and trace ID.
80
166
func TraceDoer (doer client.Doer ) client.Doer {
0 commit comments