span.go raw

   1  // Copyright The OpenTelemetry Authors
   2  // SPDX-License-Identifier: Apache-2.0
   3  
   4  package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry"
   5  
   6  import (
   7  	"bytes"
   8  	"encoding/hex"
   9  	"encoding/json"
  10  	"errors"
  11  	"fmt"
  12  	"io"
  13  	"math"
  14  	"time"
  15  )
  16  
  17  // A Span represents a single operation performed by a single component of the
  18  // system.
  19  type Span struct {
  20  	// A unique identifier for a trace. All spans from the same trace share
  21  	// the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR
  22  	// of length other than 16 bytes is considered invalid (empty string in OTLP/JSON
  23  	// is zero-length and thus is also invalid).
  24  	//
  25  	// This field is required.
  26  	TraceID TraceID `json:"traceId,omitempty"`
  27  	// A unique identifier for a span within a trace, assigned when the span
  28  	// is created. The ID is an 8-byte array. An ID with all zeroes OR of length
  29  	// other than 8 bytes is considered invalid (empty string in OTLP/JSON
  30  	// is zero-length and thus is also invalid).
  31  	//
  32  	// This field is required.
  33  	SpanID SpanID `json:"spanId,omitempty"`
  34  	// trace_state conveys information about request position in multiple distributed tracing graphs.
  35  	// It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header
  36  	// See also https://github.com/w3c/distributed-tracing for more details about this field.
  37  	TraceState string `json:"traceState,omitempty"`
  38  	// The `span_id` of this span's parent span. If this is a root span, then this
  39  	// field must be empty. The ID is an 8-byte array.
  40  	ParentSpanID SpanID `json:"parentSpanId,omitempty"`
  41  	// Flags, a bit field.
  42  	//
  43  	// Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
  44  	// Context specification. To read the 8-bit W3C trace flag, use
  45  	// `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
  46  	//
  47  	// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
  48  	//
  49  	// Bits 8 and 9 represent the 3 states of whether a span's parent
  50  	// is remote. The states are (unknown, is not remote, is remote).
  51  	// To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
  52  	// To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
  53  	//
  54  	// When creating span messages, if the message is logically forwarded from another source
  55  	// with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD
  56  	// be copied as-is. If creating from a source that does not have an equivalent flags field
  57  	// (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST
  58  	// be set to zero.
  59  	// Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
  60  	//
  61  	// [Optional].
  62  	Flags uint32 `json:"flags,omitempty"`
  63  	// A description of the span's operation.
  64  	//
  65  	// For example, the name can be a qualified method name or a file name
  66  	// and a line number where the operation is called. A best practice is to use
  67  	// the same display name at the same call point in an application.
  68  	// This makes it easier to correlate spans in different traces.
  69  	//
  70  	// This field is semantically required to be set to non-empty string.
  71  	// Empty value is equivalent to an unknown span name.
  72  	//
  73  	// This field is required.
  74  	Name string `json:"name"`
  75  	// Distinguishes between spans generated in a particular context. For example,
  76  	// two spans with the same name may be distinguished using `CLIENT` (caller)
  77  	// and `SERVER` (callee) to identify queueing latency associated with the span.
  78  	Kind SpanKind `json:"kind,omitempty"`
  79  	// start_time_unix_nano is the start time of the span. On the client side, this is the time
  80  	// kept by the local machine where the span execution starts. On the server side, this
  81  	// is the time when the server's application handler starts running.
  82  	// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
  83  	//
  84  	// This field is semantically required and it is expected that end_time >= start_time.
  85  	StartTime time.Time `json:"startTimeUnixNano,omitempty"`
  86  	// end_time_unix_nano is the end time of the span. On the client side, this is the time
  87  	// kept by the local machine where the span execution ends. On the server side, this
  88  	// is the time when the server application handler stops running.
  89  	// Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970.
  90  	//
  91  	// This field is semantically required and it is expected that end_time >= start_time.
  92  	EndTime time.Time `json:"endTimeUnixNano,omitempty"`
  93  	// attributes is a collection of key/value pairs. Note, global attributes
  94  	// like server name can be set using the resource API. Examples of attributes:
  95  	//
  96  	//     "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
  97  	//     "/http/server_latency": 300
  98  	//     "example.com/myattribute": true
  99  	//     "example.com/score": 10.239
 100  	//
 101  	// The OpenTelemetry API specification further restricts the allowed value types:
 102  	// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute
 103  	// Attribute keys MUST be unique (it is not allowed to have more than one
 104  	// attribute with the same key).
 105  	Attrs []Attr `json:"attributes,omitempty"`
 106  	// dropped_attributes_count is the number of attributes that were discarded. Attributes
 107  	// can be discarded because their keys are too long or because there are too many
 108  	// attributes. If this value is 0, then no attributes were dropped.
 109  	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"`
 110  	// events is a collection of Event items.
 111  	Events []*SpanEvent `json:"events,omitempty"`
 112  	// dropped_events_count is the number of dropped events. If the value is 0, then no
 113  	// events were dropped.
 114  	DroppedEvents uint32 `json:"droppedEventsCount,omitempty"`
 115  	// links is a collection of Links, which are references from this span to a span
 116  	// in the same or different trace.
 117  	Links []*SpanLink `json:"links,omitempty"`
 118  	// dropped_links_count is the number of dropped links after the maximum size was
 119  	// enforced. If this value is 0, then no links were dropped.
 120  	DroppedLinks uint32 `json:"droppedLinksCount,omitempty"`
 121  	// An optional final status for this span. Semantically when Status isn't set, it means
 122  	// span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0).
 123  	Status *Status `json:"status,omitempty"`
 124  }
 125  
 126  // MarshalJSON encodes s into OTLP formatted JSON.
 127  func (s Span) MarshalJSON() ([]byte, error) {
 128  	startT := s.StartTime.UnixNano()
 129  	if s.StartTime.IsZero() || startT < 0 {
 130  		startT = 0
 131  	}
 132  
 133  	endT := s.EndTime.UnixNano()
 134  	if s.EndTime.IsZero() || endT < 0 {
 135  		endT = 0
 136  	}
 137  
 138  	// Override non-empty default SpanID marshal and omitempty.
 139  	var parentSpanId string
 140  	if !s.ParentSpanID.IsEmpty() {
 141  		b := make([]byte, hex.EncodedLen(spanIDSize))
 142  		hex.Encode(b, s.ParentSpanID[:])
 143  		parentSpanId = string(b)
 144  	}
 145  
 146  	type Alias Span
 147  	return json.Marshal(struct {
 148  		Alias
 149  		ParentSpanID string `json:"parentSpanId,omitempty"`
 150  		StartTime    uint64 `json:"startTimeUnixNano,omitempty"`
 151  		EndTime      uint64 `json:"endTimeUnixNano,omitempty"`
 152  	}{
 153  		Alias:        Alias(s),
 154  		ParentSpanID: parentSpanId,
 155  		StartTime:    uint64(startT), // nolint:gosec  // >0 checked above.
 156  		EndTime:      uint64(endT),   // nolint:gosec  // >0 checked above.
 157  	})
 158  }
 159  
 160  // UnmarshalJSON decodes the OTLP formatted JSON contained in data into s.
 161  func (s *Span) UnmarshalJSON(data []byte) error {
 162  	decoder := json.NewDecoder(bytes.NewReader(data))
 163  
 164  	t, err := decoder.Token()
 165  	if err != nil {
 166  		return err
 167  	}
 168  	if t != json.Delim('{') {
 169  		return errors.New("invalid Span type")
 170  	}
 171  
 172  	for decoder.More() {
 173  		keyIface, err := decoder.Token()
 174  		if err != nil {
 175  			if errors.Is(err, io.EOF) {
 176  				// Empty.
 177  				return nil
 178  			}
 179  			return err
 180  		}
 181  
 182  		key, ok := keyIface.(string)
 183  		if !ok {
 184  			return fmt.Errorf("invalid Span field: %#v", keyIface)
 185  		}
 186  
 187  		switch key {
 188  		case "traceId", "trace_id":
 189  			err = decoder.Decode(&s.TraceID)
 190  		case "spanId", "span_id":
 191  			err = decoder.Decode(&s.SpanID)
 192  		case "traceState", "trace_state":
 193  			err = decoder.Decode(&s.TraceState)
 194  		case "parentSpanId", "parent_span_id":
 195  			err = decoder.Decode(&s.ParentSpanID)
 196  		case "flags":
 197  			err = decoder.Decode(&s.Flags)
 198  		case "name":
 199  			err = decoder.Decode(&s.Name)
 200  		case "kind":
 201  			err = decoder.Decode(&s.Kind)
 202  		case "startTimeUnixNano", "start_time_unix_nano":
 203  			var val protoUint64
 204  			err = decoder.Decode(&val)
 205  			v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec  // Overflow checked.
 206  			s.StartTime = time.Unix(0, v)
 207  		case "endTimeUnixNano", "end_time_unix_nano":
 208  			var val protoUint64
 209  			err = decoder.Decode(&val)
 210  			v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec  // Overflow checked.
 211  			s.EndTime = time.Unix(0, v)
 212  		case "attributes":
 213  			err = decoder.Decode(&s.Attrs)
 214  		case "droppedAttributesCount", "dropped_attributes_count":
 215  			err = decoder.Decode(&s.DroppedAttrs)
 216  		case "events":
 217  			err = decoder.Decode(&s.Events)
 218  		case "droppedEventsCount", "dropped_events_count":
 219  			err = decoder.Decode(&s.DroppedEvents)
 220  		case "links":
 221  			err = decoder.Decode(&s.Links)
 222  		case "droppedLinksCount", "dropped_links_count":
 223  			err = decoder.Decode(&s.DroppedLinks)
 224  		case "status":
 225  			err = decoder.Decode(&s.Status)
 226  		default:
 227  			// Skip unknown.
 228  		}
 229  
 230  		if err != nil {
 231  			return err
 232  		}
 233  	}
 234  	return nil
 235  }
 236  
 237  // SpanFlags represents constants used to interpret the
 238  // Span.flags field, which is protobuf 'fixed32' type and is to
 239  // be used as bit-fields. Each non-zero value defined in this enum is
 240  // a bit-mask.  To extract the bit-field, for example, use an
 241  // expression like:
 242  //
 243  //	(span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK)
 244  //
 245  // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
 246  //
 247  // Note that Span flags were introduced in version 1.1 of the
 248  // OpenTelemetry protocol.  Older Span producers do not set this
 249  // field, consequently consumers should not rely on the absence of a
 250  // particular flag bit to indicate the presence of a particular feature.
 251  type SpanFlags int32
 252  
 253  const (
 254  	// SpanFlagsTraceFlagsMask is a mask for trace-flags.
 255  	//
 256  	// Bits 0-7 are used for trace flags.
 257  	SpanFlagsTraceFlagsMask SpanFlags = 255
 258  	// SpanFlagsContextHasIsRemoteMask is a mask for HAS_IS_REMOTE status.
 259  	//
 260  	// Bits 8 and 9 are used to indicate that the parent span or link span is
 261  	// remote. Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known.
 262  	SpanFlagsContextHasIsRemoteMask SpanFlags = 256
 263  	// SpanFlagsContextIsRemoteMask is a mask for IS_REMOTE status.
 264  	//
 265  	// Bits 8 and 9 are used to indicate that the parent span or link span is
 266  	// remote. Bit 9 (`IS_REMOTE`) indicates whether the span or link is
 267  	// remote.
 268  	SpanFlagsContextIsRemoteMask SpanFlags = 512
 269  )
 270  
 271  // SpanKind is the type of span. Can be used to specify additional relationships between spans
 272  // in addition to a parent/child relationship.
 273  type SpanKind int32
 274  
 275  const (
 276  	// SpanKindInternal indicates that the span represents an internal
 277  	// operation within an application, as opposed to an operation happening at
 278  	// the boundaries.
 279  	SpanKindInternal SpanKind = 1
 280  	// SpanKindServer indicates that the span covers server-side handling of an
 281  	// RPC or other remote network request.
 282  	SpanKindServer SpanKind = 2
 283  	// SpanKindClient indicates that the span describes a request to some
 284  	// remote service.
 285  	SpanKindClient SpanKind = 3
 286  	// SpanKindProducer indicates that the span describes a producer sending a
 287  	// message to a broker. Unlike SpanKindClient and SpanKindServer, there is
 288  	// often no direct critical path latency relationship between producer and
 289  	// consumer spans. A SpanKindProducer span ends when the message was
 290  	// accepted by the broker while the logical processing of the message might
 291  	// span a much longer time.
 292  	SpanKindProducer SpanKind = 4
 293  	// SpanKindConsumer indicates that the span describes a consumer receiving
 294  	// a message from a broker. Like SpanKindProducer, there is often no direct
 295  	// critical path latency relationship between producer and consumer spans.
 296  	SpanKindConsumer SpanKind = 5
 297  )
 298  
 299  // SpanEvent is a time-stamped annotation of the span, consisting of
 300  // user-supplied text description and key-value pairs.
 301  type SpanEvent struct {
 302  	// time_unix_nano is the time the event occurred.
 303  	Time time.Time `json:"timeUnixNano,omitempty"`
 304  	// name of the event.
 305  	// This field is semantically required to be set to non-empty string.
 306  	Name string `json:"name,omitempty"`
 307  	// attributes is a collection of attribute key/value pairs on the event.
 308  	// Attribute keys MUST be unique (it is not allowed to have more than one
 309  	// attribute with the same key).
 310  	Attrs []Attr `json:"attributes,omitempty"`
 311  	// dropped_attributes_count is the number of dropped attributes. If the value is 0,
 312  	// then no attributes were dropped.
 313  	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"`
 314  }
 315  
 316  // MarshalJSON encodes e into OTLP formatted JSON.
 317  func (e SpanEvent) MarshalJSON() ([]byte, error) {
 318  	t := e.Time.UnixNano()
 319  	if e.Time.IsZero() || t < 0 {
 320  		t = 0
 321  	}
 322  
 323  	type Alias SpanEvent
 324  	return json.Marshal(struct {
 325  		Alias
 326  		Time uint64 `json:"timeUnixNano,omitempty"`
 327  	}{
 328  		Alias: Alias(e),
 329  		Time:  uint64(t), // nolint: gosec  // >0 checked above
 330  	})
 331  }
 332  
 333  // UnmarshalJSON decodes the OTLP formatted JSON contained in data into se.
 334  func (se *SpanEvent) UnmarshalJSON(data []byte) error {
 335  	decoder := json.NewDecoder(bytes.NewReader(data))
 336  
 337  	t, err := decoder.Token()
 338  	if err != nil {
 339  		return err
 340  	}
 341  	if t != json.Delim('{') {
 342  		return errors.New("invalid SpanEvent type")
 343  	}
 344  
 345  	for decoder.More() {
 346  		keyIface, err := decoder.Token()
 347  		if err != nil {
 348  			if errors.Is(err, io.EOF) {
 349  				// Empty.
 350  				return nil
 351  			}
 352  			return err
 353  		}
 354  
 355  		key, ok := keyIface.(string)
 356  		if !ok {
 357  			return fmt.Errorf("invalid SpanEvent field: %#v", keyIface)
 358  		}
 359  
 360  		switch key {
 361  		case "timeUnixNano", "time_unix_nano":
 362  			var val protoUint64
 363  			err = decoder.Decode(&val)
 364  			v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec  // Overflow checked.
 365  			se.Time = time.Unix(0, v)
 366  		case "name":
 367  			err = decoder.Decode(&se.Name)
 368  		case "attributes":
 369  			err = decoder.Decode(&se.Attrs)
 370  		case "droppedAttributesCount", "dropped_attributes_count":
 371  			err = decoder.Decode(&se.DroppedAttrs)
 372  		default:
 373  			// Skip unknown.
 374  		}
 375  
 376  		if err != nil {
 377  			return err
 378  		}
 379  	}
 380  	return nil
 381  }
 382  
 383  // SpanLink is a reference from the current span to another span in the same
 384  // trace or in a different trace. For example, this can be used in batching
 385  // operations, where a single batch handler processes multiple requests from
 386  // different traces or when the handler receives a request from a different
 387  // project.
 388  type SpanLink struct {
 389  	// A unique identifier of a trace that this linked span is part of. The ID is a
 390  	// 16-byte array.
 391  	TraceID TraceID `json:"traceId,omitempty"`
 392  	// A unique identifier for the linked span. The ID is an 8-byte array.
 393  	SpanID SpanID `json:"spanId,omitempty"`
 394  	// The trace_state associated with the link.
 395  	TraceState string `json:"traceState,omitempty"`
 396  	// attributes is a collection of attribute key/value pairs on the link.
 397  	// Attribute keys MUST be unique (it is not allowed to have more than one
 398  	// attribute with the same key).
 399  	Attrs []Attr `json:"attributes,omitempty"`
 400  	// dropped_attributes_count is the number of dropped attributes. If the value is 0,
 401  	// then no attributes were dropped.
 402  	DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"`
 403  	// Flags, a bit field.
 404  	//
 405  	// Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace
 406  	// Context specification. To read the 8-bit W3C trace flag, use
 407  	// `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`.
 408  	//
 409  	// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions.
 410  	//
 411  	// Bits 8 and 9 represent the 3 states of whether the link is remote.
 412  	// The states are (unknown, is not remote, is remote).
 413  	// To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`.
 414  	// To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`.
 415  	//
 416  	// Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero.
 417  	// When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero.
 418  	//
 419  	// [Optional].
 420  	Flags uint32 `json:"flags,omitempty"`
 421  }
 422  
 423  // UnmarshalJSON decodes the OTLP formatted JSON contained in data into sl.
 424  func (sl *SpanLink) UnmarshalJSON(data []byte) error {
 425  	decoder := json.NewDecoder(bytes.NewReader(data))
 426  
 427  	t, err := decoder.Token()
 428  	if err != nil {
 429  		return err
 430  	}
 431  	if t != json.Delim('{') {
 432  		return errors.New("invalid SpanLink type")
 433  	}
 434  
 435  	for decoder.More() {
 436  		keyIface, err := decoder.Token()
 437  		if err != nil {
 438  			if errors.Is(err, io.EOF) {
 439  				// Empty.
 440  				return nil
 441  			}
 442  			return err
 443  		}
 444  
 445  		key, ok := keyIface.(string)
 446  		if !ok {
 447  			return fmt.Errorf("invalid SpanLink field: %#v", keyIface)
 448  		}
 449  
 450  		switch key {
 451  		case "traceId", "trace_id":
 452  			err = decoder.Decode(&sl.TraceID)
 453  		case "spanId", "span_id":
 454  			err = decoder.Decode(&sl.SpanID)
 455  		case "traceState", "trace_state":
 456  			err = decoder.Decode(&sl.TraceState)
 457  		case "attributes":
 458  			err = decoder.Decode(&sl.Attrs)
 459  		case "droppedAttributesCount", "dropped_attributes_count":
 460  			err = decoder.Decode(&sl.DroppedAttrs)
 461  		case "flags":
 462  			err = decoder.Decode(&sl.Flags)
 463  		default:
 464  			// Skip unknown.
 465  		}
 466  
 467  		if err != nil {
 468  			return err
 469  		}
 470  	}
 471  	return nil
 472  }
 473