github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/span.go (about)

     1  // (c) Copyright IBM Corp. 2021
     2  // (c) Copyright Instana Inc. 2016
     3  
     4  package instana
     5  
     6  import (
     7  	"bytes"
     8  	"strings"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/instana/go-sensor/logger"
    13  	ot "github.com/opentracing/opentracing-go"
    14  	"github.com/opentracing/opentracing-go/ext"
    15  	otlog "github.com/opentracing/opentracing-go/log"
    16  )
    17  
    18  const minSpanLogLevel = logger.WarnLevel
    19  
    20  var _ ot.Span = (*spanS)(nil)
    21  
    22  type spanS struct {
    23  	Service     string
    24  	Operation   string
    25  	Start       time.Time
    26  	Duration    time.Duration
    27  	Correlation EUMCorrelationData
    28  	Tags        ot.Tags
    29  	Logs        []ot.LogRecord
    30  	ErrorCount  int
    31  
    32  	tracer *tracerS
    33  	mu     sync.Mutex
    34  
    35  	context SpanContext
    36  }
    37  
    38  func (r *spanS) BaggageItem(key string) string {
    39  	r.mu.Lock()
    40  	defer r.mu.Unlock()
    41  
    42  	return r.context.Baggage[key]
    43  }
    44  
    45  func (r *spanS) SetBaggageItem(key, val string) ot.Span {
    46  	r.mu.Lock()
    47  	defer r.mu.Unlock()
    48  	r.context = r.context.WithBaggageItem(key, val)
    49  
    50  	return r
    51  }
    52  
    53  func (r *spanS) Context() ot.SpanContext {
    54  	return r.context
    55  }
    56  
    57  func (r *spanS) Finish() {
    58  	r.FinishWithOptions(ot.FinishOptions{})
    59  }
    60  
    61  func (r *spanS) FinishWithOptions(opts ot.FinishOptions) {
    62  	finishTime := opts.FinishTime
    63  	if finishTime.IsZero() {
    64  		finishTime = time.Now()
    65  	}
    66  
    67  	duration := finishTime.Sub(r.Start)
    68  
    69  	r.mu.Lock()
    70  	defer r.mu.Unlock()
    71  
    72  	for _, lr := range opts.LogRecords {
    73  		r.appendLog(lr)
    74  	}
    75  
    76  	for _, ld := range opts.BulkLogData {
    77  		r.appendLog(ld.ToLogRecord())
    78  	}
    79  
    80  	r.Duration = duration
    81  	if r.sendSpanToAgent() {
    82  		if sensor.Agent().Ready() {
    83  			r.tracer.recorder.RecordSpan(r)
    84  		} else {
    85  			delayed.append(r)
    86  		}
    87  		r.sendOpenTracingLogRecords()
    88  	}
    89  }
    90  
    91  func (r *spanS) sendSpanToAgent() bool {
    92  	//if suppress tag is present, span shouldn't be forwarded
    93  	if r.context.Suppressed {
    94  		return false
    95  	}
    96  
    97  	if !isRootExitSpan(r.Tags[string(ext.SpanKind)], r.context.ParentID == 0) {
    98  		// if the span is an entry span, intermediate span, exit span with a parent
    99  		// it should be forwarded to the agent
   100  		return true
   101  	}
   102  
   103  	// if the span is an exit span without a parent span, then it should be forwarded
   104  	// only if ALLOW_ROOT_EXIT_SPAN is configured by the user
   105  	return allowRootExitSpan()
   106  }
   107  
   108  func (r *spanS) appendLog(lr ot.LogRecord) {
   109  	maxLogs := r.tracer.Options().MaxLogsPerSpan
   110  	if maxLogs == 0 || len(r.Logs) < maxLogs {
   111  		r.Logs = append(r.Logs, lr)
   112  	}
   113  }
   114  
   115  func (r *spanS) Log(ld ot.LogData) {
   116  	if r.tracer.Options().DropAllLogs {
   117  		return
   118  	}
   119  
   120  	r.mu.Lock()
   121  	defer r.mu.Unlock()
   122  
   123  	if ld.Timestamp.IsZero() {
   124  		ld.Timestamp = time.Now()
   125  	}
   126  
   127  	r.appendLog(ld.ToLogRecord())
   128  }
   129  
   130  func (r *spanS) LogEvent(event string) {
   131  	r.Log(ot.LogData{
   132  		Event: event})
   133  }
   134  
   135  func (r *spanS) LogEventWithPayload(event string, payload interface{}) {
   136  	r.Log(ot.LogData{
   137  		Event:   event,
   138  		Payload: payload})
   139  }
   140  
   141  func (r *spanS) LogFields(fields ...otlog.Field) {
   142  
   143  	for _, v := range fields {
   144  		// If this tag indicates an error, increase the error count
   145  		if openTracingLogFieldLevel(v) == logger.ErrorLevel {
   146  			r.ErrorCount++
   147  		}
   148  	}
   149  
   150  	lr := ot.LogRecord{
   151  		Fields: fields,
   152  	}
   153  
   154  	if r.tracer.Options().DropAllLogs {
   155  		return
   156  	}
   157  
   158  	r.mu.Lock()
   159  	defer r.mu.Unlock()
   160  
   161  	if lr.Timestamp.IsZero() {
   162  		lr.Timestamp = time.Now()
   163  	}
   164  
   165  	r.appendLog(lr)
   166  }
   167  
   168  func (r *spanS) LogKV(keyValues ...interface{}) {
   169  	fields, err := otlog.InterleavedKVToFields(keyValues...)
   170  	if err != nil {
   171  		r.LogFields(otlog.Error(err), otlog.String("function", "LogKV"))
   172  
   173  		return
   174  	}
   175  
   176  	r.LogFields(fields...)
   177  }
   178  
   179  func (r *spanS) SetOperationName(operationName string) ot.Span {
   180  	r.mu.Lock()
   181  	defer r.mu.Unlock()
   182  
   183  	r.Operation = operationName
   184  
   185  	return r
   186  }
   187  
   188  func (r *spanS) SetTag(key string, value interface{}) ot.Span {
   189  	r.mu.Lock()
   190  	defer r.mu.Unlock()
   191  
   192  	if r.Tags == nil {
   193  		r.Tags = ot.Tags{}
   194  	}
   195  
   196  	// If this tag indicates an error, increase the error count
   197  	if key == "error" {
   198  		r.ErrorCount++
   199  	}
   200  
   201  	if key == suppressTracingTag {
   202  		r.context.Suppressed = true
   203  		return r
   204  	}
   205  
   206  	r.Tags[key] = value
   207  
   208  	return r
   209  }
   210  
   211  func (r *spanS) Tracer() ot.Tracer {
   212  	return r.tracer
   213  }
   214  
   215  // sendOpenTracingLogRecords converts OpenTracing log records that contain errors
   216  // to Instana log spans and sends them to the agent
   217  func (r *spanS) sendOpenTracingLogRecords() {
   218  	for _, lr := range r.Logs {
   219  		r.sendOpenTracingLogRecord(lr)
   220  	}
   221  }
   222  
   223  func (r *spanS) sendOpenTracingLogRecord(lr ot.LogRecord) {
   224  	lvl := openTracingHighestLogRecordLevel(lr)
   225  
   226  	if lvl.Less(minSpanLogLevel) {
   227  		return
   228  	}
   229  
   230  	buf := bytes.NewBuffer(nil)
   231  
   232  	enc := newOpenTracingLogEncoder(buf)
   233  	for _, lf := range lr.Fields {
   234  		lf.Marshal(enc)
   235  		buf.WriteByte(' ')
   236  	}
   237  
   238  	r.tracer.StartSpan(
   239  		"log.go",
   240  		ot.ChildOf(r.context),
   241  		ot.StartTime(lr.Timestamp),
   242  		ot.Tags{
   243  			"log.level":   lvl.String(),
   244  			"log.message": strings.TrimSpace(buf.String()),
   245  		},
   246  	).FinishWithOptions(
   247  		ot.FinishOptions{
   248  			FinishTime: lr.Timestamp,
   249  		},
   250  	)
   251  }
   252  
   253  // openTracingHighestLogRecordLevel determines the level of this record by inspecting its fields.
   254  // If there are multiple fields suggesting the log level, i.e. both "error" and "warn" are present,
   255  // the highest one takes precedence.
   256  func openTracingHighestLogRecordLevel(lr ot.LogRecord) logger.Level {
   257  	highestLvl := logger.DebugLevel
   258  
   259  	for _, lf := range lr.Fields {
   260  		if lvl := openTracingLogFieldLevel(lf); highestLvl.Less(lvl) {
   261  			highestLvl = lvl
   262  		}
   263  	}
   264  
   265  	return highestLvl
   266  }
   267  
   268  func openTracingLogFieldLevel(lf otlog.Field) logger.Level {
   269  	switch lf.Key() {
   270  	case "error", "error.object":
   271  		return logger.ErrorLevel
   272  	case "warn":
   273  		return logger.WarnLevel
   274  	default:
   275  		return logger.DebugLevel
   276  	}
   277  }