github.com/pinpoint-apm/pinpoint-go-agent@v1.4.1-0.20240110120318-a50c2eb18c8c/span.go (about)

     1  package pinpoint
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math/rand"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"sync/atomic"
    13  	"time"
    14  )
    15  
    16  const (
    17  	apiTypeDefault       = 0
    18  	apiTypeWebRequest    = 100
    19  	apiTypeInvocation    = 200
    20  	noneAsyncId          = 0
    21  	minEventDepth        = 2
    22  	minEventSequence     = 4
    23  	defaultEventDepth    = 64
    24  	defaultEventSequence = 5000
    25  )
    26  
    27  var (
    28  	asyncIdGen int32 = 0
    29  )
    30  
    31  type span struct {
    32  	agent              *agent
    33  	txId               TransactionId
    34  	spanId             int64
    35  	parentSpanId       int64
    36  	parentAppName      string
    37  	parentAppType      int
    38  	parentAppNamespace string
    39  	serviceType        int32
    40  	rpcName            string
    41  	endPoint           string
    42  	remoteAddr         string
    43  	acceptorHost       string
    44  	spanEvents         []*spanEvent
    45  	annotations        annotation
    46  	loggingInfo        int32
    47  	apiId              int32
    48  
    49  	eventSequence    int32
    50  	eventDepth       int32
    51  	eventOverflow    int
    52  	eventOverflowLog bool
    53  
    54  	startTime     time.Time
    55  	elapsed       int64
    56  	operationName string
    57  	flags         int
    58  	err           int
    59  	errorFuncId   int32
    60  	errorString   string
    61  	recovered     bool
    62  	asyncId       int32
    63  	asyncSequence int32
    64  	goroutineId   int64
    65  	eventStack    *stack
    66  	appendLock    sync.Mutex
    67  	urlStat       *UrlStatEntry
    68  	errorChains   []*exception
    69  }
    70  
    71  func generateSpanId() int64 {
    72  	return rand.Int63()
    73  }
    74  
    75  func defaultSpan() *span {
    76  	span := span{}
    77  
    78  	span.parentSpanId = -1
    79  	span.parentAppName = ""
    80  	span.parentAppType = 1 //UNKNOWN
    81  	span.eventDepth = 1
    82  	span.serviceType = ServiceTypeGoApp
    83  	span.startTime = time.Now()
    84  	span.goroutineId = 0
    85  	span.asyncId = noneAsyncId
    86  	span.eventStack = &stack{}
    87  	span.spanEvents = make([]*spanEvent, 0)
    88  	span.errorChains = make([]*exception, 0)
    89  
    90  	return &span
    91  }
    92  
    93  func newSampledSpan(agent *agent, operation string, rpcName string) *span {
    94  	span := defaultSpan()
    95  
    96  	span.agent = agent
    97  	span.operationName = operation
    98  	span.rpcName = rpcName
    99  	span.apiId = agent.cacheSpanApi(operation, apiTypeWebRequest)
   100  
   101  	return span
   102  }
   103  
   104  func (span *span) EndSpan() {
   105  	endTime := time.Now()
   106  	span.elapsed = endTime.Sub(span.startTime).Milliseconds()
   107  
   108  	if span.isAsyncSpan() {
   109  		span.EndSpanEvent() //async span event
   110  	} else {
   111  		dropSampledActiveSpan(span)
   112  		collectResponseTime(span.elapsed)
   113  	}
   114  
   115  	if span.eventStack.len() > 0 {
   116  		span.eventStack.empty()
   117  		Log("span").Warnf("abnormal span - has unclosed event")
   118  	}
   119  
   120  	if span.agent.enqueueSpan(span) {
   121  		if len(span.errorChains) > 0 {
   122  			span.agent.enqueueExceptionMeta(span)
   123  		}
   124  	} else {
   125  		Log("span").Tracef("span channel - max capacity reached or closed")
   126  	}
   127  
   128  	if span.urlStat != nil {
   129  		span.agent.enqueueUrlStat(&urlStat{entry: span.urlStat, endTime: endTime, elapsed: span.elapsed})
   130  	}
   131  }
   132  
   133  func (span *span) Inject(writer DistributedTracingContextWriter) {
   134  	if span.eventOverflow > 0 {
   135  		return
   136  	}
   137  	if se, ok := span.eventStack.peek(); ok {
   138  		writer.Set(HeaderTraceId, span.txId.String())
   139  
   140  		nextSpanId := se.generateNextSpanId()
   141  		writer.Set(HeaderSpanId, strconv.FormatInt(nextSpanId, 10))
   142  
   143  		writer.Set(HeaderParentSpanId, strconv.FormatInt(span.spanId, 10))
   144  		writer.Set(HeaderFlags, strconv.Itoa(span.flags))
   145  		writer.Set(HeaderParentApplicationName, span.agent.appName)
   146  		writer.Set(HeaderParentApplicationType, strconv.Itoa(int(span.agent.appType)))
   147  		writer.Set(HeaderParentApplicationNamespace, "")
   148  
   149  		se.endPoint = se.destinationId
   150  		writer.Set(HeaderHost, se.destinationId)
   151  
   152  		Log("span").Tracef("span inject: %v, %d, %d, %s", span.txId, nextSpanId, span.spanId, se.destinationId)
   153  	} else {
   154  		Log("span").Warnf("abnormal span - has no event")
   155  	}
   156  }
   157  
   158  func (span *span) Extract(reader DistributedTracingContextReader) {
   159  	tid := reader.Get(HeaderTraceId)
   160  	if tid != "" {
   161  		s := strings.Split(tid, "^")
   162  		span.txId.AgentId = s[0]
   163  		span.txId.StartTime, _ = strconv.ParseInt(s[1], 10, 0)
   164  		span.txId.Sequence, _ = strconv.ParseInt(s[2], 10, 0)
   165  	} else {
   166  		span.txId = span.agent.generateTransactionId()
   167  	}
   168  
   169  	spanid := reader.Get(HeaderSpanId)
   170  	if spanid != "" {
   171  		span.spanId, _ = strconv.ParseInt(spanid, 10, 0)
   172  	} else {
   173  		span.spanId = generateSpanId()
   174  	}
   175  
   176  	pspanid := reader.Get(HeaderParentSpanId)
   177  	if pspanid != "" {
   178  		span.parentSpanId, _ = strconv.ParseInt(pspanid, 10, 0)
   179  	}
   180  
   181  	flag := reader.Get(HeaderFlags)
   182  	if flag != "" {
   183  		span.flags, _ = strconv.Atoi(flag)
   184  	}
   185  
   186  	pappname := reader.Get(HeaderParentApplicationName)
   187  	if pappname != "" {
   188  		span.parentAppName = pappname
   189  	}
   190  
   191  	papptype := reader.Get(HeaderParentApplicationType)
   192  	if papptype != "" {
   193  		span.parentAppType, _ = strconv.Atoi(papptype)
   194  	}
   195  
   196  	host := reader.Get(HeaderHost)
   197  	if host != "" {
   198  		span.acceptorHost = host
   199  		span.endPoint = host
   200  		span.remoteAddr = host // for message queue (kafka, ...)
   201  	}
   202  
   203  	addSampledActiveSpan(span)
   204  	Log("span").Tracef("span extract: %s, %s, %s, %s, %s, %s", tid, spanid, pappname, pspanid, papptype, host)
   205  }
   206  
   207  func (span *span) NewSpanEvent(operationName string) Tracer {
   208  	cfg := span.config()
   209  	if span.eventSequence >= cfg.spanMaxEventSequence || span.eventDepth >= cfg.spanMaxEventDepth {
   210  		span.eventOverflow++
   211  		if !span.eventOverflowLog {
   212  			Log("span").Warnf("callStack maximum depth/sequence exceeded. (depth=%d, seq=%d)", span.eventDepth, span.eventSequence)
   213  			span.eventOverflowLog = true
   214  		}
   215  	} else {
   216  		span.appendSpanEvent(newSpanEvent(span, operationName))
   217  	}
   218  	return span
   219  }
   220  
   221  func (span *span) appendSpanEvent(se *spanEvent) {
   222  	span.appendLock.Lock()
   223  	defer span.appendLock.Unlock()
   224  
   225  	span.spanEvents = append(span.spanEvents, se)
   226  	span.eventStack.push(se)
   227  	span.eventSequence++
   228  	span.eventDepth++
   229  }
   230  
   231  func (span *span) EndSpanEvent() {
   232  	if span.eventOverflow > 0 {
   233  		span.eventOverflow--
   234  		return
   235  	}
   236  	if se, ok := span.eventStack.pop(); ok {
   237  		se.end()
   238  		if !span.recovered {
   239  			if v := recover(); v != nil {
   240  				err, ok := v.(error)
   241  				if !ok {
   242  					err = errors.New(fmt.Sprint(v))
   243  				}
   244  				se.SetError(err, "panic")
   245  				span.SetError(err)
   246  				span.recovered = true
   247  				panic(err)
   248  			}
   249  		}
   250  	} else {
   251  		Log("span").Warnf("abnormal span - has no event")
   252  	}
   253  }
   254  
   255  func (span *span) newAsyncSpan() Tracer {
   256  	if span.eventOverflow > 0 {
   257  		return NoopTracer()
   258  	}
   259  	if se, ok := span.eventStack.peek(); ok {
   260  		asyncSpan := defaultSpan()
   261  
   262  		asyncSpan.agent = span.agent
   263  		asyncSpan.txId = span.txId
   264  		asyncSpan.spanId = span.spanId
   265  
   266  		for se.asyncId == noneAsyncId {
   267  			se.asyncId = atomic.AddInt32(&asyncIdGen, 1)
   268  		}
   269  		se.asyncSeqGen++
   270  
   271  		asyncSpan.asyncId = se.asyncId
   272  		asyncSpan.asyncSequence = se.asyncSeqGen
   273  		asyncSpan.appendSpanEvent(newSpanEventGoroutine(asyncSpan))
   274  
   275  		return asyncSpan
   276  	} else {
   277  		Log("span").Warnf("abnormal span - has no event")
   278  		return NoopTracer()
   279  	}
   280  }
   281  
   282  func (span *span) isAsyncSpan() bool {
   283  	return span.asyncId != noneAsyncId
   284  }
   285  
   286  func (span *span) NewAsyncSpan() Tracer {
   287  	return span.newAsyncSpan()
   288  }
   289  
   290  func (span *span) NewGoroutineTracer() Tracer {
   291  	return span.newAsyncSpan()
   292  }
   293  
   294  func (span *span) WrapGoroutine(goroutineName string, goroutine func(context.Context), ctx context.Context) func() {
   295  	asyncSpan := span.newAsyncSpan()
   296  
   297  	var newCtx context.Context
   298  	if ctx == nil {
   299  		newCtx = NewContext(context.Background(), asyncSpan)
   300  	} else {
   301  		newCtx = NewContext(ctx, asyncSpan)
   302  	}
   303  
   304  	return func() {
   305  		defer asyncSpan.EndSpan()
   306  		defer asyncSpan.NewSpanEvent(goroutineName).EndSpanEvent()
   307  		goroutine(newCtx)
   308  	}
   309  }
   310  
   311  func (span *span) TransactionId() TransactionId {
   312  	return span.txId
   313  }
   314  
   315  func (span *span) SpanId() int64 {
   316  	return span.spanId
   317  }
   318  
   319  func (span *span) Span() SpanRecorder {
   320  	return span
   321  }
   322  
   323  func (span *span) SpanEvent() SpanEventRecorder {
   324  	if span.eventOverflow > 0 {
   325  		return &defaultNoopSpanEvent
   326  	}
   327  	if se, ok := span.eventStack.peek(); ok {
   328  		return se
   329  	}
   330  	Log("span").Warnf("abnormal span - has no event")
   331  	return &defaultNoopSpanEvent
   332  }
   333  
   334  func (span *span) IsSampled() bool {
   335  	return true
   336  }
   337  
   338  func (span *span) SetError(e error) {
   339  	if e == nil || span.eventOverflow > 0 {
   340  		return
   341  	}
   342  
   343  	id := span.agent.cacheError("error")
   344  	span.errorFuncId = id
   345  	span.errorString = e.Error()
   346  	span.err = 1
   347  }
   348  
   349  func (span *span) SetFailure() {
   350  	span.err = 1
   351  }
   352  
   353  func (span *span) SetServiceType(typ int32) {
   354  	span.serviceType = typ
   355  }
   356  
   357  func (span *span) SetRpcName(rpc string) {
   358  	span.rpcName = rpc
   359  }
   360  
   361  func (span *span) SetRemoteAddress(remoteAddress string) {
   362  	span.remoteAddr = remoteAddress
   363  }
   364  
   365  func (span *span) SetEndPoint(endPoint string) {
   366  	span.endPoint = endPoint
   367  }
   368  
   369  func (span *span) SetAcceptorHost(host string) {
   370  	span.acceptorHost = host
   371  }
   372  
   373  func (span *span) Annotations() Annotation {
   374  	return &span.annotations
   375  }
   376  
   377  func (span *span) SetLogging(logInfo int32) {
   378  	span.loggingInfo = logInfo
   379  }
   380  
   381  func (span *span) collectUrlStat(stat *UrlStatEntry) {
   382  	if span.config().collectUrlStat {
   383  		if stat.Url == "" {
   384  			stat.Url = "UNKNOWN_URL"
   385  		}
   386  
   387  		span.urlStat = stat
   388  	}
   389  }
   390  
   391  func (span *span) AddMetric(metric string, value interface{}) {
   392  	if metric == MetricURLStat {
   393  		span.collectUrlStat(value.(*UrlStatEntry))
   394  	}
   395  }
   396  
   397  func (span *span) JsonString() []byte {
   398  	m := make(map[string]interface{}, 0)
   399  	m["RpcName"] = span.rpcName
   400  	m["EndPoint"] = span.endPoint
   401  	m["RemoteAddr"] = span.remoteAddr
   402  	m["Err"] = span.err
   403  	m["Events"] = span.spanEvents
   404  	m["Annotations"] = span.annotations.list
   405  	b, _ := json.Marshal(m)
   406  	return b
   407  }
   408  
   409  func (span *span) config() *Config {
   410  	return span.agent.config
   411  }
   412  
   413  type stack struct {
   414  	lock sync.RWMutex
   415  	top  *node
   416  	size int
   417  }
   418  
   419  type node struct {
   420  	value *spanEvent
   421  	next  *node
   422  }
   423  
   424  func (s *stack) len() int {
   425  	s.lock.RLock()
   426  	defer s.lock.RUnlock()
   427  	return s.size
   428  }
   429  
   430  func (s *stack) push(v *spanEvent) {
   431  	s.lock.Lock()
   432  	defer s.lock.Unlock()
   433  
   434  	s.top = &node{value: v, next: s.top}
   435  	s.size++
   436  }
   437  
   438  func (s *stack) pop() (*spanEvent, bool) {
   439  	s.lock.Lock()
   440  	defer s.lock.Unlock()
   441  
   442  	if s.size > 0 {
   443  		save := s.top.value
   444  		s.top = s.top.next
   445  		s.size--
   446  		return save, true
   447  	}
   448  	return nil, false
   449  }
   450  
   451  func (s *stack) peek() (*spanEvent, bool) {
   452  	s.lock.RLock()
   453  	defer s.lock.RUnlock()
   454  
   455  	if s.size > 0 {
   456  		return s.top.value, true
   457  	}
   458  	return nil, false
   459  }
   460  
   461  func (s *stack) empty() {
   462  	s.lock.Lock()
   463  	defer s.lock.Unlock()
   464  
   465  	for s.size > 0 {
   466  		s.top.value.end()
   467  		s.top = s.top.next
   468  		s.size--
   469  	}
   470  }