value.go raw

   1  // Copyright The OpenTelemetry Authors
   2  // SPDX-License-Identifier: Apache-2.0
   3  
   4  package telemetry // import "go.opentelemetry.io/otel/trace/internal/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{
  94  		num: uint64(v), // nolint: gosec  // Store raw bytes.
  95  		any: ValueKindInt64,
  96  	}
  97  }
  98  
  99  // Float64Value returns a [Value] for a float64.
 100  func Float64Value(v float64) Value {
 101  	return Value{num: math.Float64bits(v), any: ValueKindFloat64}
 102  }
 103  
 104  // BoolValue returns a [Value] for a bool.
 105  func 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.
 115  func 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.
 124  func 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.
 133  func 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.
 141  func (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.
 151  func (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.
 156  func (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.
 166  func (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.
 172  func (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.
 182  func (v Value) asBool() bool { return v.num == 1 }
 183  
 184  // AsFloat64 returns the value held by v as a float64.
 185  func (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.
 195  func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) }
 196  
 197  // AsBytes returns the value held by v as a []byte.
 198  func (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.
 208  func (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.
 213  func (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.
 223  func (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.
 228  func (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.
 238  func (v Value) asMap() []Attr {
 239  	return unsafe.Slice((*Attr)(v.any.(mapptr)), v.num)
 240  }
 241  
 242  // Kind returns the Kind of v.
 243  func (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 reports whether v does not hold any value.
 261  func (v Value) Empty() bool { return v.Kind() == ValueKindEmpty }
 262  
 263  // Equal reports whether v is equal to w.
 264  func (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  
 293  func 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.
 307  func (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 string(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.
 338  func (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.
 384  func (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  }
 454