1 package mocktracer
2
3 import (
4 "fmt"
5 "sync"
6 "sync/atomic"
7 "time"
8
9 "github.com/opentracing/opentracing-go"
10 "github.com/opentracing/opentracing-go/ext"
11 "github.com/opentracing/opentracing-go/log"
12 )
13
14
15
16
17
18
19
20
21 type MockSpanContext struct {
22 TraceID int
23 SpanID int
24 Sampled bool
25 Baggage map[string]string
26 }
27
28 var mockIDSource = uint32(42)
29
30 func nextMockID() int {
31 return int(atomic.AddUint32(&mockIDSource, 1))
32 }
33
34
35 func (c MockSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
36 for k, v := range c.Baggage {
37 if !handler(k, v) {
38 break
39 }
40 }
41 }
42
43
44 func (c MockSpanContext) WithBaggageItem(key, value string) MockSpanContext {
45 var newBaggage map[string]string
46 if c.Baggage == nil {
47 newBaggage = map[string]string{key: value}
48 } else {
49 newBaggage = make(map[string]string, len(c.Baggage)+1)
50 for k, v := range c.Baggage {
51 newBaggage[k] = v
52 }
53 newBaggage[key] = value
54 }
55
56 return MockSpanContext{c.TraceID, c.SpanID, c.Sampled, newBaggage}
57 }
58
59
60
61 type MockSpan struct {
62 sync.RWMutex
63
64 ParentID int
65
66 OperationName string
67 StartTime time.Time
68 FinishTime time.Time
69
70
71 SpanContext MockSpanContext
72 tags map[string]interface{}
73 logs []MockLogRecord
74 tracer *MockTracer
75 }
76
77 func newMockSpan(t *MockTracer, name string, opts opentracing.StartSpanOptions) *MockSpan {
78 tags := opts.Tags
79 if tags == nil {
80 tags = map[string]interface{}{}
81 }
82 traceID := nextMockID()
83 parentID := int(0)
84 var baggage map[string]string
85 sampled := true
86 if len(opts.References) > 0 {
87 traceID = opts.References[0].ReferencedContext.(MockSpanContext).TraceID
88 parentID = opts.References[0].ReferencedContext.(MockSpanContext).SpanID
89 sampled = opts.References[0].ReferencedContext.(MockSpanContext).Sampled
90 baggage = opts.References[0].ReferencedContext.(MockSpanContext).Baggage
91 }
92 spanContext := MockSpanContext{traceID, nextMockID(), sampled, baggage}
93 startTime := opts.StartTime
94 if startTime.IsZero() {
95 startTime = time.Now()
96 }
97 return &MockSpan{
98 ParentID: parentID,
99 OperationName: name,
100 StartTime: startTime,
101 tags: tags,
102 logs: []MockLogRecord{},
103 SpanContext: spanContext,
104
105 tracer: t,
106 }
107 }
108
109
110 func (s *MockSpan) Tags() map[string]interface{} {
111 s.RLock()
112 defer s.RUnlock()
113 tags := make(map[string]interface{})
114 for k, v := range s.tags {
115 tags[k] = v
116 }
117 return tags
118 }
119
120
121 func (s *MockSpan) Tag(k string) interface{} {
122 s.RLock()
123 defer s.RUnlock()
124 return s.tags[k]
125 }
126
127
128 func (s *MockSpan) Logs() []MockLogRecord {
129 s.RLock()
130 defer s.RUnlock()
131 logs := make([]MockLogRecord, len(s.logs))
132 copy(logs, s.logs)
133 return logs
134 }
135
136
137 func (s *MockSpan) Context() opentracing.SpanContext {
138 s.Lock()
139 defer s.Unlock()
140 return s.SpanContext
141 }
142
143
144 func (s *MockSpan) SetTag(key string, value interface{}) opentracing.Span {
145 s.Lock()
146 defer s.Unlock()
147 if key == string(ext.SamplingPriority) {
148 if v, ok := value.(uint16); ok {
149 s.SpanContext.Sampled = v > 0
150 return s
151 }
152 if v, ok := value.(int); ok {
153 s.SpanContext.Sampled = v > 0
154 return s
155 }
156 }
157 s.tags[key] = value
158 return s
159 }
160
161
162 func (s *MockSpan) SetBaggageItem(key, val string) opentracing.Span {
163 s.Lock()
164 defer s.Unlock()
165 s.SpanContext = s.SpanContext.WithBaggageItem(key, val)
166 return s
167 }
168
169
170 func (s *MockSpan) BaggageItem(key string) string {
171 s.RLock()
172 defer s.RUnlock()
173 return s.SpanContext.Baggage[key]
174 }
175
176
177 func (s *MockSpan) Finish() {
178 s.Lock()
179 s.FinishTime = time.Now()
180 s.Unlock()
181 s.tracer.recordFinishedSpan(s)
182 }
183
184
185 func (s *MockSpan) FinishWithOptions(opts opentracing.FinishOptions) {
186 s.Lock()
187 s.FinishTime = opts.FinishTime
188 s.Unlock()
189
190
191 for _, lr := range opts.LogRecords {
192 s.logFieldsWithTimestamp(lr.Timestamp, lr.Fields...)
193 }
194
195 for _, ld := range opts.BulkLogData {
196 if ld.Payload != nil {
197 s.logFieldsWithTimestamp(
198 ld.Timestamp,
199 log.String("event", ld.Event),
200 log.Object("payload", ld.Payload))
201 } else {
202 s.logFieldsWithTimestamp(
203 ld.Timestamp,
204 log.String("event", ld.Event))
205 }
206 }
207
208 s.tracer.recordFinishedSpan(s)
209 }
210
211
212 func (s *MockSpan) String() string {
213 return fmt.Sprintf(
214 "traceId=%d, spanId=%d, parentId=%d, sampled=%t, name=%s",
215 s.SpanContext.TraceID, s.SpanContext.SpanID, s.ParentID,
216 s.SpanContext.Sampled, s.OperationName)
217 }
218
219
220 func (s *MockSpan) LogFields(fields ...log.Field) {
221 s.logFieldsWithTimestamp(time.Now(), fields...)
222 }
223
224
225 func (s *MockSpan) logFieldsWithTimestamp(ts time.Time, fields ...log.Field) {
226 lr := MockLogRecord{
227 Timestamp: ts,
228 Fields: make([]MockKeyValue, len(fields)),
229 }
230 for i, f := range fields {
231 outField := &(lr.Fields[i])
232 f.Marshal(outField)
233 }
234
235 s.Lock()
236 defer s.Unlock()
237 s.logs = append(s.logs, lr)
238 }
239
240
241
242
243
244
245 func (s *MockSpan) LogKV(keyValues ...interface{}) {
246 if len(keyValues)%2 != 0 {
247 s.LogFields(log.Error(fmt.Errorf("Non-even keyValues len: %v", len(keyValues))))
248 return
249 }
250 fields, err := log.InterleavedKVToFields(keyValues...)
251 if err != nil {
252 s.LogFields(log.Error(err), log.String("function", "LogKV"))
253 return
254 }
255 s.LogFields(fields...)
256 }
257
258
259 func (s *MockSpan) LogEvent(event string) {
260 s.LogFields(log.String("event", event))
261 }
262
263
264 func (s *MockSpan) LogEventWithPayload(event string, payload interface{}) {
265 s.LogFields(log.String("event", event), log.Object("payload", payload))
266 }
267
268
269 func (s *MockSpan) Log(data opentracing.LogData) {
270 panic("MockSpan.Log() no longer supported")
271 }
272
273
274 func (s *MockSpan) SetOperationName(operationName string) opentracing.Span {
275 s.Lock()
276 defer s.Unlock()
277 s.OperationName = operationName
278 return s
279 }
280
281
282 func (s *MockSpan) Tracer() opentracing.Tracer {
283 return s.tracer
284 }
285
View as plain text