gitea源码

trace.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright 2025 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package gtprof
  4. import (
  5. "context"
  6. "fmt"
  7. "sync"
  8. "time"
  9. "code.gitea.io/gitea/modules/util"
  10. )
  11. type contextKey struct {
  12. name string
  13. }
  14. var contextKeySpan = &contextKey{"span"}
  15. type traceStarter interface {
  16. start(ctx context.Context, traceSpan *TraceSpan, internalSpanIdx int) (context.Context, traceSpanInternal)
  17. }
  18. type traceSpanInternal interface {
  19. addEvent(name string, cfg *EventConfig)
  20. recordError(err error, cfg *EventConfig)
  21. end()
  22. }
  23. type TraceSpan struct {
  24. // immutable
  25. parent *TraceSpan
  26. internalSpans []traceSpanInternal
  27. internalContexts []context.Context
  28. // mutable, must be protected by mutex
  29. mu sync.RWMutex
  30. name string
  31. statusCode uint32
  32. statusDesc string
  33. startTime time.Time
  34. endTime time.Time
  35. attributes []*TraceAttribute
  36. children []*TraceSpan
  37. }
  38. type TraceAttribute struct {
  39. Key string
  40. Value TraceValue
  41. }
  42. type TraceValue struct {
  43. v any
  44. }
  45. func (t *TraceValue) AsString() string {
  46. return fmt.Sprint(t.v)
  47. }
  48. func (t *TraceValue) AsInt64() int64 {
  49. v, _ := util.ToInt64(t.v)
  50. return v
  51. }
  52. func (t *TraceValue) AsFloat64() float64 {
  53. v, _ := util.ToFloat64(t.v)
  54. return v
  55. }
  56. var globalTraceStarters []traceStarter
  57. type Tracer struct {
  58. starters []traceStarter
  59. }
  60. func (s *TraceSpan) SetName(name string) {
  61. s.mu.Lock()
  62. defer s.mu.Unlock()
  63. s.name = name
  64. }
  65. func (s *TraceSpan) SetStatus(code uint32, desc string) {
  66. s.mu.Lock()
  67. defer s.mu.Unlock()
  68. s.statusCode, s.statusDesc = code, desc
  69. }
  70. func (s *TraceSpan) AddEvent(name string, options ...EventOption) {
  71. cfg := eventConfigFromOptions(options...)
  72. for _, tsp := range s.internalSpans {
  73. tsp.addEvent(name, cfg)
  74. }
  75. }
  76. func (s *TraceSpan) RecordError(err error, options ...EventOption) {
  77. cfg := eventConfigFromOptions(options...)
  78. for _, tsp := range s.internalSpans {
  79. tsp.recordError(err, cfg)
  80. }
  81. }
  82. func (s *TraceSpan) SetAttributeString(key, value string) *TraceSpan {
  83. s.mu.Lock()
  84. defer s.mu.Unlock()
  85. s.attributes = append(s.attributes, &TraceAttribute{Key: key, Value: TraceValue{v: value}})
  86. return s
  87. }
  88. func (t *Tracer) Start(ctx context.Context, spanName string) (context.Context, *TraceSpan) {
  89. starters := t.starters
  90. if starters == nil {
  91. starters = globalTraceStarters
  92. }
  93. ts := &TraceSpan{name: spanName, startTime: time.Now()}
  94. parentSpan := GetContextSpan(ctx)
  95. if parentSpan != nil {
  96. parentSpan.mu.Lock()
  97. parentSpan.children = append(parentSpan.children, ts)
  98. parentSpan.mu.Unlock()
  99. ts.parent = parentSpan
  100. }
  101. parentCtx := ctx
  102. for internalSpanIdx, tsp := range starters {
  103. var internalSpan traceSpanInternal
  104. if parentSpan != nil {
  105. parentCtx = parentSpan.internalContexts[internalSpanIdx]
  106. }
  107. ctx, internalSpan = tsp.start(parentCtx, ts, internalSpanIdx)
  108. ts.internalContexts = append(ts.internalContexts, ctx)
  109. ts.internalSpans = append(ts.internalSpans, internalSpan)
  110. }
  111. ctx = context.WithValue(ctx, contextKeySpan, ts)
  112. return ctx, ts
  113. }
  114. type mutableContext interface {
  115. context.Context
  116. SetContextValue(key, value any)
  117. GetContextValue(key any) any
  118. }
  119. // StartInContext starts a trace span in Gitea's mutable context (usually the web request context).
  120. // Due to the design limitation of Gitea's web framework, it can't use `context.WithValue` to bind a new span into a new context.
  121. // So here we use our "reqctx" framework to achieve the same result: web request context could always see the latest "span".
  122. func (t *Tracer) StartInContext(ctx mutableContext, spanName string) (*TraceSpan, func()) {
  123. curTraceSpan := GetContextSpan(ctx)
  124. _, newTraceSpan := GetTracer().Start(ctx, spanName)
  125. ctx.SetContextValue(contextKeySpan, newTraceSpan)
  126. return newTraceSpan, func() {
  127. newTraceSpan.End()
  128. ctx.SetContextValue(contextKeySpan, curTraceSpan)
  129. }
  130. }
  131. func (s *TraceSpan) End() {
  132. s.mu.Lock()
  133. s.endTime = time.Now()
  134. s.mu.Unlock()
  135. for _, tsp := range s.internalSpans {
  136. tsp.end()
  137. }
  138. }
  139. func GetTracer() *Tracer {
  140. return &Tracer{}
  141. }
  142. func GetContextSpan(ctx context.Context) *TraceSpan {
  143. ts, _ := ctx.Value(contextKeySpan).(*TraceSpan)
  144. return ts
  145. }