trpc.group/trpc-go/trpc-go@v1.0.3/rpcz/span.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package rpcz 15 16 import ( 17 "sync" 18 "time" 19 ) 20 21 // Ender tells an operation to end its work. 22 // Ender will return to caller while creating a Span by NewSpanContext or Span.NewChild, 23 // to remind the caller to call Ender.End when this Span is completed. 24 // Usually using type EndFunc func() is better than Ender interface. 25 // However, return a function, method, function literals: https://go.dev/ref/spec#Function_literals or closures 26 // can't avoid memory allocated on heap if them are not inlineable. https://github.com/golang/go/issues/28727 27 type Ender interface { 28 // End does not wait for the work to stop. 29 // End can only be called once. 30 // After the first call, subsequent calls to End is undefined behavior. 31 End() 32 } 33 34 // Span represents a unit of work or operation. 35 // It tracks specific operations that a request makes, 36 // painting a picture of what happened during the time in which that operation was executed. 37 type Span interface { 38 // AddEvent adds a event. 39 AddEvent(name string) 40 41 // Event returns the time when event happened. 42 Event(name string) (time.Time, bool) 43 44 // SetAttribute sets Attribute with (name, value). 45 SetAttribute(name string, value interface{}) 46 47 // Attribute gets the attribute value by the attribute name, 48 // and the returned result is a shallow copy of the attribute value. 49 // In general, you should not directly modify the return value, 50 // otherwise it may affect the associated Span, 51 // cause other goroutine to read dirty data from Span's Attribute. 52 Attribute(name string) (interface{}, bool) 53 54 // Name returns the name of Span. 55 Name() string 56 57 // ID returns SpanID. 58 ID() SpanID 59 60 // StartTime returns start time of the span. 61 StartTime() time.Time 62 63 // EndTime returns end time of the span. 64 EndTime() time.Time 65 66 // NewChild creates a child span from current span. 67 // Ender ends this span if related operation is completed. 68 NewChild(name string) (Span, Ender) 69 70 // Child returns the child of current span. 71 Child(name string) (Span, bool) 72 } 73 74 type recorder interface { 75 record(child *span) 76 } 77 78 type span struct { 79 m sync.Mutex // guards endTime, childSpans, events and attributes. 80 81 id SpanID 82 name string 83 parent recorder 84 85 // startTime is the time at which this span was started. 86 startTime time.Time 87 88 // endTime is the time at which this span was ended. It contains the zero 89 // value of time.Time until the span is ended. 90 endTime time.Time 91 92 // childSpans stores child span created from current span. 93 childSpans []*span 94 95 // events track event, used at codec and kinds of filter. 96 events []Event 97 98 // attributes records attributes 99 attributes []Attribute 100 } 101 102 func (s *span) AddEvent(name string) { 103 s.m.Lock() 104 defer s.m.Unlock() 105 s.events = append(s.events, Event{Name: name, Time: time.Now()}) 106 } 107 108 func (s *span) Event(name string) (time.Time, bool) { 109 s.m.Lock() 110 defer s.m.Unlock() 111 for i := len(s.events) - 1; i >= 0; i-- { 112 if s.events[i].Name == name { 113 return s.events[i].Time, true 114 } 115 } 116 return time.Time{}, false 117 } 118 119 func (s *span) SetAttribute(name string, value interface{}) { 120 s.m.Lock() 121 defer s.m.Unlock() 122 s.attributes = append(s.attributes, Attribute{Name: name, Value: value}) 123 } 124 125 func (s *span) Attribute(name string) (interface{}, bool) { 126 s.m.Lock() 127 defer s.m.Unlock() 128 return s.attribute(name) 129 } 130 131 // attribute should be guarded by mutex, and its concurrency is guaranteed by caller, 132 // and the returned interface{} is also not goroutine-safe. 133 func (s *span) attribute(name string) (interface{}, bool) { 134 for i := len(s.attributes) - 1; i >= 0; i-- { 135 if s.attributes[i].Name == name { 136 return s.attributes[i].Value, true 137 } 138 } 139 return nil, false 140 } 141 142 func (s *span) Name() string { 143 return s.name 144 } 145 146 func (s *span) ID() SpanID { 147 return s.id 148 } 149 150 func (s *span) StartTime() time.Time { 151 return s.startTime 152 } 153 154 func (s *span) EndTime() time.Time { 155 s.m.Lock() 156 defer s.m.Unlock() 157 return s.endTime 158 } 159 160 // End sets span's endTime and record span to its parent. 161 func (s *span) End() { 162 if s.trySetEndTime(time.Now()) { 163 s.parent.record(s) 164 } 165 } 166 167 func (s *span) trySetEndTime(t time.Time) bool { 168 s.m.Lock() 169 defer s.m.Unlock() 170 if !s.endTime.IsZero() { 171 return false 172 } 173 s.endTime = t 174 return true 175 } 176 177 func (s *span) record(childSpan *span) { 178 s.m.Lock() 179 defer s.m.Unlock() 180 181 if !s.hasChild(childSpan) { 182 putSpanToPool(childSpan) 183 } 184 } 185 186 func (s *span) hasChild(childSpan *span) bool { 187 for _, cs := range s.childSpans { 188 if cs == childSpan { 189 return true 190 } 191 } 192 return false 193 } 194 195 func (s *span) NewChild(name string) (Span, Ender) { 196 cs := newSpan(name, s.id, s) 197 198 s.m.Lock() 199 s.childSpans = append(s.childSpans, cs) 200 s.m.Unlock() 201 return cs, cs 202 } 203 204 func (s *span) Child(name string) (Span, bool) { 205 s.m.Lock() 206 defer s.m.Unlock() 207 for _, cs := range s.childSpans { 208 if cs.name == name { 209 return cs, true 210 } 211 } 212 return nil, false 213 } 214 215 func (s *span) isEnded() bool { 216 s.m.Lock() 217 defer s.m.Unlock() 218 return !s.endTime.IsZero() 219 } 220 221 func (s *span) convertedToReadOnlySpan() *ReadOnlySpan { 222 s.m.Lock() 223 defer s.m.Unlock() 224 readOnlySpan := &ReadOnlySpan{ 225 ID: s.id, 226 Name: s.name, 227 StartTime: s.startTime, 228 EndTime: s.endTime, 229 } 230 231 readOnlySpan.ChildSpans = make([]*ReadOnlySpan, len(s.childSpans)) 232 for i, cs := range s.childSpans { 233 readOnlySpan.ChildSpans[i] = cs.convertedToReadOnlySpan() 234 } 235 236 readOnlySpan.Attributes = make([]Attribute, len(s.attributes)) 237 copy(readOnlySpan.Attributes, s.attributes) 238 239 readOnlySpan.Events = make([]Event, len(s.events)) 240 copy(readOnlySpan.Events, s.events) 241 242 return readOnlySpan 243 } 244 245 var spanPool = sync.Pool{New: func() interface{} { return new(span) }} 246 247 // newSpan creates root or child span, if parent is nil, creates a root span. 248 func newSpan(name string, id SpanID, parent recorder) *span { 249 s := spanPool.Get().(*span) 250 s.id = id 251 s.name = name 252 s.parent = parent 253 s.startTime = time.Now() 254 return s 255 } 256 257 func putSpanToPool(s *span) { 258 s.m.Lock() 259 defer s.m.Unlock() 260 261 s.id = nilSpanID 262 s.name = "" 263 s.parent = nil 264 s.startTime = time.Time{} 265 s.endTime = time.Time{} 266 s.events = s.events[:0] 267 s.attributes = s.attributes[:0] 268 269 for _, cs := range s.childSpans { 270 if cs.isEnded() { 271 putSpanToPool(cs) 272 } 273 } 274 s.childSpans = s.childSpans[:0] 275 276 spanPool.Put(s) 277 }