value.go

  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}