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  }