Source file src/log/slog/value.go

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

View as plain text