Codebase list golang-github-go-kit-kit / a4dd947
refactor(tracing.opentracing): add TraceEndpoint function - TraceServer and TraceClient should differ only in span kind tag value - remain TraceServer and TraceClient for backward compatibility Alexander Babai 3 years ago
2 changed file(s) with 103 addition(s) and 295 deletion(s). Raw diff Collapse all Expand all
88 "github.com/go-kit/kit/endpoint"
99 )
1010
11 // TraceServer returns a Middleware that wraps the `next` Endpoint in an
11 // TraceEndpoint returns a Middleware that wraps the `next` Endpoint in an
1212 // OpenTracing Span called `operationName`.
1313 //
14 // If `ctx` already has a Span, it is re-used and the operation name is
15 // overwritten. If `ctx` does not yet have a Span, one is created here.
16 func TraceServer(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware {
17 cfg := &EndpointOptions{}
14 // If `ctx` already has a Span, child span is created from it.
15 // If `ctx` doesn't yet have a Span, the new one is created.
16 func TraceEndpoint(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware {
17 cfg := &EndpointOptions{
18 Tags: make(opentracing.Tags),
19 }
1820
1921 for _, opt := range opts {
2022 opt(cfg)
2830 }
2931 }
3032
31 serverSpan := opentracing.SpanFromContext(ctx)
32 if serverSpan == nil {
33 // All we can do is create a new root span.
34 serverSpan = tracer.StartSpan(operationName)
33 span := opentracing.SpanFromContext(ctx)
34 if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {
35 span = tracer.StartSpan(
36 operationName,
37 opentracing.ChildOf(parentSpan.Context()),
38 )
3539 } else {
36 serverSpan.SetOperationName(operationName)
40 span = tracer.StartSpan(operationName)
3741 }
38 defer serverSpan.Finish()
39 ext.SpanKindRPCServer.Set(serverSpan)
40 ctx = opentracing.ContextWithSpan(ctx, serverSpan)
42 defer span.Finish()
43 ctx = opentracing.ContextWithSpan(ctx, span)
4144
42 applyTags(serverSpan, cfg.Tags)
45 applyTags(span, cfg.Tags)
4346 if cfg.GetTags != nil {
4447 extraTags := cfg.GetTags(ctx)
45 applyTags(serverSpan, extraTags)
48 applyTags(span, extraTags)
4649 }
4750
4851 response, err := next(ctx, request)
4952 if err := identifyError(response, err, cfg.IgnoreBusinessError); err != nil {
50 ext.LogError(serverSpan, err)
53 ext.LogError(span, err)
5154 }
5255
5356 return response, err
5558 }
5659 }
5760
61 // TraceServer returns a Middleware that wraps the `next` Endpoint in an
62 // OpenTracing Span called `operationName` with server span.kind tag..
63 func TraceServer(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware {
64 opts = append(opts, WithTags(map[string]interface{}{
65 ext.SpanKindRPCServer.Key: ext.SpanKindRPCServer.Value,
66 }))
67
68 return TraceEndpoint(tracer, operationName, opts...)
69 }
70
5871 // TraceClient returns a Middleware that wraps the `next` Endpoint in an
59 // OpenTracing Span called `operationName`.
72 // OpenTracing Span called `operationName` with client span.kind tag.
6073 func TraceClient(tracer opentracing.Tracer, operationName string, opts ...EndpointOption) endpoint.Middleware {
61 cfg := &EndpointOptions{}
74 opts = append(opts, WithTags(map[string]interface{}{
75 ext.SpanKindRPCServer.Key: ext.SpanKindRPCClient.Value,
76 }))
6277
63 for _, opt := range opts {
64 opt(cfg)
65 }
66
67 return func(next endpoint.Endpoint) endpoint.Endpoint {
68 return func(ctx context.Context, request interface{}) (interface{}, error) {
69 if cfg.GetOperationName != nil {
70 if newOperationName := cfg.GetOperationName(ctx, operationName); newOperationName != "" {
71 operationName = newOperationName
72 }
73 }
74
75 var clientSpan opentracing.Span
76 if parentSpan := opentracing.SpanFromContext(ctx); parentSpan != nil {
77 clientSpan = tracer.StartSpan(
78 operationName,
79 opentracing.ChildOf(parentSpan.Context()),
80 )
81 } else {
82 clientSpan = tracer.StartSpan(operationName)
83 }
84 defer clientSpan.Finish()
85 ext.SpanKindRPCClient.Set(clientSpan)
86 ctx = opentracing.ContextWithSpan(ctx, clientSpan)
87
88 applyTags(clientSpan, cfg.Tags)
89 if cfg.GetTags != nil {
90 extraTags := cfg.GetTags(ctx)
91 applyTags(clientSpan, extraTags)
92 }
93
94 response, err := next(ctx, request)
95 if err := identifyError(response, err, cfg.IgnoreBusinessError); err != nil {
96 ext.LogError(clientSpan, err)
97 }
98
99 return response, err
100 }
101 }
78 return TraceEndpoint(tracer, operationName, opts...)
10279 }
10380
10481 func applyTags(span opentracing.Span, tags opentracing.Tags) {
4040 return r.err
4141 }
4242
43 func TestTraceServer(t *testing.T) {
44 tracer := mocktracer.New()
45
46 // Initialize the ctx with a nameless Span.
47 contextSpan := tracer.StartSpan("").(*mocktracer.MockSpan)
48 ctx := opentracing.ContextWithSpan(context.Background(), contextSpan)
49
50 tracedEndpoint := kitot.TraceServer(tracer, "testOp")(endpoint.Nop)
43 func TestTraceEndpoint(t *testing.T) {
44 tracer := mocktracer.New()
45
46 // Initialize the ctx with a parent Span.
47 parentSpan := tracer.StartSpan("parent").(*mocktracer.MockSpan)
48 defer parentSpan.Finish()
49 ctx := opentracing.ContextWithSpan(context.Background(), parentSpan)
50
51 tracedEndpoint := kitot.TraceEndpoint(tracer, "testOp")(endpoint.Nop)
5152 if _, err := tracedEndpoint(ctx, struct{}{}); err != nil {
5253 t.Fatal(err)
5354 }
5455
56 // tracedEndpoint created a new Span.
5557 finishedSpans := tracer.FinishedSpans()
5658 if want, have := 1, len(finishedSpans); want != have {
5759 t.Fatalf("Want %v span(s), found %v", want, have)
5860 }
5961
60 // Test that the op name is updated
6162 endpointSpan := finishedSpans[0]
6263 if want, have := "testOp", endpointSpan.OperationName; want != have {
6364 t.Fatalf("Want %q, have %q", want, have)
6465 }
65 contextContext := contextSpan.Context().(mocktracer.MockSpanContext)
66 endpointContext := endpointSpan.Context().(mocktracer.MockSpanContext)
67 // ...and that the ID is unmodified.
68 if want, have := contextContext.SpanID, endpointContext.SpanID; want != have {
69 t.Errorf("Want SpanID %q, have %q", want, have)
70 }
71 }
72
73 func TestTraceServerNoContextSpan(t *testing.T) {
66
67 parentContext := parentSpan.Context().(mocktracer.MockSpanContext)
68 endpointContext := parentSpan.Context().(mocktracer.MockSpanContext)
69
70 // ... and that the parent ID is set appropriately.
71 if want, have := parentContext.SpanID, endpointContext.SpanID; want != have {
72 t.Errorf("Want ParentID %q, have %q", want, have)
73 }
74 }
75
76 func TestTraceEndpointNoContextSpan(t *testing.T) {
7477 tracer := mocktracer.New()
7578
7679 // Empty/background context.
77 tracedEndpoint := kitot.TraceServer(tracer, "testOp")(endpoint.Nop)
80 tracedEndpoint := kitot.TraceEndpoint(tracer, "testOp")(endpoint.Nop)
7881 if _, err := tracedEndpoint(context.Background(), struct{}{}); err != nil {
7982 t.Fatal(err)
8083 }
8689 }
8790
8891 endpointSpan := finishedSpans[0]
92
8993 if want, have := "testOp", endpointSpan.OperationName; want != have {
9094 t.Fatalf("Want %q, have %q", want, have)
9195 }
9296 }
9397
94 func TestTraceServerWithOptions(t *testing.T) {
98 func TestTraceEndpointWithOptions(t *testing.T) {
9599 tracer := mocktracer.New()
96100
97101 // span 1 without options
98 mw := kitot.TraceServer(tracer, span1)
102 mw := kitot.TraceEndpoint(tracer, span1)
99103 tracedEndpoint := mw(endpoint.Nop)
100104 _, _ = tracedEndpoint(context.Background(), struct{}{})
101105
102106 // span 2 with options
103 mw = kitot.TraceServer(
107 mw = kitot.TraceEndpoint(
104108 tracer,
105109 span2,
106110 kitot.WithOptions(kitot.EndpointOptions{}),
111115 _, _ = tracedEndpoint(context.Background(), struct{}{})
112116
113117 // span 3 with disabled IgnoreBusinessError option
114 mw = kitot.TraceServer(
118 mw = kitot.TraceEndpoint(
115119 tracer,
116120 span3,
117121 kitot.WithIgnoreBusinessError(false),
124128 _, _ = tracedEndpoint(context.Background(), struct{}{})
125129
126130 // span 4 with enabled IgnoreBusinessError option
127 mw = kitot.TraceServer(tracer, span4, kitot.WithIgnoreBusinessError(true))
131 mw = kitot.TraceEndpoint(tracer, span4, kitot.WithIgnoreBusinessError(true))
128132 tracedEndpoint = mw(func(context.Context, interface{}) (interface{}, error) {
129133 return failedResponse{
130134 err: err3,
133137 _, _ = tracedEndpoint(context.Background(), struct{}{})
134138
135139 // span 5 with OperationNameFunc option
136 mw = kitot.TraceServer(
140 mw = kitot.TraceEndpoint(
137141 tracer,
138142 span5,
139143 kitot.WithOperationNameFunc(func(ctx context.Context, name string) string {
144148 _, _ = tracedEndpoint(context.Background(), struct{}{})
145149
146150 // span 6 with Tags options
147 mw = kitot.TraceServer(
151 mw = kitot.TraceEndpoint(
148152 tracer,
149153 span6,
150154 kitot.WithTags(map[string]interface{}{
159163 _, _ = tracedEndpoint(context.Background(), struct{}{})
160164
161165 // span 7 with TagsFunc options
162 mw = kitot.TraceServer(
166 mw = kitot.TraceEndpoint(
163167 tracer,
164168 span7,
165169 kitot.WithTags(map[string]interface{}{
238242 }
239243
240244 if want, have := map[string]interface{}{
241 "span.kind": ext.SpanKindRPCServerEnum,
242 "tag1": "tag1",
243 "tag2": "tag2",
244 "tag3": "tag3",
245 "tag1": "tag1",
246 "tag2": "tag2",
247 "tag3": "tag3",
245248 }, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {
246249 t.Fatalf("Want %q, have %q", want, have)
247250 }
254257 }
255258
256259 if want, have := map[string]interface{}{
257 "span.kind": ext.SpanKindRPCServerEnum,
258 "tag1": "tag1",
259 "tag2": "tag2",
260 "tag3": "tag3",
261 "tag4": "tag4",
260 "tag1": "tag1",
261 "tag2": "tag2",
262 "tag3": "tag3",
263 "tag4": "tag4",
262264 }, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {
263265 t.Fatalf("Want %q, have %q", want, have)
264266 }
265267 }
266268
269 func TestTraceServer(t *testing.T) {
270 tracer := mocktracer.New()
271
272 // Empty/background context.
273 tracedEndpoint := kitot.TraceServer(tracer, "testOp")(endpoint.Nop)
274 if _, err := tracedEndpoint(context.Background(), struct{}{}); err != nil {
275 t.Fatal(err)
276 }
277
278 // tracedEndpoint created a new Span.
279 finishedSpans := tracer.FinishedSpans()
280 if want, have := 1, len(finishedSpans); want != have {
281 t.Fatalf("Want %v span(s), found %v", want, have)
282 }
283
284 span := finishedSpans[0]
285
286 if want, have := "testOp", span.OperationName; want != have {
287 t.Fatalf("Want %q, have %q", want, have)
288 }
289
290 if want, have := map[string]interface{}{
291 ext.SpanKindRPCServer.Key: ext.SpanKindRPCServer.Value,
292 }, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {
293 t.Fatalf("Want %q, have %q", want, have)
294 }
295 }
296
267297 func TestTraceClient(t *testing.T) {
268 tracer := mocktracer.New()
269
270 // Initialize the ctx with a parent Span.
271 parentSpan := tracer.StartSpan("parent").(*mocktracer.MockSpan)
272 defer parentSpan.Finish()
273 ctx := opentracing.ContextWithSpan(context.Background(), parentSpan)
274
275 tracedEndpoint := kitot.TraceClient(tracer, "testOp")(endpoint.Nop)
276 if _, err := tracedEndpoint(ctx, struct{}{}); err != nil {
277 t.Fatal(err)
278 }
279
280 // tracedEndpoint created a new Span.
281 finishedSpans := tracer.FinishedSpans()
282 if want, have := 1, len(finishedSpans); want != have {
283 t.Fatalf("Want %v span(s), found %v", want, have)
284 }
285
286 endpointSpan := finishedSpans[0]
287 if want, have := "testOp", endpointSpan.OperationName; want != have {
288 t.Fatalf("Want %q, have %q", want, have)
289 }
290
291 parentContext := parentSpan.Context().(mocktracer.MockSpanContext)
292 endpointContext := parentSpan.Context().(mocktracer.MockSpanContext)
293
294 // ... and that the parent ID is set appropriately.
295 if want, have := parentContext.SpanID, endpointContext.SpanID; want != have {
296 t.Errorf("Want ParentID %q, have %q", want, have)
297 }
298 }
299
300 func TestTraceClientNoContextSpan(t *testing.T) {
301298 tracer := mocktracer.New()
302299
303300 // Empty/background context.
312309 t.Fatalf("Want %v span(s), found %v", want, have)
313310 }
314311
315 endpointSpan := finishedSpans[0]
316 if want, have := "testOp", endpointSpan.OperationName; want != have {
317 t.Fatalf("Want %q, have %q", want, have)
318 }
319 }
320
321 func TestTraceClientWithOptions(t *testing.T) {
322 tracer := mocktracer.New()
323
324 // span 1 without options
325 mw := kitot.TraceClient(tracer, span1)
326 tracedEndpoint := mw(endpoint.Nop)
327 _, _ = tracedEndpoint(context.Background(), struct{}{})
328
329 // span 2 with options
330 mw = kitot.TraceClient(
331 tracer,
332 span2,
333 kitot.WithOptions(kitot.EndpointOptions{}),
334 )
335 tracedEndpoint = mw(func(context.Context, interface{}) (interface{}, error) {
336 return nil, err1
337 })
338 _, _ = tracedEndpoint(context.Background(), struct{}{})
339
340 // span 3 with disabled IgnoreBusinessError option
341 mw = kitot.TraceClient(
342 tracer,
343 span3,
344 kitot.WithIgnoreBusinessError(false),
345 )
346 tracedEndpoint = mw(func(context.Context, interface{}) (interface{}, error) {
347 return failedResponse{
348 err: err2,
349 }, nil
350 })
351 _, _ = tracedEndpoint(context.Background(), struct{}{})
352
353 // span 4 with enabled IgnoreBusinessError option
354 mw = kitot.TraceClient(tracer, span4, kitot.WithIgnoreBusinessError(true))
355 tracedEndpoint = mw(func(context.Context, interface{}) (interface{}, error) {
356 return failedResponse{
357 err: err3,
358 }, nil
359 })
360 _, _ = tracedEndpoint(context.Background(), struct{}{})
361
362 // span 5 with OperationNameFunc option
363 mw = kitot.TraceClient(
364 tracer,
365 span5,
366 kitot.WithOperationNameFunc(func(ctx context.Context, name string) string {
367 return fmt.Sprintf("%s-%s", "new", name)
368 }),
369 )
370 tracedEndpoint = mw(endpoint.Nop)
371 _, _ = tracedEndpoint(context.Background(), struct{}{})
372
373 // span 6 with Tags options
374 mw = kitot.TraceClient(
375 tracer,
376 span6,
377 kitot.WithTags(map[string]interface{}{
378 "tag1": "tag1",
379 "tag2": "tag2",
380 }),
381 kitot.WithTags(map[string]interface{}{
382 "tag3": "tag3",
383 }),
384 )
385 tracedEndpoint = mw(endpoint.Nop)
386 _, _ = tracedEndpoint(context.Background(), struct{}{})
387
388 // span 7 with TagsFunc options
389 mw = kitot.TraceClient(
390 tracer,
391 span7,
392 kitot.WithTags(map[string]interface{}{
393 "tag1": "tag1",
394 "tag2": "tag2",
395 }),
396 kitot.WithTags(map[string]interface{}{
397 "tag3": "tag3",
398 }),
399 kitot.WithTagsFunc(func(ctx context.Context) opentracing.Tags {
400 return map[string]interface{}{
401 "tag4": "tag4",
402 }
403 }),
404 )
405 tracedEndpoint = mw(endpoint.Nop)
406 _, _ = tracedEndpoint(context.Background(), struct{}{})
407
408 finishedSpans := tracer.FinishedSpans()
409 if want, have := 7, len(finishedSpans); want != have {
410 t.Fatalf("Want %v span(s), found %v", want, have)
411 }
412
413 // test span 1
414312 span := finishedSpans[0]
415313
416 if want, have := span1, span.OperationName; want != have {
417 t.Fatalf("Want %q, have %q", want, have)
418 }
419
420 // test span 2
421 span = finishedSpans[1]
422
423 if want, have := span2, span.OperationName; want != have {
424 t.Fatalf("Want %q, have %q", want, have)
425 }
426
427 if want, have := true, span.Tag("error"); want != have {
428 t.Fatalf("Want %v, have %v", want, have)
429 }
430
431 // test span 3
432 span = finishedSpans[2]
433
434 if want, have := span3, span.OperationName; want != have {
435 t.Fatalf("Want %q, have %q", want, have)
436 }
437
438 if want, have := true, span.Tag("error"); want != have {
439 t.Fatalf("Want %v, have %v", want, have)
440 }
441
442 // test span 4
443 span = finishedSpans[3]
444
445 if want, have := span4, span.OperationName; want != have {
446 t.Fatalf("Want %q, have %q", want, have)
447 }
448
449 if want, have := (interface{})(nil), span.Tag("error"); want != have {
450 t.Fatalf("Want %q, have %q", want, have)
451 }
452
453 // test span 5
454 span = finishedSpans[4]
455
456 if want, have := fmt.Sprintf("%s-%s", "new", span5), span.OperationName; want != have {
457 t.Fatalf("Want %q, have %q", want, have)
458 }
459
460 // test span 6
461 span = finishedSpans[5]
462
463 if want, have := span6, span.OperationName; want != have {
314 if want, have := "testOp", span.OperationName; want != have {
464315 t.Fatalf("Want %q, have %q", want, have)
465316 }
466317
467318 if want, have := map[string]interface{}{
468 "span.kind": ext.SpanKindRPCClientEnum,
469 "tag1": "tag1",
470 "tag2": "tag2",
471 "tag3": "tag3",
319 ext.SpanKindRPCClient.Key: ext.SpanKindRPCClient.Value,
472320 }, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {
473321 t.Fatalf("Want %q, have %q", want, have)
474322 }
475
476 // test span 7
477 span = finishedSpans[6]
478
479 if want, have := span7, span.OperationName; want != have {
480 t.Fatalf("Want %q, have %q", want, have)
481 }
482
483 if want, have := map[string]interface{}{
484 "span.kind": ext.SpanKindRPCClientEnum,
485 "tag1": "tag1",
486 "tag2": "tag2",
487 "tag3": "tag3",
488 "tag4": "tag4",
489 }, span.Tags(); fmt.Sprint(want) != fmt.Sprint(have) {
490 t.Fatalf("Want %q, have %q", want, have)
491 }
492 }
323 }