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