-
-
Notifications
You must be signed in to change notification settings - Fork 559
/
trace.go
181 lines (162 loc) Β· 5.27 KB
/
trace.go
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package middleware
import (
"context"
"regexp"
)
type (
// IDFunc is a function that produces span and trace IDs for consumption
// by tracing systems such as Zipkin or AWS X-Ray.
IDFunc func() string
// TraceOption is a constructor option that makes it possible to customize
// the middleware.
TraceOption func(*TraceOptions) *TraceOptions
// TraceOptions is the struct storing all the options for trace middleware.
TraceOptions struct {
traceIDFunc IDFunc
spanIDFunc IDFunc
samplingPercent int
maxSamplingRate int
sampleSize int
discards []*regexp.Regexp
}
// tracedLogger is a logger which logs the trace ID with every log entry
// when one is present.
tracedLogger struct {
logger Logger
traceID string
}
)
// NewTraceOptions returns the trace middleware options by running the given
// constructors.
func NewTraceOptions(opts ...TraceOption) *TraceOptions {
o := &TraceOptions{
traceIDFunc: shortID,
spanIDFunc: shortID,
samplingPercent: 100,
// Below only apply if maxSamplingRate is set
sampleSize: 1000,
}
for _, opt := range opts {
o = opt(o)
}
return o
}
// NewSampler returns a Sampler. If maxSamplingRate is positive it returns
// an adaptive sampler or else it returns a fixed sampler.
func (o *TraceOptions) NewSampler() Sampler {
if o.maxSamplingRate > 0 {
return NewAdaptiveSampler(o.maxSamplingRate, o.sampleSize)
}
return NewFixedSampler(o.samplingPercent)
}
// TraceID returns a new trace ID. Use TraceIDFunc to set the function that
// generates trace IDs.
func (o *TraceOptions) TraceID() string {
return o.traceIDFunc()
}
// SpanID returns a new span ID. Use SpanIDFunc to set the function that
// generates span IDs.
func (o *TraceOptions) SpanID() string {
return o.spanIDFunc()
}
// Discards returns the list of regular expressions used to match paths to be
// discarded from tracing.
func (o *TraceOptions) Discards() []*regexp.Regexp {
return o.discards
}
// TraceIDFunc configures the function used to compute trace IDs. Use this
// option to generate IDs compatible with backend tracing systems
// (e.g. AWS XRay).
func TraceIDFunc(f IDFunc) TraceOption {
return func(o *TraceOptions) *TraceOptions {
o.traceIDFunc = f
return o
}
}
// SpanIDFunc configures the function used to compute span IDs. Use this
// option to generate IDs compatible with backend tracing systems
// (e.g. AWS XRay).
func SpanIDFunc(f IDFunc) TraceOption {
return func(o *TraceOptions) *TraceOptions {
o.spanIDFunc = f
return o
}
}
// SamplingPercent configures the percentage of requests that should be traced.
// If the incoming request has a Trace ID the sampling rate is disregarded and
// tracing is enabled. It sets the tracing sampling rate as a percentage value.
// It panics if p is less than 0 or more than 100. SamplingPercent and
// MaxSamplingRate are mutually exclusive.
func SamplingPercent(p int) TraceOption {
if p < 0 || p > 100 {
panic("sampling rate must be between 0 and 100")
}
return func(o *TraceOptions) *TraceOptions {
o.samplingPercent = p
return o
}
}
// MaxSamplingRate sets a target sampling rate in requests per second. Setting a
// max sampling rate causes the middleware to adjust the sampling percent
// dynamically. Defaults to 2 req/s. SamplingPercent and MaxSamplingRate are
// mutually exclusive.
func MaxSamplingRate(r int) TraceOption {
if r <= 0 {
panic("max sampling rate must be greater than 0")
}
return func(o *TraceOptions) *TraceOptions {
o.maxSamplingRate = r
return o
}
}
// SampleSize sets the number of requests between two adjustments of the sampling
// rate when MaxSamplingRate is set. Defaults to 1,000.
func SampleSize(s int) TraceOption {
if s <= 0 {
panic("sample size must be greater than 0")
}
return func(o *TraceOptions) *TraceOptions {
o.sampleSize = s
return o
}
}
// DiscardFromTrace adds a regular expression for matching a request path to be discarded from tracing.
// this is useful for frequent API calls that are not important to trace, such as health checks.
// the pattern can be a full or partial match and could even support both HTTP and gRPC paths with an
// OR expression, etc.
//
// note that discards can be overridden if the incoming request already has a trace ID.
func DiscardFromTrace(discard *regexp.Regexp) TraceOption {
if discard == nil {
panic("discard cannot be nil")
}
return func(o *TraceOptions) *TraceOptions {
o.discards = append(o.discards, discard)
return o
}
}
// WithSpan returns a context containing the given trace, span and parent span
// IDs.
func WithSpan(ctx context.Context, traceID, spanID, parentID string) context.Context {
if parentID != "" {
ctx = context.WithValue(ctx, TraceParentSpanIDKey, parentID)
}
ctx = context.WithValue(ctx, TraceIDKey, traceID)
ctx = context.WithValue(ctx, TraceSpanIDKey, spanID)
return ctx
}
// WrapLogger returns a logger which logs the trace ID with every message if
// there is one.
func WrapLogger(l Logger, traceID string) Logger {
return &tracedLogger{logger: l, traceID: traceID}
}
// Log logs the trace ID when present then the values passed as argument.
func (l *tracedLogger) Log(keyvals ...interface{}) error {
if l.traceID == "" {
l.logger.Log(keyvals...)
return nil
}
keyvals = append([]interface{}{"trace", l.traceID}, keyvals...)
l.logger.Log(keyvals)
return nil
}