context.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package tracing
  2. import "context"
  3. type (
  4. operationTracerKey struct{}
  5. spanLineageKey struct{}
  6. )
  7. // GetSpan returns the active trace Span on the context.
  8. //
  9. // The boolean in the return indicates whether a Span was actually in the
  10. // context, but a no-op implementation will be returned if not, so callers
  11. // can generally disregard the boolean unless they wish to explicitly confirm
  12. // presence/absence of a Span.
  13. func GetSpan(ctx context.Context) (Span, bool) {
  14. lineage := getLineage(ctx)
  15. if len(lineage) == 0 {
  16. return nopSpan{}, false
  17. }
  18. return lineage[len(lineage)-1], true
  19. }
  20. // WithSpan sets the active trace Span on the context.
  21. func WithSpan(parent context.Context, span Span) context.Context {
  22. lineage := getLineage(parent)
  23. if len(lineage) == 0 {
  24. return context.WithValue(parent, spanLineageKey{}, []Span{span})
  25. }
  26. lineage = append(lineage, span)
  27. return context.WithValue(parent, spanLineageKey{}, lineage)
  28. }
  29. // PopSpan pops the current Span off the context, setting the active Span on
  30. // the returned Context back to its parent and returning the REMOVED one.
  31. //
  32. // PopSpan on a context with no active Span will return a no-op instance.
  33. //
  34. // This is mostly necessary for the runtime to manage base trace spans due to
  35. // the wrapped-function nature of the middleware stack. End-users of Smithy
  36. // clients SHOULD NOT generally be using this API.
  37. func PopSpan(parent context.Context) (context.Context, Span) {
  38. lineage := getLineage(parent)
  39. if len(lineage) == 0 {
  40. return parent, nopSpan{}
  41. }
  42. span := lineage[len(lineage)-1]
  43. lineage = lineage[:len(lineage)-1]
  44. return context.WithValue(parent, spanLineageKey{}, lineage), span
  45. }
  46. func getLineage(ctx context.Context) []Span {
  47. v := ctx.Value(spanLineageKey{})
  48. if v == nil {
  49. return nil
  50. }
  51. return v.([]Span)
  52. }
  53. // GetOperationTracer returns the embedded operation-scoped Tracer on a
  54. // Context.
  55. //
  56. // The boolean in the return indicates whether a Tracer was actually in the
  57. // context, but a no-op implementation will be returned if not, so callers
  58. // can generally disregard the boolean unless they wish to explicitly confirm
  59. // presence/absence of a Tracer.
  60. func GetOperationTracer(ctx context.Context) (Tracer, bool) {
  61. v := ctx.Value(operationTracerKey{})
  62. if v == nil {
  63. return nopTracer{}, false
  64. }
  65. return v.(Tracer), true
  66. }
  67. // WithOperationTracer returns a child Context embedding the given Tracer.
  68. //
  69. // The runtime will use this embed a scoped tracer for client operations,
  70. // Smithy/SDK client callers DO NOT need to do this explicitly.
  71. func WithOperationTracer(parent context.Context, tracer Tracer) context.Context {
  72. return context.WithValue(parent, operationTracerKey{}, tracer)
  73. }
  74. // StartSpan is a convenience API for creating tracing Spans from a Context.
  75. //
  76. // StartSpan uses the operation-scoped Tracer, previously stored using
  77. // [WithOperationTracer], to start the Span. If a Tracer has not been embedded
  78. // the returned Span will be a no-op implementation.
  79. func StartSpan(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) {
  80. tracer, _ := GetOperationTracer(ctx)
  81. return tracer.StartSpan(ctx, name, opts...)
  82. }