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.
15 // Definitions for Value.
16 // The Value type itself can be found in value_{safe,unsafe}.go.
18 // Kind is the kind of a Value.
21 // The following list is sorted alphabetically, but it's also important that
22 // KindAny is 0 so that a zero Value represents nil.
37 var kindStrings = []string{
50 func (k Kind) String() string {
51 if k >= 0 && int(k) < len(kindStrings) {
54 return "<unknown slog.Kind>"
57 // Unexported version of Kind, just so we can store Kinds in Values.
58 // (No user-provided value has this type.)
61 //////////////// Constructors
63 // IntValue returns a Value for an int.
64 func IntValue(v int) Value {
65 return Int64Value(int64(v))
68 // Int64Value returns a Value for an int64.
69 func Int64Value(v int64) Value {
70 return Value{num: uint64(v), any: KindInt64}
73 // Uint64Value returns a Value for a uint64.
74 func Uint64Value(v uint64) Value {
75 return Value{num: v, any: KindUint64}
78 // Float64Value returns a Value for a floating-point number.
79 func Float64Value(v float64) Value {
80 return Value{num: math.Float64bits(v), any: KindFloat64}
83 // BoolValue returns a Value for a bool.
84 func BoolValue(v bool) Value {
89 return Value{num: u, any: KindBool}
92 // Unexported version of *time.Location, just so we can store *time.Locations in
93 // Values. (No user-provided value has this type.)
94 type timeLocation *time.Location
96 // TimeValue returns a Value for a time.Time.
97 // It discards the monotonic portion.
98 func TimeValue(v time.Time) Value {
100 // UnixNano on the zero time is undefined, so represent the zero time
101 // with a nil *time.Location instead. time.Time.Location method never
102 // returns nil, so a Value with any == timeLocation(nil) cannot be
103 // mistaken for any other Value, time.Time or otherwise.
104 return Value{any: timeLocation(nil)}
106 return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())}
109 // DurationValue returns a Value for a time.Duration.
110 func DurationValue(v time.Duration) Value {
111 return Value{num: uint64(v.Nanoseconds()), any: KindDuration}
114 // GroupValue returns a new Value for a list of Attrs.
115 // The caller must not subsequently mutate the argument slice.
116 func GroupValue(as ...Attr) Value {
117 return groupValue(as)
120 // AnyValue returns a Value for the supplied value.
122 // If the supplied value is of type Value, it is returned
125 // Given a value of one of Go's predeclared string, bool, or
126 // (non-complex) numeric types, AnyValue returns a Value of kind
127 // String, Bool, Uint64, Int64, or Float64. The width of the
128 // original numeric type is not preserved.
130 // Given a time.Time or time.Duration value, AnyValue returns a Value of kind
131 // KindTime or KindDuration. The monotonic time is not preserved.
133 // For nil, or values of all other types, including named types whose
134 // underlying type is numeric, AnyValue returns a value of kind KindAny.
135 func AnyValue(v any) Value {
136 switch v := v.(type) {
138 return StringValue(v)
140 return Int64Value(int64(v))
142 return Uint64Value(uint64(v))
146 return Uint64Value(v)
150 return DurationValue(v)
154 return Uint64Value(uint64(v))
156 return Uint64Value(uint64(v))
158 return Uint64Value(uint64(v))
160 return Uint64Value(uint64(v))
162 return Int64Value(int64(v))
164 return Int64Value(int64(v))
166 return Int64Value(int64(v))
168 return Float64Value(v)
170 return Float64Value(float64(v))
172 return GroupValue(v...)
174 return Value{any: kind(v)}
182 //////////////// Accessors
184 // Any returns v's value as an any.
185 func (v Value) Any() any {
188 if k, ok := v.any.(kind); ok {
195 return v.uncheckedGroup()
211 panic(fmt.Sprintf("bad kind: %s", v.Kind()))
215 // Int64 returns v's value as an int64. It panics
216 // if v is not a signed integer.
217 func (v Value) Int64() int64 {
218 if g, w := v.Kind(), KindInt64; g != w {
219 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
224 // Uint64 returns v's value as a uint64. It panics
225 // if v is not an unsigned integer.
226 func (v Value) Uint64() uint64 {
227 if g, w := v.Kind(), KindUint64; g != w {
228 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
233 // Bool returns v's value as a bool. It panics
234 // if v is not a bool.
235 func (v Value) Bool() bool {
236 if g, w := v.Kind(), KindBool; g != w {
237 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
242 func (a Value) bool() bool {
246 // Duration returns v's value as a time.Duration. It panics
247 // if v is not a time.Duration.
248 func (a Value) Duration() time.Duration {
249 if g, w := a.Kind(), KindDuration; g != w {
250 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
256 func (a Value) duration() time.Duration {
257 return time.Duration(int64(a.num))
260 // Float64 returns v's value as a float64. It panics
261 // if v is not a float64.
262 func (v Value) Float64() float64 {
263 if g, w := v.Kind(), KindFloat64; g != w {
264 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
270 func (a Value) float() float64 {
271 return math.Float64frombits(a.num)
274 // Time returns v's value as a time.Time. It panics
275 // if v is not a time.Time.
276 func (v Value) Time() time.Time {
277 if g, w := v.Kind(), KindTime; g != w {
278 panic(fmt.Sprintf("Value kind is %s, not %s", g, w))
283 func (v Value) time() time.Time {
284 loc := v.any.(timeLocation)
288 return time.Unix(0, int64(v.num)).In(loc)
291 // LogValuer returns v's value as a LogValuer. It panics
292 // if v is not a LogValuer.
293 func (v Value) LogValuer() LogValuer {
294 return v.any.(LogValuer)
297 // Group returns v's value as a []Attr.
298 // It panics if v's Kind is not KindGroup.
299 func (v Value) Group() []Attr {
303 //////////////// Other
305 // Equal reports whether v and w have equal keys and values.
306 func (v Value) Equal(w Value) bool {
313 case KindInt64, KindUint64, KindBool, KindDuration:
314 return v.num == w.num
316 return v.str() == w.str()
318 return v.float() == w.float()
320 return v.time().Equal(w.time())
321 case KindAny, KindLogValuer:
322 return v.any == w.any // may panic if non-comparable
324 return slices.EqualFunc(v.uncheckedGroup(), w.uncheckedGroup(), Attr.Equal)
326 panic(fmt.Sprintf("bad kind: %s", k1))
330 // append appends a text representation of v to dst.
331 // v is formatted as with fmt.Sprint.
332 func (v Value) append(dst []byte) []byte {
335 return append(dst, v.str()...)
337 return strconv.AppendInt(dst, int64(v.num), 10)
339 return strconv.AppendUint(dst, v.num, 10)
341 return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
343 return strconv.AppendBool(dst, v.bool())
345 return append(dst, v.duration().String()...)
347 return append(dst, v.time().String()...)
348 case KindAny, KindGroup, KindLogValuer:
349 return append(dst, fmt.Sprint(v.any)...)
351 panic(fmt.Sprintf("bad kind: %s", v.Kind()))
355 // A LogValuer is any Go value that can convert itself into a Value for logging.
357 // This mechanism may be used to defer expensive operations until they are
358 // needed, or to expand a single value into a sequence of components.
359 type LogValuer interface {
363 const maxLogValues = 100
365 // Resolve repeatedly calls LogValue on v while it implements LogValuer,
366 // and returns the result.
367 // If v resolves to a group, the group's attributes' values are also resolved.
368 // If the number of LogValue calls exceeds a threshold, a Value containing an
369 // error is returned.
370 // Resolve's return value is guaranteed not to be of Kind KindLogValuer.
371 func (v Value) Resolve() Value {
373 if v.Kind() == KindGroup {
374 resolveAttrs(v.Group())
379 func (v Value) resolve() Value {
381 for i := 0; i < maxLogValues; i++ {
382 if v.Kind() != KindLogValuer {
385 v = v.LogValuer().LogValue()
387 err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any())
391 // resolveAttrs replaces the values of the given Attrs with their
393 func resolveAttrs(as []Attr) {
394 for i, a := range as {
395 as[i].Value = a.Value.Resolve()