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 }