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 }