github.com/AndrienkoAleksandr/go@v0.0.19/src/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 return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))} 168 } 169 170 // AnyValue returns a Value for the supplied value. 171 // 172 // If the supplied value is of type Value, it is returned 173 // unmodified. 174 // 175 // Given a value of one of Go's predeclared string, bool, or 176 // (non-complex) numeric types, AnyValue returns a Value of kind 177 // String, Bool, Uint64, Int64, or Float64. The width of the 178 // original numeric type is not preserved. 179 // 180 // Given a time.Time or time.Duration value, AnyValue returns a Value of kind 181 // KindTime or KindDuration. The monotonic time is not preserved. 182 // 183 // For nil, or values of all other types, including named types whose 184 // underlying type is numeric, AnyValue returns a value of kind KindAny. 185 func AnyValue(v any) Value { 186 switch v := v.(type) { 187 case string: 188 return StringValue(v) 189 case int: 190 return Int64Value(int64(v)) 191 case uint: 192 return Uint64Value(uint64(v)) 193 case int64: 194 return Int64Value(v) 195 case uint64: 196 return Uint64Value(v) 197 case bool: 198 return BoolValue(v) 199 case time.Duration: 200 return DurationValue(v) 201 case time.Time: 202 return TimeValue(v) 203 case uint8: 204 return Uint64Value(uint64(v)) 205 case uint16: 206 return Uint64Value(uint64(v)) 207 case uint32: 208 return Uint64Value(uint64(v)) 209 case uintptr: 210 return Uint64Value(uint64(v)) 211 case int8: 212 return Int64Value(int64(v)) 213 case int16: 214 return Int64Value(int64(v)) 215 case int32: 216 return Int64Value(int64(v)) 217 case float64: 218 return Float64Value(v) 219 case float32: 220 return Float64Value(float64(v)) 221 case []Attr: 222 return GroupValue(v...) 223 case Kind: 224 return Value{any: kind(v)} 225 case Value: 226 return v 227 default: 228 return Value{any: v} 229 } 230 } 231 232 //////////////// Accessors 233 234 // Any returns v's value as an any. 235 func (v Value) Any() any { 236 switch v.Kind() { 237 case KindAny: 238 if k, ok := v.any.(kind); ok { 239 return Kind(k) 240 } 241 return v.any 242 case KindLogValuer: 243 return v.any 244 case KindGroup: 245 return v.group() 246 case KindInt64: 247 return int64(v.num) 248 case KindUint64: 249 return v.num 250 case KindFloat64: 251 return v.float() 252 case KindString: 253 return v.str() 254 case KindBool: 255 return v.bool() 256 case KindDuration: 257 return v.duration() 258 case KindTime: 259 return v.time() 260 default: 261 panic(fmt.Sprintf("bad kind: %s", v.Kind())) 262 } 263 } 264 265 // String returns Value's value as a string, formatted like fmt.Sprint. Unlike 266 // the methods Int64, Float64, and so on, which panic if v is of the 267 // wrong kind, String never panics. 268 func (v Value) String() string { 269 if sp, ok := v.any.(stringptr); ok { 270 return unsafe.String(sp, v.num) 271 } 272 var buf []byte 273 return string(v.append(buf)) 274 } 275 276 func (v Value) str() string { 277 return unsafe.String(v.any.(stringptr), v.num) 278 } 279 280 // Int64 returns v's value as an int64. It panics 281 // if v is not a signed integer. 282 func (v Value) Int64() int64 { 283 if g, w := v.Kind(), KindInt64; g != w { 284 panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) 285 } 286 return int64(v.num) 287 } 288 289 // Uint64 returns v's value as a uint64. It panics 290 // if v is not an unsigned integer. 291 func (v Value) Uint64() uint64 { 292 if g, w := v.Kind(), KindUint64; g != w { 293 panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) 294 } 295 return v.num 296 } 297 298 // Bool returns v's value as a bool. It panics 299 // if v is not a bool. 300 func (v Value) Bool() bool { 301 if g, w := v.Kind(), KindBool; g != w { 302 panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) 303 } 304 return v.bool() 305 } 306 307 func (a Value) bool() bool { 308 return a.num == 1 309 } 310 311 // Duration returns v's value as a time.Duration. It panics 312 // if v is not a time.Duration. 313 func (a Value) Duration() time.Duration { 314 if g, w := a.Kind(), KindDuration; g != w { 315 panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) 316 } 317 318 return a.duration() 319 } 320 321 func (a Value) duration() time.Duration { 322 return time.Duration(int64(a.num)) 323 } 324 325 // Float64 returns v's value as a float64. It panics 326 // if v is not a float64. 327 func (v Value) Float64() float64 { 328 if g, w := v.Kind(), KindFloat64; g != w { 329 panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) 330 } 331 332 return v.float() 333 } 334 335 func (a Value) float() float64 { 336 return math.Float64frombits(a.num) 337 } 338 339 // Time returns v's value as a time.Time. It panics 340 // if v is not a time.Time. 341 func (v Value) Time() time.Time { 342 if g, w := v.Kind(), KindTime; g != w { 343 panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) 344 } 345 return v.time() 346 } 347 348 func (v Value) time() time.Time { 349 loc := v.any.(timeLocation) 350 if loc == nil { 351 return time.Time{} 352 } 353 return time.Unix(0, int64(v.num)).In(loc) 354 } 355 356 // LogValuer returns v's value as a LogValuer. It panics 357 // if v is not a LogValuer. 358 func (v Value) LogValuer() LogValuer { 359 return v.any.(LogValuer) 360 } 361 362 // Group returns v's value as a []Attr. 363 // It panics if v's Kind is not KindGroup. 364 func (v Value) Group() []Attr { 365 if sp, ok := v.any.(groupptr); ok { 366 return unsafe.Slice((*Attr)(sp), v.num) 367 } 368 panic("Group: bad kind") 369 } 370 371 func (v Value) group() []Attr { 372 return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num) 373 } 374 375 //////////////// Other 376 377 // Equal reports whether v and w represent the same Go value. 378 func (v Value) Equal(w Value) bool { 379 k1 := v.Kind() 380 k2 := w.Kind() 381 if k1 != k2 { 382 return false 383 } 384 switch k1 { 385 case KindInt64, KindUint64, KindBool, KindDuration: 386 return v.num == w.num 387 case KindString: 388 return v.str() == w.str() 389 case KindFloat64: 390 return v.float() == w.float() 391 case KindTime: 392 return v.time().Equal(w.time()) 393 case KindAny, KindLogValuer: 394 return v.any == w.any // may panic if non-comparable 395 case KindGroup: 396 return slices.EqualFunc(v.group(), w.group(), Attr.Equal) 397 default: 398 panic(fmt.Sprintf("bad kind: %s", k1)) 399 } 400 } 401 402 // append appends a text representation of v to dst. 403 // v is formatted as with fmt.Sprint. 404 func (v Value) append(dst []byte) []byte { 405 switch v.Kind() { 406 case KindString: 407 return append(dst, v.str()...) 408 case KindInt64: 409 return strconv.AppendInt(dst, int64(v.num), 10) 410 case KindUint64: 411 return strconv.AppendUint(dst, v.num, 10) 412 case KindFloat64: 413 return strconv.AppendFloat(dst, v.float(), 'g', -1, 64) 414 case KindBool: 415 return strconv.AppendBool(dst, v.bool()) 416 case KindDuration: 417 return append(dst, v.duration().String()...) 418 case KindTime: 419 return append(dst, v.time().String()...) 420 case KindGroup: 421 return fmt.Append(dst, v.group()) 422 case KindAny, KindLogValuer: 423 return fmt.Append(dst, v.any) 424 default: 425 panic(fmt.Sprintf("bad kind: %s", v.Kind())) 426 } 427 } 428 429 // A LogValuer is any Go value that can convert itself into a Value for logging. 430 // 431 // This mechanism may be used to defer expensive operations until they are 432 // needed, or to expand a single value into a sequence of components. 433 type LogValuer interface { 434 LogValue() Value 435 } 436 437 const maxLogValues = 100 438 439 // Resolve repeatedly calls LogValue on v while it implements LogValuer, 440 // and returns the result. 441 // If v resolves to a group, the group's attributes' values are not recursively 442 // resolved. 443 // If the number of LogValue calls exceeds a threshold, a Value containing an 444 // error is returned. 445 // Resolve's return value is guaranteed not to be of Kind KindLogValuer. 446 func (v Value) Resolve() (rv Value) { 447 orig := v 448 defer func() { 449 if r := recover(); r != nil { 450 rv = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5))) 451 } 452 }() 453 454 for i := 0; i < maxLogValues; i++ { 455 if v.Kind() != KindLogValuer { 456 return v 457 } 458 v = v.LogValuer().LogValue() 459 } 460 err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any()) 461 return AnyValue(err) 462 } 463 464 func stack(skip, nFrames int) string { 465 pcs := make([]uintptr, nFrames+1) 466 n := runtime.Callers(skip+1, pcs) 467 if n == 0 { 468 return "(no stack)" 469 } 470 frames := runtime.CallersFrames(pcs[:n]) 471 var b strings.Builder 472 i := 0 473 for { 474 frame, more := frames.Next() 475 fmt.Fprintf(&b, "called from %s (%s:%d)\n", frame.Function, frame.File, frame.Line) 476 if !more { 477 break 478 } 479 i++ 480 if i >= nFrames { 481 fmt.Fprintf(&b, "(rest of stack elided)\n") 482 break 483 } 484 } 485 return b.String() 486 }