1// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4package telemetry
5
6import (
7 "encoding/hex"
8 "errors"
9 "fmt"
10)
11
12const (
13 traceIDSize = 16
14 spanIDSize = 8
15)
16
17// TraceID is a custom data type that is used for all trace IDs.
18type TraceID [traceIDSize]byte
19
20// String returns the hex string representation form of a TraceID.
21func (tid TraceID) String() string {
22 return hex.EncodeToString(tid[:])
23}
24
25// IsEmpty returns false if id contains at least one non-zero byte.
26func (tid TraceID) IsEmpty() bool {
27 return tid == [traceIDSize]byte{}
28}
29
30// MarshalJSON converts the trace ID into a hex string enclosed in quotes.
31func (tid TraceID) MarshalJSON() ([]byte, error) {
32 if tid.IsEmpty() {
33 return []byte(`""`), nil
34 }
35 return marshalJSON(tid[:])
36}
37
38// UnmarshalJSON inflates the trace ID from hex string, possibly enclosed in
39// quotes.
40func (tid *TraceID) UnmarshalJSON(data []byte) error {
41 *tid = [traceIDSize]byte{}
42 return unmarshalJSON(tid[:], data)
43}
44
45// SpanID is a custom data type that is used for all span IDs.
46type SpanID [spanIDSize]byte
47
48// String returns the hex string representation form of a SpanID.
49func (sid SpanID) String() string {
50 return hex.EncodeToString(sid[:])
51}
52
53// IsEmpty returns true if the span ID contains at least one non-zero byte.
54func (sid SpanID) IsEmpty() bool {
55 return sid == [spanIDSize]byte{}
56}
57
58// MarshalJSON converts span ID into a hex string enclosed in quotes.
59func (sid SpanID) MarshalJSON() ([]byte, error) {
60 if sid.IsEmpty() {
61 return []byte(`""`), nil
62 }
63 return marshalJSON(sid[:])
64}
65
66// UnmarshalJSON decodes span ID from hex string, possibly enclosed in quotes.
67func (sid *SpanID) UnmarshalJSON(data []byte) error {
68 *sid = [spanIDSize]byte{}
69 return unmarshalJSON(sid[:], data)
70}
71
72// marshalJSON converts id into a hex string enclosed in quotes.
73func marshalJSON(id []byte) ([]byte, error) {
74 // Plus 2 quote chars at the start and end.
75 hexLen := hex.EncodedLen(len(id)) + 2
76
77 b := make([]byte, hexLen)
78 hex.Encode(b[1:hexLen-1], id)
79 b[0], b[hexLen-1] = '"', '"'
80
81 return b, nil
82}
83
84// unmarshalJSON inflates trace id from hex string, possibly enclosed in quotes.
85func unmarshalJSON(dst []byte, src []byte) error {
86 if l := len(src); l >= 2 && src[0] == '"' && src[l-1] == '"' {
87 src = src[1 : l-1]
88 }
89 nLen := len(src)
90 if nLen == 0 {
91 return nil
92 }
93
94 if len(dst) != hex.DecodedLen(nLen) {
95 return errors.New("invalid length for ID")
96 }
97
98 _, err := hex.Decode(dst, src)
99 if err != nil {
100 return fmt.Errorf("cannot unmarshal ID from string '%s': %w", string(src), err)
101 }
102 return nil
103}