github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/log/slog/value.go (about) 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 }