trace.go raw

   1  // Copyright The OpenTelemetry Authors
   2  // SPDX-License-Identifier: Apache-2.0
   3  
   4  package trace // import "go.opentelemetry.io/otel/trace"
   5  
   6  import (
   7  	"encoding/json"
   8  )
   9  
  10  const (
  11  	// FlagsSampled is a bitmask with the sampled bit set. A SpanContext
  12  	// with the sampling bit set means the span is sampled.
  13  	FlagsSampled = TraceFlags(0x01)
  14  
  15  	errInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase"
  16  
  17  	errInvalidTraceIDLength errorConst = "hex encoded trace-id must have length equals to 32"
  18  	errNilTraceID           errorConst = "trace-id can't be all zero"
  19  
  20  	errInvalidSpanIDLength errorConst = "hex encoded span-id must have length equals to 16"
  21  	errNilSpanID           errorConst = "span-id can't be all zero"
  22  )
  23  
  24  type errorConst string
  25  
  26  func (e errorConst) Error() string {
  27  	return string(e)
  28  }
  29  
  30  // TraceID is a unique identity of a trace.
  31  // nolint:revive // revive complains about stutter of `trace.TraceID`.
  32  type TraceID [16]byte
  33  
  34  var (
  35  	nilTraceID TraceID
  36  	_          json.Marshaler = nilTraceID
  37  )
  38  
  39  // IsValid reports whether the trace TraceID is valid. A valid trace ID does
  40  // not consist of zeros only.
  41  func (t TraceID) IsValid() bool {
  42  	return t != nilTraceID
  43  }
  44  
  45  // MarshalJSON implements a custom marshal function to encode TraceID
  46  // as a hex string.
  47  func (t TraceID) MarshalJSON() ([]byte, error) {
  48  	b := [32 + 2]byte{0: '"', 33: '"'}
  49  	h := t.hexBytes()
  50  	copy(b[1:], h[:])
  51  	return b[:], nil
  52  }
  53  
  54  // String returns the hex string representation form of a TraceID.
  55  func (t TraceID) String() string {
  56  	h := t.hexBytes()
  57  	return string(h[:])
  58  }
  59  
  60  // hexBytes returns the hex string representation form of a TraceID.
  61  func (t TraceID) hexBytes() [32]byte {
  62  	return [32]byte{
  63  		hexLU[t[0x0]>>4], hexLU[t[0x0]&0xf],
  64  		hexLU[t[0x1]>>4], hexLU[t[0x1]&0xf],
  65  		hexLU[t[0x2]>>4], hexLU[t[0x2]&0xf],
  66  		hexLU[t[0x3]>>4], hexLU[t[0x3]&0xf],
  67  		hexLU[t[0x4]>>4], hexLU[t[0x4]&0xf],
  68  		hexLU[t[0x5]>>4], hexLU[t[0x5]&0xf],
  69  		hexLU[t[0x6]>>4], hexLU[t[0x6]&0xf],
  70  		hexLU[t[0x7]>>4], hexLU[t[0x7]&0xf],
  71  		hexLU[t[0x8]>>4], hexLU[t[0x8]&0xf],
  72  		hexLU[t[0x9]>>4], hexLU[t[0x9]&0xf],
  73  		hexLU[t[0xa]>>4], hexLU[t[0xa]&0xf],
  74  		hexLU[t[0xb]>>4], hexLU[t[0xb]&0xf],
  75  		hexLU[t[0xc]>>4], hexLU[t[0xc]&0xf],
  76  		hexLU[t[0xd]>>4], hexLU[t[0xd]&0xf],
  77  		hexLU[t[0xe]>>4], hexLU[t[0xe]&0xf],
  78  		hexLU[t[0xf]>>4], hexLU[t[0xf]&0xf],
  79  	}
  80  }
  81  
  82  // SpanID is a unique identity of a span in a trace.
  83  type SpanID [8]byte
  84  
  85  var (
  86  	nilSpanID SpanID
  87  	_         json.Marshaler = nilSpanID
  88  )
  89  
  90  // IsValid reports whether the SpanID is valid. A valid SpanID does not consist
  91  // of zeros only.
  92  func (s SpanID) IsValid() bool {
  93  	return s != nilSpanID
  94  }
  95  
  96  // MarshalJSON implements a custom marshal function to encode SpanID
  97  // as a hex string.
  98  func (s SpanID) MarshalJSON() ([]byte, error) {
  99  	b := [16 + 2]byte{0: '"', 17: '"'}
 100  	h := s.hexBytes()
 101  	copy(b[1:], h[:])
 102  	return b[:], nil
 103  }
 104  
 105  // String returns the hex string representation form of a SpanID.
 106  func (s SpanID) String() string {
 107  	b := s.hexBytes()
 108  	return string(b[:])
 109  }
 110  
 111  func (s SpanID) hexBytes() [16]byte {
 112  	return [16]byte{
 113  		hexLU[s[0]>>4], hexLU[s[0]&0xf],
 114  		hexLU[s[1]>>4], hexLU[s[1]&0xf],
 115  		hexLU[s[2]>>4], hexLU[s[2]&0xf],
 116  		hexLU[s[3]>>4], hexLU[s[3]&0xf],
 117  		hexLU[s[4]>>4], hexLU[s[4]&0xf],
 118  		hexLU[s[5]>>4], hexLU[s[5]&0xf],
 119  		hexLU[s[6]>>4], hexLU[s[6]&0xf],
 120  		hexLU[s[7]>>4], hexLU[s[7]&0xf],
 121  	}
 122  }
 123  
 124  // TraceIDFromHex returns a TraceID from a hex string if it is compliant with
 125  // the W3C trace-context specification.  See more at
 126  // https://www.w3.org/TR/trace-context/#trace-id
 127  // nolint:revive // revive complains about stutter of `trace.TraceIDFromHex`.
 128  func TraceIDFromHex(h string) (TraceID, error) {
 129  	if len(h) != 32 {
 130  		return [16]byte{}, errInvalidTraceIDLength
 131  	}
 132  	var b [16]byte
 133  	invalidMark := byte(0)
 134  	for i := 0; i < len(h); i += 4 {
 135  		b[i/2] = (hexRev[h[i]] << 4) | hexRev[h[i+1]]
 136  		b[i/2+1] = (hexRev[h[i+2]] << 4) | hexRev[h[i+3]]
 137  		invalidMark |= hexRev[h[i]] | hexRev[h[i+1]] | hexRev[h[i+2]] | hexRev[h[i+3]]
 138  	}
 139  	// If the upper 4 bits of any byte are not zero, there was an invalid hex
 140  	// character since invalid hex characters are 0xff in hexRev.
 141  	if invalidMark&0xf0 != 0 {
 142  		return [16]byte{}, errInvalidHexID
 143  	}
 144  	// If we didn't set any bits, then h was all zeros.
 145  	if invalidMark == 0 {
 146  		return [16]byte{}, errNilTraceID
 147  	}
 148  	return b, nil
 149  }
 150  
 151  // SpanIDFromHex returns a SpanID from a hex string if it is compliant
 152  // with the w3c trace-context specification.
 153  // See more at https://www.w3.org/TR/trace-context/#parent-id
 154  func SpanIDFromHex(h string) (SpanID, error) {
 155  	if len(h) != 16 {
 156  		return [8]byte{}, errInvalidSpanIDLength
 157  	}
 158  	var b [8]byte
 159  	invalidMark := byte(0)
 160  	for i := 0; i < len(h); i += 4 {
 161  		b[i/2] = (hexRev[h[i]] << 4) | hexRev[h[i+1]]
 162  		b[i/2+1] = (hexRev[h[i+2]] << 4) | hexRev[h[i+3]]
 163  		invalidMark |= hexRev[h[i]] | hexRev[h[i+1]] | hexRev[h[i+2]] | hexRev[h[i+3]]
 164  	}
 165  	// If the upper 4 bits of any byte are not zero, there was an invalid hex
 166  	// character since invalid hex characters are 0xff in hexRev.
 167  	if invalidMark&0xf0 != 0 {
 168  		return [8]byte{}, errInvalidHexID
 169  	}
 170  	// If we didn't set any bits, then h was all zeros.
 171  	if invalidMark == 0 {
 172  		return [8]byte{}, errNilSpanID
 173  	}
 174  	return b, nil
 175  }
 176  
 177  // TraceFlags contains flags that can be set on a SpanContext.
 178  type TraceFlags byte //nolint:revive // revive complains about stutter of `trace.TraceFlags`.
 179  
 180  // IsSampled reports whether the sampling bit is set in the TraceFlags.
 181  func (tf TraceFlags) IsSampled() bool {
 182  	return tf&FlagsSampled == FlagsSampled
 183  }
 184  
 185  // WithSampled sets the sampling bit in a new copy of the TraceFlags.
 186  func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { // nolint:revive  // sampled is not a control flag.
 187  	if sampled {
 188  		return tf | FlagsSampled
 189  	}
 190  
 191  	return tf &^ FlagsSampled
 192  }
 193  
 194  // MarshalJSON implements a custom marshal function to encode TraceFlags
 195  // as a hex string.
 196  func (tf TraceFlags) MarshalJSON() ([]byte, error) {
 197  	b := [2 + 2]byte{0: '"', 3: '"'}
 198  	h := tf.hexBytes()
 199  	copy(b[1:], h[:])
 200  	return b[:], nil
 201  }
 202  
 203  // String returns the hex string representation form of TraceFlags.
 204  func (tf TraceFlags) String() string {
 205  	h := tf.hexBytes()
 206  	return string(h[:])
 207  }
 208  
 209  func (tf TraceFlags) hexBytes() [2]byte {
 210  	return [2]byte{hexLU[tf>>4], hexLU[tf&0xf]}
 211  }
 212  
 213  // SpanContextConfig contains mutable fields usable for constructing
 214  // an immutable SpanContext.
 215  type SpanContextConfig struct {
 216  	TraceID    TraceID
 217  	SpanID     SpanID
 218  	TraceFlags TraceFlags
 219  	TraceState TraceState
 220  	Remote     bool
 221  }
 222  
 223  // NewSpanContext constructs a SpanContext using values from the provided
 224  // SpanContextConfig.
 225  func NewSpanContext(config SpanContextConfig) SpanContext {
 226  	return SpanContext{
 227  		traceID:    config.TraceID,
 228  		spanID:     config.SpanID,
 229  		traceFlags: config.TraceFlags,
 230  		traceState: config.TraceState,
 231  		remote:     config.Remote,
 232  	}
 233  }
 234  
 235  // SpanContext contains identifying trace information about a Span.
 236  type SpanContext struct {
 237  	traceID    TraceID
 238  	spanID     SpanID
 239  	traceFlags TraceFlags
 240  	traceState TraceState
 241  	remote     bool
 242  }
 243  
 244  var _ json.Marshaler = SpanContext{}
 245  
 246  // IsValid reports whether the SpanContext is valid. A valid span context has a
 247  // valid TraceID and SpanID.
 248  func (sc SpanContext) IsValid() bool {
 249  	return sc.HasTraceID() && sc.HasSpanID()
 250  }
 251  
 252  // IsRemote reports whether the SpanContext represents a remotely-created Span.
 253  func (sc SpanContext) IsRemote() bool {
 254  	return sc.remote
 255  }
 256  
 257  // WithRemote returns a copy of sc with the Remote property set to remote.
 258  func (sc SpanContext) WithRemote(remote bool) SpanContext {
 259  	return SpanContext{
 260  		traceID:    sc.traceID,
 261  		spanID:     sc.spanID,
 262  		traceFlags: sc.traceFlags,
 263  		traceState: sc.traceState,
 264  		remote:     remote,
 265  	}
 266  }
 267  
 268  // TraceID returns the TraceID from the SpanContext.
 269  func (sc SpanContext) TraceID() TraceID {
 270  	return sc.traceID
 271  }
 272  
 273  // HasTraceID reports whether the SpanContext has a valid TraceID.
 274  func (sc SpanContext) HasTraceID() bool {
 275  	return sc.traceID.IsValid()
 276  }
 277  
 278  // WithTraceID returns a new SpanContext with the TraceID replaced.
 279  func (sc SpanContext) WithTraceID(traceID TraceID) SpanContext {
 280  	return SpanContext{
 281  		traceID:    traceID,
 282  		spanID:     sc.spanID,
 283  		traceFlags: sc.traceFlags,
 284  		traceState: sc.traceState,
 285  		remote:     sc.remote,
 286  	}
 287  }
 288  
 289  // SpanID returns the SpanID from the SpanContext.
 290  func (sc SpanContext) SpanID() SpanID {
 291  	return sc.spanID
 292  }
 293  
 294  // HasSpanID reports whether the SpanContext has a valid SpanID.
 295  func (sc SpanContext) HasSpanID() bool {
 296  	return sc.spanID.IsValid()
 297  }
 298  
 299  // WithSpanID returns a new SpanContext with the SpanID replaced.
 300  func (sc SpanContext) WithSpanID(spanID SpanID) SpanContext {
 301  	return SpanContext{
 302  		traceID:    sc.traceID,
 303  		spanID:     spanID,
 304  		traceFlags: sc.traceFlags,
 305  		traceState: sc.traceState,
 306  		remote:     sc.remote,
 307  	}
 308  }
 309  
 310  // TraceFlags returns the flags from the SpanContext.
 311  func (sc SpanContext) TraceFlags() TraceFlags {
 312  	return sc.traceFlags
 313  }
 314  
 315  // IsSampled reports whether the sampling bit is set in the SpanContext's TraceFlags.
 316  func (sc SpanContext) IsSampled() bool {
 317  	return sc.traceFlags.IsSampled()
 318  }
 319  
 320  // WithTraceFlags returns a new SpanContext with the TraceFlags replaced.
 321  func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext {
 322  	return SpanContext{
 323  		traceID:    sc.traceID,
 324  		spanID:     sc.spanID,
 325  		traceFlags: flags,
 326  		traceState: sc.traceState,
 327  		remote:     sc.remote,
 328  	}
 329  }
 330  
 331  // TraceState returns the TraceState from the SpanContext.
 332  func (sc SpanContext) TraceState() TraceState {
 333  	return sc.traceState
 334  }
 335  
 336  // WithTraceState returns a new SpanContext with the TraceState replaced.
 337  func (sc SpanContext) WithTraceState(state TraceState) SpanContext {
 338  	return SpanContext{
 339  		traceID:    sc.traceID,
 340  		spanID:     sc.spanID,
 341  		traceFlags: sc.traceFlags,
 342  		traceState: state,
 343  		remote:     sc.remote,
 344  	}
 345  }
 346  
 347  // Equal reports whether two SpanContext values are equal.
 348  func (sc SpanContext) Equal(other SpanContext) bool {
 349  	return sc.traceID == other.traceID &&
 350  		sc.spanID == other.spanID &&
 351  		sc.traceFlags == other.traceFlags &&
 352  		sc.traceState.String() == other.traceState.String() &&
 353  		sc.remote == other.remote
 354  }
 355  
 356  // MarshalJSON implements a custom marshal function to encode a SpanContext.
 357  func (sc SpanContext) MarshalJSON() ([]byte, error) {
 358  	return json.Marshal(SpanContextConfig{
 359  		TraceID:    sc.traceID,
 360  		SpanID:     sc.spanID,
 361  		TraceFlags: sc.traceFlags,
 362  		TraceState: sc.traceState,
 363  		Remote:     sc.remote,
 364  	})
 365  }
 366