1// Copyright The OpenTelemetry Authors
2// SPDX-License-Identifier: Apache-2.0
3
4package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry"
5
6import (
7 "bytes"
8 "cmp"
9 "encoding/base64"
10 "encoding/json"
11 "errors"
12 "fmt"
13 "io"
14 "math"
15 "slices"
16 "strconv"
17 "unsafe"
18)
19
20// A Value represents a structured value.
21// A zero value is valid and represents an empty value.
22type Value struct {
23 // Ensure forward compatibility by explicitly making this not comparable.
24 noCmp [0]func() //nolint: unused // This is indeed used.
25
26 // num holds the value for Int64, Float64, and Bool. It holds the length
27 // for String, Bytes, Slice, Map.
28 num uint64
29 // any holds either the KindBool, KindInt64, KindFloat64, stringptr,
30 // bytesptr, sliceptr, or mapptr. If KindBool, KindInt64, or KindFloat64
31 // then the value of Value is in num as described above. Otherwise, it
32 // contains the value wrapped in the appropriate type.
33 any any
34}
35
36type (
37 // sliceptr represents a value in Value.any for KindString Values.
38 stringptr *byte
39 // bytesptr represents a value in Value.any for KindBytes Values.
40 bytesptr *byte
41 // sliceptr represents a value in Value.any for KindSlice Values.
42 sliceptr *Value
43 // mapptr represents a value in Value.any for KindMap Values.
44 mapptr *Attr
45)
46
47// ValueKind is the kind of a [Value].
48type ValueKind int
49
50// ValueKind values.
51const (
52 ValueKindEmpty ValueKind = iota
53 ValueKindBool
54 ValueKindFloat64
55 ValueKindInt64
56 ValueKindString
57 ValueKindBytes
58 ValueKindSlice
59 ValueKindMap
60)
61
62var valueKindStrings = []string{
63 "Empty",
64 "Bool",
65 "Float64",
66 "Int64",
67 "String",
68 "Bytes",
69 "Slice",
70 "Map",
71}
72
73func (k ValueKind) String() string {
74 if k >= 0 && int(k) < len(valueKindStrings) {
75 return valueKindStrings[k]
76 }
77 return "<unknown telemetry.ValueKind>"
78}
79
80// StringValue returns a new [Value] for a string.
81func StringValue(v string) Value {
82 return Value{
83 num: uint64(len(v)),
84 any: stringptr(unsafe.StringData(v)),
85 }
86}
87
88// IntValue returns a [Value] for an int.
89func IntValue(v int) Value { return Int64Value(int64(v)) }
90
91// Int64Value returns a [Value] for an int64.
92func Int64Value(v int64) Value {
93 return Value{
94 num: uint64(v), // nolint: gosec // Store raw bytes.
95 any: ValueKindInt64,
96 }
97}
98
99// Float64Value returns a [Value] for a float64.
100func Float64Value(v float64) Value {
101 return Value{num: math.Float64bits(v), any: ValueKindFloat64}
102}
103
104// BoolValue returns a [Value] for a bool.
105func BoolValue(v bool) Value { //nolint:revive // Not a control flag.
106 var n uint64
107 if v {
108 n = 1
109 }
110 return Value{num: n, any: ValueKindBool}
111}
112
113// BytesValue returns a [Value] for a byte slice. The passed slice must not be
114// changed after it is passed.
115func BytesValue(v []byte) Value {
116 return Value{
117 num: uint64(len(v)),
118 any: bytesptr(unsafe.SliceData(v)),
119 }
120}
121
122// SliceValue returns a [Value] for a slice of [Value]. The passed slice must
123// not be changed after it is passed.
124func SliceValue(vs ...Value) Value {
125 return Value{
126 num: uint64(len(vs)),
127 any: sliceptr(unsafe.SliceData(vs)),
128 }
129}
130
131// MapValue returns a new [Value] for a slice of key-value pairs. The passed
132// slice must not be changed after it is passed.
133func MapValue(kvs ...Attr) Value {
134 return Value{
135 num: uint64(len(kvs)),
136 any: mapptr(unsafe.SliceData(kvs)),
137 }
138}
139
140// AsString returns the value held by v as a string.
141func (v Value) AsString() string {
142 if sp, ok := v.any.(stringptr); ok {
143 return unsafe.String(sp, v.num)
144 }
145 // TODO: error handle
146 return ""
147}
148
149// asString returns the value held by v as a string. It will panic if the Value
150// is not KindString.
151func (v Value) asString() string {
152 return unsafe.String(v.any.(stringptr), v.num)
153}
154
155// AsInt64 returns the value held by v as an int64.
156func (v Value) AsInt64() int64 {
157 if v.Kind() != ValueKindInt64 {
158 // TODO: error handle
159 return 0
160 }
161 return v.asInt64()
162}
163
164// asInt64 returns the value held by v as an int64. If v is not of KindInt64,
165// this will return garbage.
166func (v Value) asInt64() int64 {
167 // Assumes v.num was a valid int64 (overflow not checked).
168 return int64(v.num) // nolint: gosec
169}
170
171// AsBool returns the value held by v as a bool.
172func (v Value) AsBool() bool {
173 if v.Kind() != ValueKindBool {
174 // TODO: error handle
175 return false
176 }
177 return v.asBool()
178}
179
180// asBool returns the value held by v as a bool. If v is not of KindBool, this
181// will return garbage.
182func (v Value) asBool() bool { return v.num == 1 }
183
184// AsFloat64 returns the value held by v as a float64.
185func (v Value) AsFloat64() float64 {
186 if v.Kind() != ValueKindFloat64 {
187 // TODO: error handle
188 return 0
189 }
190 return v.asFloat64()
191}
192
193// asFloat64 returns the value held by v as a float64. If v is not of
194// KindFloat64, this will return garbage.
195func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) }
196
197// AsBytes returns the value held by v as a []byte.
198func (v Value) AsBytes() []byte {
199 if sp, ok := v.any.(bytesptr); ok {
200 return unsafe.Slice((*byte)(sp), v.num)
201 }
202 // TODO: error handle
203 return nil
204}
205
206// asBytes returns the value held by v as a []byte. It will panic if the Value
207// is not KindBytes.
208func (v Value) asBytes() []byte {
209 return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num)
210}
211
212// AsSlice returns the value held by v as a []Value.
213func (v Value) AsSlice() []Value {
214 if sp, ok := v.any.(sliceptr); ok {
215 return unsafe.Slice((*Value)(sp), v.num)
216 }
217 // TODO: error handle
218 return nil
219}
220
221// asSlice returns the value held by v as a []Value. It will panic if the Value
222// is not KindSlice.
223func (v Value) asSlice() []Value {
224 return unsafe.Slice((*Value)(v.any.(sliceptr)), v.num)
225}
226
227// AsMap returns the value held by v as a []Attr.
228func (v Value) AsMap() []Attr {
229 if sp, ok := v.any.(mapptr); ok {
230 return unsafe.Slice((*Attr)(sp), v.num)
231 }
232 // TODO: error handle
233 return nil
234}
235
236// asMap returns the value held by v as a []Attr. It will panic if the
237// Value is not KindMap.
238func (v Value) asMap() []Attr {
239 return unsafe.Slice((*Attr)(v.any.(mapptr)), v.num)
240}
241
242// Kind returns the Kind of v.
243func (v Value) Kind() ValueKind {
244 switch x := v.any.(type) {
245 case ValueKind:
246 return x
247 case stringptr:
248 return ValueKindString
249 case bytesptr:
250 return ValueKindBytes
251 case sliceptr:
252 return ValueKindSlice
253 case mapptr:
254 return ValueKindMap
255 default:
256 return ValueKindEmpty
257 }
258}
259
260// Empty returns if v does not hold any value.
261func (v Value) Empty() bool { return v.Kind() == ValueKindEmpty }
262
263// Equal returns if v is equal to w.
264func (v Value) Equal(w Value) bool {
265 k1 := v.Kind()
266 k2 := w.Kind()
267 if k1 != k2 {
268 return false
269 }
270 switch k1 {
271 case ValueKindInt64, ValueKindBool:
272 return v.num == w.num
273 case ValueKindString:
274 return v.asString() == w.asString()
275 case ValueKindFloat64:
276 return v.asFloat64() == w.asFloat64()
277 case ValueKindSlice:
278 return slices.EqualFunc(v.asSlice(), w.asSlice(), Value.Equal)
279 case ValueKindMap:
280 sv := sortMap(v.asMap())
281 sw := sortMap(w.asMap())
282 return slices.EqualFunc(sv, sw, Attr.Equal)
283 case ValueKindBytes:
284 return bytes.Equal(v.asBytes(), w.asBytes())
285 case ValueKindEmpty:
286 return true
287 default:
288 // TODO: error handle
289 return false
290 }
291}
292
293func sortMap(m []Attr) []Attr {
294 sm := make([]Attr, len(m))
295 copy(sm, m)
296 slices.SortFunc(sm, func(a, b Attr) int {
297 return cmp.Compare(a.Key, b.Key)
298 })
299
300 return sm
301}
302
303// String returns Value's value as a string, formatted like [fmt.Sprint].
304//
305// The returned string is meant for debugging;
306// the string representation is not stable.
307func (v Value) String() string {
308 switch v.Kind() {
309 case ValueKindString:
310 return v.asString()
311 case ValueKindInt64:
312 // Assumes v.num was a valid int64 (overflow not checked).
313 return strconv.FormatInt(int64(v.num), 10) // nolint: gosec
314 case ValueKindFloat64:
315 return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64)
316 case ValueKindBool:
317 return strconv.FormatBool(v.asBool())
318 case ValueKindBytes:
319 return fmt.Sprint(v.asBytes())
320 case ValueKindMap:
321 return fmt.Sprint(v.asMap())
322 case ValueKindSlice:
323 return fmt.Sprint(v.asSlice())
324 case ValueKindEmpty:
325 return "<nil>"
326 default:
327 // Try to handle this as gracefully as possible.
328 //
329 // Don't panic here. The goal here is to have developers find this
330 // first if a slog.Kind is is not handled. It is
331 // preferable to have user's open issue asking why their attributes
332 // have a "unhandled: " prefix than say that their code is panicking.
333 return fmt.Sprintf("<unhandled telemetry.ValueKind: %s>", v.Kind())
334 }
335}
336
337// MarshalJSON encodes v into OTLP formatted JSON.
338func (v *Value) MarshalJSON() ([]byte, error) {
339 switch v.Kind() {
340 case ValueKindString:
341 return json.Marshal(struct {
342 Value string `json:"stringValue"`
343 }{v.asString()})
344 case ValueKindInt64:
345 return json.Marshal(struct {
346 Value string `json:"intValue"`
347 }{strconv.FormatInt(int64(v.num), 10)}) // nolint: gosec // From raw bytes.
348 case ValueKindFloat64:
349 return json.Marshal(struct {
350 Value float64 `json:"doubleValue"`
351 }{v.asFloat64()})
352 case ValueKindBool:
353 return json.Marshal(struct {
354 Value bool `json:"boolValue"`
355 }{v.asBool()})
356 case ValueKindBytes:
357 return json.Marshal(struct {
358 Value []byte `json:"bytesValue"`
359 }{v.asBytes()})
360 case ValueKindMap:
361 return json.Marshal(struct {
362 Value struct {
363 Values []Attr `json:"values"`
364 } `json:"kvlistValue"`
365 }{struct {
366 Values []Attr `json:"values"`
367 }{v.asMap()}})
368 case ValueKindSlice:
369 return json.Marshal(struct {
370 Value struct {
371 Values []Value `json:"values"`
372 } `json:"arrayValue"`
373 }{struct {
374 Values []Value `json:"values"`
375 }{v.asSlice()}})
376 case ValueKindEmpty:
377 return nil, nil
378 default:
379 return nil, fmt.Errorf("unknown Value kind: %s", v.Kind().String())
380 }
381}
382
383// UnmarshalJSON decodes the OTLP formatted JSON contained in data into v.
384func (v *Value) UnmarshalJSON(data []byte) error {
385 decoder := json.NewDecoder(bytes.NewReader(data))
386
387 t, err := decoder.Token()
388 if err != nil {
389 return err
390 }
391 if t != json.Delim('{') {
392 return errors.New("invalid Value type")
393 }
394
395 for decoder.More() {
396 keyIface, err := decoder.Token()
397 if err != nil {
398 if errors.Is(err, io.EOF) {
399 // Empty.
400 return nil
401 }
402 return err
403 }
404
405 key, ok := keyIface.(string)
406 if !ok {
407 return fmt.Errorf("invalid Value key: %#v", keyIface)
408 }
409
410 switch key {
411 case "stringValue", "string_value":
412 var val string
413 err = decoder.Decode(&val)
414 *v = StringValue(val)
415 case "boolValue", "bool_value":
416 var val bool
417 err = decoder.Decode(&val)
418 *v = BoolValue(val)
419 case "intValue", "int_value":
420 var val protoInt64
421 err = decoder.Decode(&val)
422 *v = Int64Value(val.Int64())
423 case "doubleValue", "double_value":
424 var val float64
425 err = decoder.Decode(&val)
426 *v = Float64Value(val)
427 case "bytesValue", "bytes_value":
428 var val64 string
429 if err := decoder.Decode(&val64); err != nil {
430 return err
431 }
432 var val []byte
433 val, err = base64.StdEncoding.DecodeString(val64)
434 *v = BytesValue(val)
435 case "arrayValue", "array_value":
436 var val struct{ Values []Value }
437 err = decoder.Decode(&val)
438 *v = SliceValue(val.Values...)
439 case "kvlistValue", "kvlist_value":
440 var val struct{ Values []Attr }
441 err = decoder.Decode(&val)
442 *v = MapValue(val.Values...)
443 default:
444 // Skip unknown.
445 continue
446 }
447 // Use first valid. Ignore the rest.
448 return err
449 }
450
451 // Only unknown fields. Return nil without unmarshaling any value.
452 return nil
453}