1 // Copyright The OpenTelemetry Authors
2 // SPDX-License-Identifier: Apache-2.0
3
4 package telemetry
5
6 import (
7 "encoding/hex"
8 "errors"
9 "fmt"
10 )
11
12 const (
13 traceIDSize = 16
14 spanIDSize = 8
15 )
16
17 // TraceID is a custom data type that is used for all trace IDs.
18 type TraceID [traceIDSize]byte
19
20 // String returns the hex string representation form of a TraceID.
21 func (tid TraceID) String() string {
22 return hex.EncodeToString(tid[:])
23 }
24
25 // IsEmpty returns false if id contains at least one non-zero byte.
26 func (tid TraceID) IsEmpty() bool {
27 return tid == [traceIDSize]byte{}
28 }
29
30 // MarshalJSON converts the trace ID into a hex string enclosed in quotes.
31 func (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.
40 func (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.
46 type SpanID [spanIDSize]byte
47
48 // String returns the hex string representation form of a SpanID.
49 func (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.
54 func (sid SpanID) IsEmpty() bool {
55 return sid == [spanIDSize]byte{}
56 }
57
58 // MarshalJSON converts span ID into a hex string enclosed in quotes.
59 func (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.
67 func (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.
73 func 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.
85 func unmarshalJSON(dst, 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 }
104