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