value.go raw

   1  // Copyright 2022 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package slog
   6  
   7  import (
   8  	"fmt"
   9  	"math"
  10  	"runtime"
  11  	"strconv"
  12  	"strings"
  13  	"time"
  14  	"unsafe"
  15  
  16  	"golang.org/x/exp/slices"
  17  )
  18  
  19  // A Value can represent any Go value, but unlike type any,
  20  // it can represent most small values without an allocation.
  21  // The zero Value corresponds to nil.
  22  type Value struct {
  23  	_ [0]func() // disallow ==
  24  	// num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration,
  25  	// the string length for KindString, and nanoseconds since the epoch for KindTime.
  26  	num uint64
  27  	// If any is of type Kind, then the value is in num as described above.
  28  	// If any is of type *time.Location, then the Kind is Time and time.Time value
  29  	// can be constructed from the Unix nanos in num and the location (monotonic time
  30  	// is not preserved).
  31  	// If any is of type stringptr, then the Kind is String and the string value
  32  	// consists of the length in num and the pointer in any.
  33  	// Otherwise, the Kind is Any and any is the value.
  34  	// (This implies that Attrs cannot store values of type Kind, *time.Location
  35  	// or stringptr.)
  36  	any any
  37  }
  38  
  39  // Kind is the kind of a Value.
  40  type Kind int
  41  
  42  // The following list is sorted alphabetically, but it's also important that
  43  // KindAny is 0 so that a zero Value represents nil.
  44  
  45  const (
  46  	KindAny Kind = iota
  47  	KindBool
  48  	KindDuration
  49  	KindFloat64
  50  	KindInt64
  51  	KindString
  52  	KindTime
  53  	KindUint64
  54  	KindGroup
  55  	KindLogValuer
  56  )
  57  
  58  var kindStrings = []string{
  59  	"Any",
  60  	"Bool",
  61  	"Duration",
  62  	"Float64",
  63  	"Int64",
  64  	"String",
  65  	"Time",
  66  	"Uint64",
  67  	"Group",
  68  	"LogValuer",
  69  }
  70  
  71  func (k Kind) String() string {
  72  	if k >= 0 && int(k) < len(kindStrings) {
  73  		return kindStrings[k]
  74  	}
  75  	return "<unknown slog.Kind>"
  76  }
  77  
  78  // Unexported version of Kind, just so we can store Kinds in Values.
  79  // (No user-provided value has this type.)
  80  type kind Kind
  81  
  82  // Kind returns v's Kind.
  83  func (v Value) Kind() Kind {
  84  	switch x := v.any.(type) {
  85  	case Kind:
  86  		return x
  87  	case stringptr:
  88  		return KindString
  89  	case timeLocation:
  90  		return KindTime
  91  	case groupptr:
  92  		return KindGroup
  93  	case LogValuer:
  94  		return KindLogValuer
  95  	case kind: // a kind is just a wrapper for a Kind
  96  		return KindAny
  97  	default:
  98  		return KindAny
  99  	}
 100  }
 101  
 102  //////////////// Constructors
 103  
 104  // IntValue returns a Value for an int.
 105  func IntValue(v int) Value {
 106  	return Int64Value(int64(v))
 107  }
 108  
 109  // Int64Value returns a Value for an int64.
 110  func Int64Value(v int64) Value {
 111  	return Value{num: uint64(v), any: KindInt64}
 112  }
 113  
 114  // Uint64Value returns a Value for a uint64.
 115  func Uint64Value(v uint64) Value {
 116  	return Value{num: v, any: KindUint64}
 117  }
 118  
 119  // Float64Value returns a Value for a floating-point number.
 120  func Float64Value(v float64) Value {
 121  	return Value{num: math.Float64bits(v), any: KindFloat64}
 122  }
 123  
 124  // BoolValue returns a Value for a bool.
 125  func BoolValue(v bool) Value {
 126  	u := uint64(0)
 127  	if v {
 128  		u = 1
 129  	}
 130  	return Value{num: u, any: KindBool}
 131  }
 132  
 133  // Unexported version of *time.Location, just so we can store *time.Locations in
 134  // Values. (No user-provided value has this type.)
 135  type timeLocation *time.Location
 136  
 137  // TimeValue returns a Value for a time.Time.
 138  // It discards the monotonic portion.
 139  func TimeValue(v time.Time) Value {
 140  	if v.IsZero() {
 141  		// UnixNano on the zero time is undefined, so represent the zero time
 142  		// with a nil *time.Location instead. time.Time.Location method never
 143  		// returns nil, so a Value with any == timeLocation(nil) cannot be
 144  		// mistaken for any other Value, time.Time or otherwise.
 145  		return Value{any: timeLocation(nil)}
 146  	}
 147  	return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())}
 148  }
 149  
 150  // DurationValue returns a Value for a time.Duration.
 151  func DurationValue(v time.Duration) Value {
 152  	return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
 153  }
 154  
 155  // AnyValue returns a Value for the supplied value.
 156  //
 157  // If the supplied value is of type Value, it is returned
 158  // unmodified.
 159  //
 160  // Given a value of one of Go's predeclared string, bool, or
 161  // (non-complex) numeric types, AnyValue returns a Value of kind
 162  // String, Bool, Uint64, Int64, or Float64. The width of the
 163  // original numeric type is not preserved.
 164  //
 165  // Given a time.Time or time.Duration value, AnyValue returns a Value of kind
 166  // KindTime or KindDuration. The monotonic time is not preserved.
 167  //
 168  // For nil, or values of all other types, including named types whose
 169  // underlying type is numeric, AnyValue returns a value of kind KindAny.
 170  func AnyValue(v any) Value {
 171  	switch v := v.(type) {
 172  	case string:
 173  		return StringValue(v)
 174  	case int:
 175  		return Int64Value(int64(v))
 176  	case uint:
 177  		return Uint64Value(uint64(v))
 178  	case int64:
 179  		return Int64Value(v)
 180  	case uint64:
 181  		return Uint64Value(v)
 182  	case bool:
 183  		return BoolValue(v)
 184  	case time.Duration:
 185  		return DurationValue(v)
 186  	case time.Time:
 187  		return TimeValue(v)
 188  	case uint8:
 189  		return Uint64Value(uint64(v))
 190  	case uint16:
 191  		return Uint64Value(uint64(v))
 192  	case uint32:
 193  		return Uint64Value(uint64(v))
 194  	case uintptr:
 195  		return Uint64Value(uint64(v))
 196  	case int8:
 197  		return Int64Value(int64(v))
 198  	case int16:
 199  		return Int64Value(int64(v))
 200  	case int32:
 201  		return Int64Value(int64(v))
 202  	case float64:
 203  		return Float64Value(v)
 204  	case float32:
 205  		return Float64Value(float64(v))
 206  	case []Attr:
 207  		return GroupValue(v...)
 208  	case Kind:
 209  		return Value{any: kind(v)}
 210  	case Value:
 211  		return v
 212  	default:
 213  		return Value{any: v}
 214  	}
 215  }
 216  
 217  //////////////// Accessors
 218  
 219  // Any returns v's value as an any.
 220  func (v Value) Any() any {
 221  	switch v.Kind() {
 222  	case KindAny:
 223  		if k, ok := v.any.(kind); ok {
 224  			return Kind(k)
 225  		}
 226  		return v.any
 227  	case KindLogValuer:
 228  		return v.any
 229  	case KindGroup:
 230  		return v.group()
 231  	case KindInt64:
 232  		return int64(v.num)
 233  	case KindUint64:
 234  		return v.num
 235  	case KindFloat64:
 236  		return v.float()
 237  	case KindString:
 238  		return v.str()
 239  	case KindBool:
 240  		return v.bool()
 241  	case KindDuration:
 242  		return v.duration()
 243  	case KindTime:
 244  		return v.time()
 245  	default:
 246  		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
 247  	}
 248  }
 249  
 250  // Int64 returns v's value as an int64. It panics
 251  // if v is not a signed integer.
 252  func (v Value) Int64() int64 {
 253  	if g, w := v.Kind(), KindInt64; g != w {
 254  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
 255  	}
 256  	return int64(v.num)
 257  }
 258  
 259  // Uint64 returns v's value as a uint64. It panics
 260  // if v is not an unsigned integer.
 261  func (v Value) Uint64() uint64 {
 262  	if g, w := v.Kind(), KindUint64; g != w {
 263  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
 264  	}
 265  	return v.num
 266  }
 267  
 268  // Bool returns v's value as a bool. It panics
 269  // if v is not a bool.
 270  func (v Value) Bool() bool {
 271  	if g, w := v.Kind(), KindBool; g != w {
 272  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
 273  	}
 274  	return v.bool()
 275  }
 276  
 277  func (v Value) bool() bool {
 278  	return v.num == 1
 279  }
 280  
 281  // Duration returns v's value as a time.Duration. It panics
 282  // if v is not a time.Duration.
 283  func (v Value) Duration() time.Duration {
 284  	if g, w := v.Kind(), KindDuration; g != w {
 285  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
 286  	}
 287  
 288  	return v.duration()
 289  }
 290  
 291  func (v Value) duration() time.Duration {
 292  	return time.Duration(int64(v.num))
 293  }
 294  
 295  // Float64 returns v's value as a float64. It panics
 296  // if v is not a float64.
 297  func (v Value) Float64() float64 {
 298  	if g, w := v.Kind(), KindFloat64; g != w {
 299  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
 300  	}
 301  
 302  	return v.float()
 303  }
 304  
 305  func (v Value) float() float64 {
 306  	return math.Float64frombits(v.num)
 307  }
 308  
 309  // Time returns v's value as a time.Time. It panics
 310  // if v is not a time.Time.
 311  func (v Value) Time() time.Time {
 312  	if g, w := v.Kind(), KindTime; g != w {
 313  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
 314  	}
 315  	return v.time()
 316  }
 317  
 318  func (v Value) time() time.Time {
 319  	loc := v.any.(timeLocation)
 320  	if loc == nil {
 321  		return time.Time{}
 322  	}
 323  	return time.Unix(0, int64(v.num)).In(loc)
 324  }
 325  
 326  // LogValuer returns v's value as a LogValuer. It panics
 327  // if v is not a LogValuer.
 328  func (v Value) LogValuer() LogValuer {
 329  	return v.any.(LogValuer)
 330  }
 331  
 332  // Group returns v's value as a []Attr.
 333  // It panics if v's Kind is not KindGroup.
 334  func (v Value) Group() []Attr {
 335  	if sp, ok := v.any.(groupptr); ok {
 336  		return unsafe.Slice((*Attr)(sp), v.num)
 337  	}
 338  	panic("Group: bad kind")
 339  }
 340  
 341  func (v Value) group() []Attr {
 342  	return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num)
 343  }
 344  
 345  //////////////// Other
 346  
 347  // Equal reports whether v and w represent the same Go value.
 348  func (v Value) Equal(w Value) bool {
 349  	k1 := v.Kind()
 350  	k2 := w.Kind()
 351  	if k1 != k2 {
 352  		return false
 353  	}
 354  	switch k1 {
 355  	case KindInt64, KindUint64, KindBool, KindDuration:
 356  		return v.num == w.num
 357  	case KindString:
 358  		return v.str() == w.str()
 359  	case KindFloat64:
 360  		return v.float() == w.float()
 361  	case KindTime:
 362  		return v.time().Equal(w.time())
 363  	case KindAny, KindLogValuer:
 364  		return v.any == w.any // may panic if non-comparable
 365  	case KindGroup:
 366  		return slices.EqualFunc(v.group(), w.group(), Attr.Equal)
 367  	default:
 368  		panic(fmt.Sprintf("bad kind: %s", k1))
 369  	}
 370  }
 371  
 372  // append appends a text representation of v to dst.
 373  // v is formatted as with fmt.Sprint.
 374  func (v Value) append(dst []byte) []byte {
 375  	switch v.Kind() {
 376  	case KindString:
 377  		return append(dst, v.str()...)
 378  	case KindInt64:
 379  		return strconv.AppendInt(dst, int64(v.num), 10)
 380  	case KindUint64:
 381  		return strconv.AppendUint(dst, v.num, 10)
 382  	case KindFloat64:
 383  		return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
 384  	case KindBool:
 385  		return strconv.AppendBool(dst, v.bool())
 386  	case KindDuration:
 387  		return append(dst, v.duration().String()...)
 388  	case KindTime:
 389  		return append(dst, v.time().String()...)
 390  	case KindGroup:
 391  		return fmt.Append(dst, v.group())
 392  	case KindAny, KindLogValuer:
 393  		return fmt.Append(dst, v.any)
 394  	default:
 395  		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
 396  	}
 397  }
 398  
 399  // A LogValuer is any Go value that can convert itself into a Value for logging.
 400  //
 401  // This mechanism may be used to defer expensive operations until they are
 402  // needed, or to expand a single value into a sequence of components.
 403  type LogValuer interface {
 404  	LogValue() Value
 405  }
 406  
 407  const maxLogValues = 100
 408  
 409  // Resolve repeatedly calls LogValue on v while it implements LogValuer,
 410  // and returns the result.
 411  // If v resolves to a group, the group's attributes' values are not recursively
 412  // resolved.
 413  // If the number of LogValue calls exceeds a threshold, a Value containing an
 414  // error is returned.
 415  // Resolve's return value is guaranteed not to be of Kind KindLogValuer.
 416  func (v Value) Resolve() (rv Value) {
 417  	orig := v
 418  	defer func() {
 419  		if r := recover(); r != nil {
 420  			rv = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5)))
 421  		}
 422  	}()
 423  
 424  	for i := 0; i < maxLogValues; i++ {
 425  		if v.Kind() != KindLogValuer {
 426  			return v
 427  		}
 428  		v = v.LogValuer().LogValue()
 429  	}
 430  	err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any())
 431  	return AnyValue(err)
 432  }
 433  
 434  func stack(skip, nFrames int) string {
 435  	pcs := make([]uintptr, nFrames+1)
 436  	n := runtime.Callers(skip+1, pcs)
 437  	if n == 0 {
 438  		return "(no stack)"
 439  	}
 440  	frames := runtime.CallersFrames(pcs[:n])
 441  	var b strings.Builder
 442  	i := 0
 443  	for {
 444  		frame, more := frames.Next()
 445  		fmt.Fprintf(&b, "called from %s (%s:%d)\n", frame.Function, frame.File, frame.Line)
 446  		if !more {
 447  			break
 448  		}
 449  		i++
 450  		if i >= nFrames {
 451  			fmt.Fprintf(&b, "(rest of stack elided)\n")
 452  			break
 453  		}
 454  	}
 455  	return b.String()
 456  }
 457