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, timeTime:
    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  type (
   143  	// Unexported version of *time.Location, just so we can store *time.Locations in
   144  	// Values. (No user-provided value has this type.)
   145  	timeLocation *time.Location
   146  
   147  	// timeTime is for times where UnixNano is undefined.
   148  	timeTime time.Time
   149  )
   150  
   151  // TimeValue returns a [Value] for a [time.Time].
   152  // It discards the monotonic portion.
   153  func TimeValue(v time.Time) Value {
   154  	if v.IsZero() {
   155  		// UnixNano on the zero time is undefined, so represent the zero time
   156  		// with a nil *time.Location instead. time.Time.Location method never
   157  		// returns nil, so a Value with any == timeLocation(nil) cannot be
   158  		// mistaken for any other Value, time.Time or otherwise.
   159  		return Value{any: timeLocation(nil)}
   160  	}
   161  	nsec := v.UnixNano()
   162  	t := time.Unix(0, nsec)
   163  	if v.Equal(t) {
   164  		// UnixNano correctly represents the time, so use a zero-alloc representation.
   165  		return Value{num: uint64(nsec), any: timeLocation(v.Location())}
   166  	}
   167  	// Fall back to the general form.
   168  	// Strip the monotonic portion to match the other representation.
   169  	return Value{any: timeTime(v.Round(0))}
   170  }
   171  
   172  // DurationValue returns a [Value] for a [time.Duration].
   173  func DurationValue(v time.Duration) Value {
   174  	return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
   175  }
   176  
   177  // GroupValue returns a new [Value] for a list of Attrs.
   178  // The caller must not subsequently mutate the argument slice.
   179  func GroupValue(as ...Attr) Value {
   180  	// Remove empty groups.
   181  	// It is simpler overall to do this at construction than
   182  	// to check each Group recursively for emptiness.
   183  	if n := countEmptyGroups(as); n > 0 {
   184  		as2 := make([]Attr, 0, len(as)-n)
   185  		for _, a := range as {
   186  			if !a.Value.isEmptyGroup() {
   187  				as2 = append(as2, a)
   188  			}
   189  		}
   190  		as = as2
   191  	}
   192  	return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))}
   193  }
   194  
   195  // countEmptyGroups returns the number of empty group values in its argument.
   196  func countEmptyGroups(as []Attr) int {
   197  	n := 0
   198  	for _, a := range as {
   199  		if a.Value.isEmptyGroup() {
   200  			n++
   201  		}
   202  	}
   203  	return n
   204  }
   205  
   206  // AnyValue returns a [Value] for the supplied value.
   207  //
   208  // If the supplied value is of type Value, it is returned
   209  // unmodified.
   210  //
   211  // Given a value of one of Go's predeclared string, bool, or
   212  // (non-complex) numeric types, AnyValue returns a Value of kind
   213  // [KindString], [KindBool], [KindUint64], [KindInt64], or [KindFloat64].
   214  // The width of the original numeric type is not preserved.
   215  //
   216  // Given a [time.Time] or [time.Duration] value, AnyValue returns a Value of kind
   217  // [KindTime] or [KindDuration]. The monotonic time is not preserved.
   218  //
   219  // For nil, or values of all other types, including named types whose
   220  // underlying type is numeric, AnyValue returns a value of kind [KindAny].
   221  func AnyValue(v any) Value {
   222  	switch v := v.(type) {
   223  	case string:
   224  		return StringValue(v)
   225  	case int:
   226  		return Int64Value(int64(v))
   227  	case uint:
   228  		return Uint64Value(uint64(v))
   229  	case int64:
   230  		return Int64Value(v)
   231  	case uint64:
   232  		return Uint64Value(v)
   233  	case bool:
   234  		return BoolValue(v)
   235  	case time.Duration:
   236  		return DurationValue(v)
   237  	case time.Time:
   238  		return TimeValue(v)
   239  	case uint8:
   240  		return Uint64Value(uint64(v))
   241  	case uint16:
   242  		return Uint64Value(uint64(v))
   243  	case uint32:
   244  		return Uint64Value(uint64(v))
   245  	case uintptr:
   246  		return Uint64Value(uint64(v))
   247  	case int8:
   248  		return Int64Value(int64(v))
   249  	case int16:
   250  		return Int64Value(int64(v))
   251  	case int32:
   252  		return Int64Value(int64(v))
   253  	case float64:
   254  		return Float64Value(v)
   255  	case float32:
   256  		return Float64Value(float64(v))
   257  	case []Attr:
   258  		return GroupValue(v...)
   259  	case Kind:
   260  		return Value{any: kind(v)}
   261  	case Value:
   262  		return v
   263  	default:
   264  		return Value{any: v}
   265  	}
   266  }
   267  
   268  //////////////// Accessors
   269  
   270  // Any returns v's value as an any.
   271  func (v Value) Any() any {
   272  	switch v.Kind() {
   273  	case KindAny:
   274  		if k, ok := v.any.(kind); ok {
   275  			return Kind(k)
   276  		}
   277  		return v.any
   278  	case KindLogValuer:
   279  		return v.any
   280  	case KindGroup:
   281  		return v.group()
   282  	case KindInt64:
   283  		return int64(v.num)
   284  	case KindUint64:
   285  		return v.num
   286  	case KindFloat64:
   287  		return v.float()
   288  	case KindString:
   289  		return v.str()
   290  	case KindBool:
   291  		return v.bool()
   292  	case KindDuration:
   293  		return v.duration()
   294  	case KindTime:
   295  		return v.time()
   296  	default:
   297  		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
   298  	}
   299  }
   300  
   301  // String returns Value's value as a string, formatted like [fmt.Sprint]. Unlike
   302  // the methods Int64, Float64, and so on, which panic if v is of the
   303  // wrong kind, String never panics.
   304  func (v Value) String() string {
   305  	if sp, ok := v.any.(stringptr); ok {
   306  		return unsafe.String(sp, v.num)
   307  	}
   308  	var buf []byte
   309  	return string(v.append(buf))
   310  }
   311  
   312  func (v Value) str() string {
   313  	return unsafe.String(v.any.(stringptr), v.num)
   314  }
   315  
   316  // Int64 returns v's value as an int64. It panics
   317  // if v is not a signed integer.
   318  func (v Value) Int64() int64 {
   319  	if g, w := v.Kind(), KindInt64; g != w {
   320  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   321  	}
   322  	return int64(v.num)
   323  }
   324  
   325  // Uint64 returns v's value as a uint64. It panics
   326  // if v is not an unsigned integer.
   327  func (v Value) Uint64() uint64 {
   328  	if g, w := v.Kind(), KindUint64; g != w {
   329  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   330  	}
   331  	return v.num
   332  }
   333  
   334  // Bool returns v's value as a bool. It panics
   335  // if v is not a bool.
   336  func (v Value) Bool() bool {
   337  	if g, w := v.Kind(), KindBool; g != w {
   338  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   339  	}
   340  	return v.bool()
   341  }
   342  
   343  func (v Value) bool() bool {
   344  	return v.num == 1
   345  }
   346  
   347  // Duration returns v's value as a [time.Duration]. It panics
   348  // if v is not a time.Duration.
   349  func (v Value) Duration() time.Duration {
   350  	if g, w := v.Kind(), KindDuration; g != w {
   351  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   352  	}
   353  
   354  	return v.duration()
   355  }
   356  
   357  func (v Value) duration() time.Duration {
   358  	return time.Duration(int64(v.num))
   359  }
   360  
   361  // Float64 returns v's value as a float64. It panics
   362  // if v is not a float64.
   363  func (v Value) Float64() float64 {
   364  	if g, w := v.Kind(), KindFloat64; g != w {
   365  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   366  	}
   367  
   368  	return v.float()
   369  }
   370  
   371  func (v Value) float() float64 {
   372  	return math.Float64frombits(v.num)
   373  }
   374  
   375  // Time returns v's value as a [time.Time]. It panics
   376  // if v is not a time.Time.
   377  func (v Value) Time() time.Time {
   378  	if g, w := v.Kind(), KindTime; g != w {
   379  		panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
   380  	}
   381  	return v.time()
   382  }
   383  
   384  // See TimeValue to understand how times are represented.
   385  func (v Value) time() time.Time {
   386  	switch a := v.any.(type) {
   387  	case timeLocation:
   388  		if a == nil {
   389  			return time.Time{}
   390  		}
   391  		return time.Unix(0, int64(v.num)).In(a)
   392  	case timeTime:
   393  		return time.Time(a)
   394  	default:
   395  		panic(fmt.Sprintf("bad time type %T", v.any))
   396  	}
   397  }
   398  
   399  // LogValuer returns v's value as a LogValuer. It panics
   400  // if v is not a LogValuer.
   401  func (v Value) LogValuer() LogValuer {
   402  	return v.any.(LogValuer)
   403  }
   404  
   405  // Group returns v's value as a []Attr.
   406  // It panics if v's [Kind] is not [KindGroup].
   407  func (v Value) Group() []Attr {
   408  	if sp, ok := v.any.(groupptr); ok {
   409  		return unsafe.Slice((*Attr)(sp), v.num)
   410  	}
   411  	panic("Group: bad kind")
   412  }
   413  
   414  func (v Value) group() []Attr {
   415  	return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num)
   416  }
   417  
   418  //////////////// Other
   419  
   420  // Equal reports whether v and w represent the same Go value.
   421  func (v Value) Equal(w Value) bool {
   422  	k1 := v.Kind()
   423  	k2 := w.Kind()
   424  	if k1 != k2 {
   425  		return false
   426  	}
   427  	switch k1 {
   428  	case KindInt64, KindUint64, KindBool, KindDuration:
   429  		return v.num == w.num
   430  	case KindString:
   431  		return v.str() == w.str()
   432  	case KindFloat64:
   433  		return v.float() == w.float()
   434  	case KindTime:
   435  		return v.time().Equal(w.time())
   436  	case KindAny, KindLogValuer:
   437  		return v.any == w.any // may panic if non-comparable
   438  	case KindGroup:
   439  		return slices.EqualFunc(v.group(), w.group(), Attr.Equal)
   440  	default:
   441  		panic(fmt.Sprintf("bad kind: %s", k1))
   442  	}
   443  }
   444  
   445  // isEmptyGroup reports whether v is a group that has no attributes.
   446  func (v Value) isEmptyGroup() bool {
   447  	if v.Kind() != KindGroup {
   448  		return false
   449  	}
   450  	// We do not need to recursively examine the group's Attrs for emptiness,
   451  	// because GroupValue removed them when the group was constructed, and
   452  	// groups are immutable.
   453  	return len(v.group()) == 0
   454  }
   455  
   456  // append appends a text representation of v to dst.
   457  // v is formatted as with fmt.Sprint.
   458  func (v Value) append(dst []byte) []byte {
   459  	switch v.Kind() {
   460  	case KindString:
   461  		return append(dst, v.str()...)
   462  	case KindInt64:
   463  		return strconv.AppendInt(dst, int64(v.num), 10)
   464  	case KindUint64:
   465  		return strconv.AppendUint(dst, v.num, 10)
   466  	case KindFloat64:
   467  		return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
   468  	case KindBool:
   469  		return strconv.AppendBool(dst, v.bool())
   470  	case KindDuration:
   471  		return append(dst, v.duration().String()...)
   472  	case KindTime:
   473  		return append(dst, v.time().String()...)
   474  	case KindGroup:
   475  		return fmt.Append(dst, v.group())
   476  	case KindAny, KindLogValuer:
   477  		return fmt.Append(dst, v.any)
   478  	default:
   479  		panic(fmt.Sprintf("bad kind: %s", v.Kind()))
   480  	}
   481  }
   482  
   483  // A LogValuer is any Go value that can convert itself into a Value for logging.
   484  //
   485  // This mechanism may be used to defer expensive operations until they are
   486  // needed, or to expand a single value into a sequence of components.
   487  type LogValuer interface {
   488  	LogValue() Value
   489  }
   490  
   491  const maxLogValues = 100
   492  
   493  // Resolve repeatedly calls LogValue on v while it implements [LogValuer],
   494  // and returns the result.
   495  // If v resolves to a group, the group's attributes' values are not recursively
   496  // resolved.
   497  // If the number of LogValue calls exceeds a threshold, a Value containing an
   498  // error is returned.
   499  // Resolve's return value is guaranteed not to be of Kind [KindLogValuer].
   500  func (v Value) Resolve() (rv Value) {
   501  	orig := v
   502  	defer func() {
   503  		if r := recover(); r != nil {
   504  			rv = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5)))
   505  		}
   506  	}()
   507  
   508  	for i := 0; i < maxLogValues; i++ {
   509  		if v.Kind() != KindLogValuer {
   510  			return v
   511  		}
   512  		v = v.LogValuer().LogValue()
   513  	}
   514  	err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any())
   515  	return AnyValue(err)
   516  }
   517  
   518  func stack(skip, nFrames int) string {
   519  	pcs := make([]uintptr, nFrames+1)
   520  	n := runtime.Callers(skip+1, pcs)
   521  	if n == 0 {
   522  		return "(no stack)"
   523  	}
   524  	frames := runtime.CallersFrames(pcs[:n])
   525  	var b strings.Builder
   526  	i := 0
   527  	for {
   528  		frame, more := frames.Next()
   529  		fmt.Fprintf(&b, "called from %s (%s:%d)\n", frame.Function, frame.File, frame.Line)
   530  		if !more {
   531  			break
   532  		}
   533  		i++
   534  		if i >= nFrames {
   535  			fmt.Fprintf(&b, "(rest of stack elided)\n")
   536  			break
   537  		}
   538  	}
   539  	return b.String()
   540  }
   541  

View as plain text