github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/span_context.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2020 3 4 package instana 5 6 import ( 7 "strings" 8 9 "github.com/instana/go-sensor/w3ctrace" 10 ) 11 12 // EUMCorrelationData represents the data sent by the Instana End-User Monitoring script 13 // integrated into frontend 14 type EUMCorrelationData struct { 15 Type string 16 ID string 17 } 18 19 // SpanReference is a reference to a span, possibly belonging to another trace, that is relevant 20 // to the span context 21 type SpanReference struct { 22 TraceID string 23 SpanID string 24 } 25 26 // SpanContext holds the basic Span metadata. 27 type SpanContext struct { 28 // The higher 4 bytes of a 128-bit trace ID 29 TraceIDHi int64 30 // A probabilistically unique identifier for a [multi-span] trace. 31 TraceID int64 32 // A probabilistically unique identifier for a span. 33 SpanID int64 34 // An optional parent span ID, 0 if this is the root span context. 35 ParentID int64 36 // Optional links to traces relevant to this context, i.e. an indirect parent 37 Links []SpanReference 38 // Whether the trace is sampled. 39 Sampled bool 40 // Whether the trace is suppressed and should not be sent to the agent. 41 Suppressed bool 42 // The span's associated baggage. 43 Baggage map[string]string // initialized on first use 44 // The W3C trace context 45 W3CContext w3ctrace.Context 46 // Whether the used trace ID came from 3rd party, e.g. W3C Trace Context 47 ForeignTrace bool 48 // Correlation is the correlation data sent by the frontend EUM script 49 Correlation EUMCorrelationData 50 } 51 52 // NewRootSpanContext initializes a new root span context issuing a new trace ID 53 func NewRootSpanContext() SpanContext { 54 spanID := randomID() 55 56 c := SpanContext{ 57 TraceID: spanID, 58 SpanID: spanID, 59 } 60 61 c.W3CContext = newW3CTraceContext(c) 62 63 return c 64 } 65 66 // NewSpanContext initializes a new child span context from its parent. It will 67 // ignore the parent context if it contains neither Instana trace and span IDs 68 // nor a W3C trace context 69 func NewSpanContext(parent SpanContext) SpanContext { 70 var foreignTrace bool 71 if parent.TraceIDHi == 0 && parent.TraceID == 0 && parent.SpanID == 0 { 72 parent = restoreFromW3CTraceContext(parent) 73 foreignTrace = !sensor.options.disableW3CTraceCorrelation 74 } 75 76 if parent.TraceIDHi == 0 && parent.TraceID == 0 && parent.SpanID == 0 { 77 c := NewRootSpanContext() 78 c.Suppressed = parent.Suppressed 79 80 // preserve the W3C trace context even if it was not used 81 if !parent.W3CContext.IsZero() { 82 c.W3CContext = parent.W3CContext 83 } 84 85 return c 86 } 87 88 c := parent.Clone() 89 c.SpanID, c.ParentID = randomID(), parent.SpanID 90 c.ForeignTrace = foreignTrace 91 92 // initialize W3C trace context if it's not set already 93 if c.W3CContext.IsZero() { 94 c.W3CContext = newW3CTraceContext(c) 95 return c 96 } 97 98 // update W3C trace context parent 99 w3cParent := c.W3CContext.Parent() 100 w3cParent.ParentID = FormatID(c.SpanID) 101 c.W3CContext.RawParent = w3cParent.String() 102 103 // check if there is Instana state stored in the W3C tracestate header 104 if foreignTrace { 105 w3cState := c.W3CContext.State() 106 if ancestor, ok := w3cState.FetchInstanaTraceStateValue(); ok { 107 if ref, ok := parseW3CInstanaState(ancestor); ok { 108 c.Links = append(c.Links, ref) 109 } 110 } 111 } 112 113 return c 114 } 115 116 // IsZero returns true if an instance of SpanContext is a zero-value 117 func (sc SpanContext) IsZero() bool { 118 return sc.TraceIDHi == 0 && sc.TraceID == 0 && sc.SpanID == 0 && sc.W3CContext.IsZero() && !sc.Suppressed 119 } 120 121 func restoreFromW3CTraceContext(parent SpanContext) SpanContext { 122 if parent.W3CContext.IsZero() { 123 return parent 124 } 125 126 traceparent := parent.W3CContext.Parent() 127 128 if sensor.options.disableW3CTraceCorrelation { 129 restored := restoreFromW3CTraceState(parent.W3CContext) 130 restored.Suppressed = parent.Suppressed 131 132 return restored 133 } 134 135 traceIDHi, traceIDLo, err := ParseLongID(traceparent.TraceID) 136 if err != nil { 137 return parent 138 } 139 140 parentID, err := ParseID(traceparent.ParentID) 141 if err != nil { 142 return parent 143 } 144 145 return SpanContext{ 146 TraceIDHi: traceIDHi, 147 TraceID: traceIDLo, 148 SpanID: parentID, 149 Suppressed: parent.Suppressed, 150 W3CContext: parent.W3CContext, 151 } 152 } 153 154 func restoreFromW3CTraceState(trCtx w3ctrace.Context) SpanContext { 155 if trCtx.IsZero() { 156 return SpanContext{} 157 } 158 159 c := SpanContext{ 160 W3CContext: trCtx, 161 } 162 163 state, ok := trCtx.State().FetchInstanaTraceStateValue() 164 if !ok { 165 return c 166 } 167 168 ref, ok := parseW3CInstanaState(state) 169 if !ok { 170 return c 171 } 172 173 traceIDHi, traceIDLo, err := ParseLongID(ref.TraceID) 174 if err != nil { 175 return c 176 } 177 178 parentID, err := ParseID(ref.SpanID) 179 if err != nil { 180 return c 181 } 182 183 c.TraceIDHi, c.TraceID, c.SpanID, c.Suppressed = traceIDHi, traceIDLo, parentID, !trCtx.Parent().Flags.Sampled 184 185 return c 186 } 187 188 // ForeachBaggageItem belongs to the opentracing.SpanContext interface 189 func (sc SpanContext) ForeachBaggageItem(handler func(k, v string) bool) { 190 for k, v := range sc.Baggage { 191 if !handler(k, v) { 192 break 193 } 194 } 195 } 196 197 // WithBaggageItem returns an entirely new SpanContext with the 198 // given key:value baggage pair set. 199 func (sc SpanContext) WithBaggageItem(key, val string) SpanContext { 200 res := sc.Clone() 201 202 if res.Baggage == nil { 203 res.Baggage = make(map[string]string, 1) 204 } 205 res.Baggage[key] = val 206 207 return res 208 } 209 210 // Clone returns a deep copy of a SpanContext 211 func (sc SpanContext) Clone() SpanContext { 212 res := SpanContext{ 213 TraceIDHi: sc.TraceIDHi, 214 TraceID: sc.TraceID, 215 SpanID: sc.SpanID, 216 ParentID: sc.ParentID, 217 Sampled: sc.Sampled, 218 Suppressed: sc.Suppressed, 219 W3CContext: sc.W3CContext, 220 } 221 222 if sc.Baggage != nil { 223 res.Baggage = make(map[string]string, len(sc.Baggage)) 224 for k, v := range sc.Baggage { 225 res.Baggage[k] = v 226 } 227 } 228 229 return res 230 } 231 232 func newW3CTraceContext(c SpanContext) w3ctrace.Context { 233 return w3ctrace.New(w3ctrace.Parent{ 234 Version: w3ctrace.Version_Max, 235 TraceID: FormatLongID(c.TraceIDHi, c.TraceID), 236 ParentID: FormatID(c.SpanID), 237 Flags: w3ctrace.Flags{ 238 Sampled: !c.Suppressed, 239 }, 240 }) 241 } 242 243 func parseW3CInstanaState(vendorData string) (ancestor SpanReference, ok bool) { 244 ind := strings.IndexByte(vendorData, ';') 245 if ind < 0 { 246 return SpanReference{}, false 247 } 248 249 return SpanReference{ 250 TraceID: vendorData[:ind], 251 SpanID: vendorData[ind+1:], 252 }, true 253 }