github.com/searKing/golang/go@v1.2.117/log/slog/text_handler.go (about) 1 // Copyright 2023 The searKing Author. 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 "encoding" 9 "encoding/json" 10 "fmt" 11 "log/slog" 12 "reflect" 13 "strconv" 14 "unicode" 15 "unicode/utf8" 16 17 "github.com/searKing/golang/go/unsafe" 18 ) 19 20 func appendTextValue(s *handleState, v slog.Value) error { 21 switch v.Kind() { 22 case slog.KindString: 23 s.appendStringMayQuote(v.String()) 24 case slog.KindTime: 25 s.appendTime(v.Time()) 26 case slog.KindAny: 27 a := v.Any() 28 if tm, ok := a.(encoding.TextMarshaler); ok { 29 data, err := tm.MarshalText() 30 if err != nil { 31 return err 32 } 33 s.appendBytesMayQuote(data) 34 return nil 35 } 36 if err, ok := a.(error); ok && err != nil { 37 s.appendStringMayQuote(err.Error()) 38 return nil 39 } 40 if tm, ok := a.(fmt.Stringer); ok { 41 s.appendStringMayQuote(tm.String()) 42 return nil 43 } 44 if tm, ok := a.(json.Marshaler); ok { 45 data, err := tm.MarshalJSON() 46 if err != nil { 47 return err 48 } 49 s.appendBytesMayQuote(data) 50 return nil 51 } 52 if bs, ok := byteSlice(a); ok { 53 // As of Go 1.19, this only allocates for strings longer than 32 bytes. 54 s.buf.WriteString(strconv.Quote(unsafe.BytesToString(bs))) 55 return nil 56 } 57 s.appendStringMayQuote(fmt.Sprintf("%+v", v.Any())) 58 default: 59 s.appendStringMayQuote(v.String()) 60 } 61 return nil 62 } 63 64 // byteSlice returns its argument as a []byte if the argument's 65 // underlying type is []byte, along with a second return value of true. 66 // Otherwise it returns nil, false. 67 func byteSlice(a any) ([]byte, bool) { 68 if bs, ok := a.([]byte); ok { 69 return bs, true 70 } 71 // Like Printf's %s, we allow both the slice type and the byte element type to be named. 72 t := reflect.TypeOf(a) 73 if t != nil && t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { 74 return reflect.ValueOf(a).Bytes(), true 75 } 76 return nil, false 77 } 78 79 func needsQuoting(s string, unprintableOnly bool) bool { 80 if len(s) == 0 { 81 return true 82 } 83 for i := 0; i < len(s); { 84 b := s[i] 85 if b < utf8.RuneSelf { 86 // Quote anything except a backslash that would need quoting in a 87 // JSON string, as well as space and '=' 88 if unprintableOnly { 89 if !safeSet[b] { 90 return true 91 } 92 } else { 93 if b != '\\' && (b == ' ' || b == '=' || !safeSet[b]) { 94 return true 95 } 96 } 97 i++ 98 continue 99 } 100 r, size := utf8.DecodeRuneInString(s[i:]) 101 if unprintableOnly { 102 if r == utf8.RuneError || !unicode.IsPrint(r) { 103 return true 104 } 105 } else { 106 if r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r) { 107 return true 108 } 109 } 110 i += size 111 } 112 return false 113 }