span.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. package trace
  2. import (
  3. "context"
  4. "fmt"
  5. "strconv"
  6. "strings"
  7. "time"
  8. "git.i2edu.net/i2/go-zero/core/stringx"
  9. "git.i2edu.net/i2/go-zero/core/timex"
  10. "git.i2edu.net/i2/go-zero/core/trace/tracespec"
  11. )
  12. const (
  13. initSpanId = "0"
  14. clientFlag = "client"
  15. serverFlag = "server"
  16. spanSepRune = '.'
  17. )
  18. var spanSep = string([]byte{spanSepRune})
  19. // A Span is a calling span that connects caller and callee.
  20. type Span struct {
  21. ctx spanContext
  22. serviceName string
  23. operationName string
  24. startTime time.Time
  25. flag string
  26. children int
  27. }
  28. func newServerSpan(carrier Carrier, serviceName, operationName string) tracespec.Trace {
  29. traceId := stringx.TakeWithPriority(func() string {
  30. if carrier != nil {
  31. return carrier.Get(traceIdKey)
  32. }
  33. return ""
  34. }, stringx.RandId)
  35. spanId := stringx.TakeWithPriority(func() string {
  36. if carrier != nil {
  37. return carrier.Get(spanIdKey)
  38. }
  39. return ""
  40. }, func() string {
  41. return initSpanId
  42. })
  43. return &Span{
  44. ctx: spanContext{
  45. traceId: traceId,
  46. spanId: spanId,
  47. },
  48. serviceName: serviceName,
  49. operationName: operationName,
  50. startTime: timex.Time(),
  51. flag: serverFlag,
  52. }
  53. }
  54. // Finish finishes the calling span.
  55. func (s *Span) Finish() {
  56. }
  57. // Follow follows the tracing service and operation names in context.
  58. func (s *Span) Follow(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
  59. span := &Span{
  60. ctx: spanContext{
  61. traceId: s.ctx.traceId,
  62. spanId: s.followSpanId(),
  63. },
  64. serviceName: serviceName,
  65. operationName: operationName,
  66. startTime: timex.Time(),
  67. flag: s.flag,
  68. }
  69. return context.WithValue(ctx, tracespec.TracingKey, span), span
  70. }
  71. // Fork forks the tracing service and operation names in context.
  72. func (s *Span) Fork(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
  73. span := &Span{
  74. ctx: spanContext{
  75. traceId: s.ctx.traceId,
  76. spanId: s.forkSpanId(),
  77. },
  78. serviceName: serviceName,
  79. operationName: operationName,
  80. startTime: timex.Time(),
  81. flag: clientFlag,
  82. }
  83. return context.WithValue(ctx, tracespec.TracingKey, span), span
  84. }
  85. // SpanId returns the span id.
  86. func (s *Span) SpanId() string {
  87. return s.ctx.SpanId()
  88. }
  89. // TraceId returns the trace id.
  90. func (s *Span) TraceId() string {
  91. return s.ctx.TraceId()
  92. }
  93. // Visit visits the span using fn.
  94. func (s *Span) Visit(fn func(key, val string) bool) {
  95. s.ctx.Visit(fn)
  96. }
  97. func (s *Span) forkSpanId() string {
  98. s.children++
  99. return fmt.Sprintf("%s.%d", s.ctx.spanId, s.children)
  100. }
  101. func (s *Span) followSpanId() string {
  102. fields := strings.FieldsFunc(s.ctx.spanId, func(r rune) bool {
  103. return r == spanSepRune
  104. })
  105. if len(fields) == 0 {
  106. return s.ctx.spanId
  107. }
  108. last := fields[len(fields)-1]
  109. val, err := strconv.Atoi(last)
  110. if err != nil {
  111. return s.ctx.spanId
  112. }
  113. last = strconv.Itoa(val + 1)
  114. fields[len(fields)-1] = last
  115. return strings.Join(fields, spanSep)
  116. }
  117. // StartClientSpan starts the client span with given context, service and operation names.
  118. func StartClientSpan(ctx context.Context, serviceName, operationName string) (context.Context, tracespec.Trace) {
  119. if span, ok := ctx.Value(tracespec.TracingKey).(*Span); ok {
  120. return span.Fork(ctx, serviceName, operationName)
  121. }
  122. return ctx, emptyNoopSpan
  123. }
  124. // StartServerSpan starts the server span with given context, carrier, service and operation names.
  125. func StartServerSpan(ctx context.Context, carrier Carrier, serviceName, operationName string) (
  126. context.Context, tracespec.Trace) {
  127. span := newServerSpan(carrier, serviceName, operationName)
  128. return context.WithValue(ctx, tracespec.TracingKey, span), span
  129. }