value.go

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