1// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4package telemetry
5
6import (
7 "bytes"
8 "encoding/json"
9 "errors"
10 "fmt"
11 "io"
12)
13
14// Traces represents the traces data that can be stored in a persistent storage,
15// OR can be embedded by other protocols that transfer OTLP traces data but do
16// not implement the OTLP protocol.
17//
18// The main difference between this message and collector protocol is that
19// in this message there will not be any "control" or "metadata" specific to
20// OTLP protocol.
21//
22// When new fields are added into this message, the OTLP request MUST be updated
23// as well.
24type Traces struct {
25 // An array of ResourceSpans.
26 // For data coming from a single resource this array will typically contain
27 // one element. Intermediary nodes that receive data from multiple origins
28 // typically batch the data before forwarding further and in that case this
29 // array will contain multiple elements.
30 ResourceSpans []*ResourceSpans `json:"resourceSpans,omitempty"`
31}
32
33// UnmarshalJSON decodes the OTLP formatted JSON contained in data into td.
34func (td *Traces) UnmarshalJSON(data []byte) error {
35 decoder := json.NewDecoder(bytes.NewReader(data))
36
37 t, err := decoder.Token()
38 if err != nil {
39 return err
40 }
41 if t != json.Delim('{') {
42 return errors.New("invalid TracesData type")
43 }
44
45 for decoder.More() {
46 keyIface, err := decoder.Token()
47 if err != nil {
48 if errors.Is(err, io.EOF) {
49 // Empty.
50 return nil
51 }
52 return err
53 }
54
55 key, ok := keyIface.(string)
56 if !ok {
57 return fmt.Errorf("invalid TracesData field: %#v", keyIface)
58 }
59
60 switch key {
61 case "resourceSpans", "resource_spans":
62 err = decoder.Decode(&td.ResourceSpans)
63 default:
64 // Skip unknown.
65 }
66
67 if err != nil {
68 return err
69 }
70 }
71 return nil
72}
73
74// A collection of ScopeSpans from a Resource.
75type ResourceSpans struct {
76 // The resource for the spans in this message.
77 // If this field is not set then no resource info is known.
78 Resource Resource `json:"resource"`
79 // A list of ScopeSpans that originate from a resource.
80 ScopeSpans []*ScopeSpans `json:"scopeSpans,omitempty"`
81 // This schema_url applies to the data in the "resource" field. It does not apply
82 // to the data in the "scope_spans" field which have their own schema_url field.
83 SchemaURL string `json:"schemaUrl,omitempty"`
84}
85
86// UnmarshalJSON decodes the OTLP formatted JSON contained in data into rs.
87func (rs *ResourceSpans) UnmarshalJSON(data []byte) error {
88 decoder := json.NewDecoder(bytes.NewReader(data))
89
90 t, err := decoder.Token()
91 if err != nil {
92 return err
93 }
94 if t != json.Delim('{') {
95 return errors.New("invalid ResourceSpans type")
96 }
97
98 for decoder.More() {
99 keyIface, err := decoder.Token()
100 if err != nil {
101 if errors.Is(err, io.EOF) {
102 // Empty.
103 return nil
104 }
105 return err
106 }
107
108 key, ok := keyIface.(string)
109 if !ok {
110 return fmt.Errorf("invalid ResourceSpans field: %#v", keyIface)
111 }
112
113 switch key {
114 case "resource":
115 err = decoder.Decode(&rs.Resource)
116 case "scopeSpans", "scope_spans":
117 err = decoder.Decode(&rs.ScopeSpans)
118 case "schemaUrl", "schema_url":
119 err = decoder.Decode(&rs.SchemaURL)
120 default:
121 // Skip unknown.
122 }
123
124 if err != nil {
125 return err
126 }
127 }
128 return nil
129}
130
131// A collection of Spans produced by an InstrumentationScope.
132type ScopeSpans struct {
133 // The instrumentation scope information for the spans in this message.
134 // Semantically when InstrumentationScope isn't set, it is equivalent with
135 // an empty instrumentation scope name (unknown).
136 Scope *Scope `json:"scope"`
137 // A list of Spans that originate from an instrumentation scope.
138 Spans []*Span `json:"spans,omitempty"`
139 // The Schema URL, if known. This is the identifier of the Schema that the span data
140 // is recorded in. To learn more about Schema URL see
141 // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url
142 // This schema_url applies to all spans and span events in the "spans" field.
143 SchemaURL string `json:"schemaUrl,omitempty"`
144}
145
146// UnmarshalJSON decodes the OTLP formatted JSON contained in data into ss.
147func (ss *ScopeSpans) UnmarshalJSON(data []byte) error {
148 decoder := json.NewDecoder(bytes.NewReader(data))
149
150 t, err := decoder.Token()
151 if err != nil {
152 return err
153 }
154 if t != json.Delim('{') {
155 return errors.New("invalid ScopeSpans type")
156 }
157
158 for decoder.More() {
159 keyIface, err := decoder.Token()
160 if err != nil {
161 if errors.Is(err, io.EOF) {
162 // Empty.
163 return nil
164 }
165 return err
166 }
167
168 key, ok := keyIface.(string)
169 if !ok {
170 return fmt.Errorf("invalid ScopeSpans field: %#v", keyIface)
171 }
172
173 switch key {
174 case "scope":
175 err = decoder.Decode(&ss.Scope)
176 case "spans":
177 err = decoder.Decode(&ss.Spans)
178 case "schemaUrl", "schema_url":
179 err = decoder.Decode(&ss.SchemaURL)
180 default:
181 // Skip unknown.
182 }
183
184 if err != nil {
185 return err
186 }
187 }
188 return nil
189}