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 5package slog 6 7import ( 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. 21type 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 38type ( 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]. 44type 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 49const ( 50 KindAny Kind = iota 51 KindBool 52 KindDuration 53 KindFloat64 54 KindInt64 55 KindString 56 KindTime 57 KindUint64 58 KindGroup 59 KindLogValuer 60) 61 62var kindStrings = []string{ 63 "Any", 64 "Bool", 65 "Duration", 66 "Float64", 67 "Int64", 68 "String", 69 "Time", 70 "Uint64", 71 "Group", 72 "LogValuer", 73} 74 75func (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.) 84type kind Kind 85 86// Kind returns v's Kind. 87func (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. 109func 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. 114func IntValue(v int) Value { 115 return Int64Value(int64(v)) 116} 117 118// Int64Value returns a [Value] for an int64. 119func Int64Value(v int64) Value { 120 return Value{num: uint64(v), any: KindInt64} 121} 122 123// Uint64Value returns a [Value] for a uint64. 124func Uint64Value(v uint64) Value { 125 return Value{num: v, any: KindUint64} 126} 127 128// Float64Value returns a [Value] for a floating-point number. 129func Float64Value(v float64) Value { 130 return Value{num: math.Float64bits(v), any: KindFloat64} 131} 132 133// BoolValue returns a [Value] for a bool. 134func BoolValue(v bool) Value { 135 u := uint64(0) 136 if v { 137 u = 1 138 } 139 return Value{num: u, any: KindBool} 140} 141 142type ( 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. 153func 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]. 173func 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. 179func 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. 196func 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]. 221func 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. 271func (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. 304func (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 312func (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. 318func (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. 327func (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. 336func (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 343func (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. 349func (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 357func (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. 363func (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 371func (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. 377func (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. 385func (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. 401func (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]. 407func (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 414func (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. 421func (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. 446func (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. 458func (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. 487type LogValuer interface { 488 LogValue() Value 489} 490 491const 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]. 500func (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 518func 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