1// Copyright The OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package trace
16
17import (
18	"bytes"
19	"encoding/hex"
20	"encoding/json"
21)
22
23const (
24	// FlagsSampled is a bitmask with the sampled bit set. A SpanContext
25	// with the sampling bit set means the span is sampled.
26	FlagsSampled = byte(0x01)
27	// FlagsDeferred is a bitmask with the deferred bit set. A SpanContext
28	// with the deferred bit set means the sampling decision has been
29	// defered to the receiver.
30	FlagsDeferred = byte(0x02)
31	// FlagsDebug is a bitmask with the debug bit set.
32	FlagsDebug = byte(0x04)
33
34	ErrInvalidHexID errorConst = "trace-id and span-id can only contain [0-9a-f] characters, all lowercase"
35
36	ErrInvalidTraceIDLength errorConst = "hex encoded trace-id must have length equals to 32"
37	ErrNilTraceID           errorConst = "trace-id can't be all zero"
38
39	ErrInvalidSpanIDLength errorConst = "hex encoded span-id must have length equals to 16"
40	ErrNilSpanID           errorConst = "span-id can't be all zero"
41)
42
43type errorConst string
44
45func (e errorConst) Error() string {
46	return string(e)
47}
48
49// ID is a unique identity of a trace.
50type ID [16]byte
51
52var nilTraceID ID
53var _ json.Marshaler = nilTraceID
54
55// IsValid checks whether the trace ID is valid. A valid trace ID does
56// not consist of zeros only.
57func (t ID) IsValid() bool {
58	return !bytes.Equal(t[:], nilTraceID[:])
59}
60
61// MarshalJSON implements a custom marshal function to encode TraceID
62// as a hex string.
63func (t ID) MarshalJSON() ([]byte, error) {
64	return json.Marshal(t.String())
65}
66
67// String returns the hex string representation form of a TraceID
68func (t ID) String() string {
69	return hex.EncodeToString(t[:])
70}
71
72// SpanID is a unique identify of a span in a trace.
73type SpanID [8]byte
74
75var nilSpanID SpanID
76var _ json.Marshaler = nilSpanID
77
78// IsValid checks whether the span ID is valid. A valid span ID does
79// not consist of zeros only.
80func (s SpanID) IsValid() bool {
81	return !bytes.Equal(s[:], nilSpanID[:])
82}
83
84// MarshalJSON implements a custom marshal function to encode SpanID
85// as a hex string.
86func (s SpanID) MarshalJSON() ([]byte, error) {
87	return json.Marshal(s.String())
88}
89
90// String returns the hex string representation form of a SpanID
91func (s SpanID) String() string {
92	return hex.EncodeToString(s[:])
93}
94
95// IDFromHex returns a TraceID from a hex string if it is compliant
96// with the w3c trace-context specification.
97// See more at https://www.w3.org/TR/trace-context/#trace-id
98func IDFromHex(h string) (ID, error) {
99	t := ID{}
100	if len(h) != 32 {
101		return t, ErrInvalidTraceIDLength
102	}
103
104	if err := decodeHex(h, t[:]); err != nil {
105		return t, err
106	}
107
108	if !t.IsValid() {
109		return t, ErrNilTraceID
110	}
111	return t, nil
112}
113
114// SpanIDFromHex returns a SpanID from a hex string if it is compliant
115// with the w3c trace-context specification.
116// See more at https://www.w3.org/TR/trace-context/#parent-id
117func SpanIDFromHex(h string) (SpanID, error) {
118	s := SpanID{}
119	if len(h) != 16 {
120		return s, ErrInvalidSpanIDLength
121	}
122
123	if err := decodeHex(h, s[:]); err != nil {
124		return s, err
125	}
126
127	if !s.IsValid() {
128		return s, ErrNilSpanID
129	}
130	return s, nil
131}
132
133func decodeHex(h string, b []byte) error {
134	for _, r := range h {
135		switch {
136		case 'a' <= r && r <= 'f':
137			continue
138		case '0' <= r && r <= '9':
139			continue
140		default:
141			return ErrInvalidHexID
142		}
143	}
144
145	decoded, err := hex.DecodeString(h)
146	if err != nil {
147		return err
148	}
149
150	copy(b, decoded)
151	return nil
152}
153
154// SpanContext contains basic information about the span - its trace
155// ID, span ID and trace flags.
156type SpanContext struct {
157	TraceID    ID
158	SpanID     SpanID
159	TraceFlags byte
160}
161
162// EmptySpanContext is meant for internal use to return invalid span
163// context during error conditions.
164func EmptySpanContext() SpanContext {
165	return SpanContext{}
166}
167
168// IsValid checks if the span context is valid. A valid span context
169// has a valid trace ID and a valid span ID.
170func (sc SpanContext) IsValid() bool {
171	return sc.HasTraceID() && sc.HasSpanID()
172}
173
174// HasTraceID checks if the span context has a valid trace ID.
175func (sc SpanContext) HasTraceID() bool {
176	return sc.TraceID.IsValid()
177}
178
179// HasSpanID checks if the span context has a valid span ID.
180func (sc SpanContext) HasSpanID() bool {
181	return sc.SpanID.IsValid()
182}
183
184// isDeferred returns if the deferred bit is set in the trace flags.
185func (sc SpanContext) isDeferred() bool {
186	return sc.TraceFlags&FlagsDeferred == FlagsDeferred
187}
188
189// isDebug returns if the debug bit is set in the trace flags.
190func (sc SpanContext) isDebug() bool {
191	return sc.TraceFlags&FlagsDebug == FlagsDebug
192}
193
194// IsSampled returns if the sampling bit is set in the trace flags.
195func (sc SpanContext) IsSampled() bool {
196	return sc.TraceFlags&FlagsSampled == FlagsSampled
197}
198