github.com/kaiiak/zaptext@v0.0.0-20220617014548-4ce28ef7833b/text_encoder.go (about) 1 package zaptext 2 3 import ( 4 "encoding/base64" 5 "math" 6 "sync" 7 "time" 8 "unicode/utf8" 9 10 "go.uber.org/zap/buffer" 11 "go.uber.org/zap/zapcore" 12 "golang.org/x/text/encoding" 13 ) 14 15 const ( 16 _hex = "0123456789abcdef" 17 ) 18 19 type ( 20 TextEncoder struct { 21 *zapcore.EncoderConfig 22 buf *buffer.Buffer 23 spaced bool 24 25 // for encoding generic values by reflection 26 reflectBuf *buffer.Buffer 27 reflectEnc *encoding.Encoder 28 } 29 ) 30 31 var ( 32 textpool = sync.Pool{New: func() interface{} { 33 return &TextEncoder{} 34 }} 35 buffpoll = buffer.NewPool() 36 ) 37 38 var _ zapcore.Encoder = (*TextEncoder)(nil) 39 var _ zapcore.ArrayEncoder = (*TextEncoder)(nil) 40 41 func NewTextEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder { 42 return &TextEncoder{EncoderConfig: &cfg, buf: buffpoll.Get()} 43 } 44 45 func (enc *TextEncoder) addKey(key string) { 46 enc.addElementSeparator() 47 if enc.spaced { 48 enc.buf.AppendByte(' ') 49 } 50 } 51 52 func (enc *TextEncoder) addElementSeparator() { 53 last := enc.buf.Len() - 1 54 if last < 0 { 55 return 56 } 57 switch enc.buf.Bytes()[last] { 58 case '{', '[', ':', ',', ' ': 59 return 60 default: 61 enc.buf.AppendByte(',') 62 if enc.spaced { 63 enc.buf.AppendByte(' ') 64 } 65 } 66 } 67 68 func (enc *TextEncoder) tryAddRuneError(r rune, size int) bool { 69 if r == utf8.RuneError && size == 1 { 70 enc.buf.AppendString(`\ufffd`) 71 return true 72 } 73 return false 74 } 75 76 // tryAddRuneSelf appends b if it is valid UTF-8 character represented in a single byte. 77 func (enc *TextEncoder) tryAddRuneSelf(b byte) bool { 78 if b >= utf8.RuneSelf { 79 return false 80 } 81 if 0x20 <= b && b != '\\' && b != '"' { 82 enc.buf.AppendByte(b) 83 return true 84 } 85 switch b { 86 case '\\', '"': 87 enc.buf.AppendByte('\\') 88 enc.buf.AppendByte(b) 89 case '\n': 90 enc.buf.AppendByte('\\') 91 enc.buf.AppendByte('n') 92 case '\r': 93 enc.buf.AppendByte('\\') 94 enc.buf.AppendByte('r') 95 case '\t': 96 enc.buf.AppendByte('\\') 97 enc.buf.AppendByte('t') 98 default: 99 // Encode bytes < 0x20, except for the escape sequences above. 100 enc.buf.AppendString(`\u00`) 101 enc.buf.AppendByte(_hex[b>>4]) 102 enc.buf.AppendByte(_hex[b&0xF]) 103 } 104 return true 105 } 106 107 func (enc *TextEncoder) appendFloat(val float64, bitSize int) { 108 enc.addElementSeparator() 109 switch { 110 case math.IsNaN(val): 111 enc.buf.AppendString(`"NaN"`) 112 case math.IsInf(val, 1): 113 enc.buf.AppendString(`"+Inf"`) 114 case math.IsInf(val, -1): 115 enc.buf.AppendString(`"-Inf"`) 116 default: 117 enc.buf.AppendFloat(val, bitSize) 118 } 119 } 120 121 // safeAddByteString is no-alloc equivalent of safeAddString(string(s)) for s []byte. 122 func (enc *TextEncoder) safeAddByteString(s []byte) { 123 for i := 0; i < len(s); { 124 if enc.tryAddRuneSelf(s[i]) { 125 i++ 126 continue 127 } 128 r, size := utf8.DecodeRune(s[i:]) 129 if enc.tryAddRuneError(r, size) { 130 i++ 131 continue 132 } 133 enc.buf.Write(s[i : i+size]) 134 i += size 135 } 136 } 137 138 // Clone copies the encoder, ensuring that adding fields to the copy doesn't 139 // affect the original. 140 func (enc *TextEncoder) Clone() zapcore.Encoder { return enc } 141 142 // EncodeEntry encodes an entry and fields, along with any accumulated 143 // context, into a byte buffer and returns ienc. Any fields that are empty, 144 // including fields on the `Entry` type, should be omitted. 145 func (enc *TextEncoder) EncodeEntry(zapcore.Entry, []zapcore.Field) (buf *buffer.Buffer, err error) { 146 return 147 } 148 149 // Logging-specific marshalers. 150 func (enc *TextEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) (err error) { return } 151 func (enc *TextEncoder) AddObject(key string, marshaler zapcore.ObjectMarshaler) (err error) { return } 152 153 func (enc *TextEncoder) AddComplex64(key string, value complex64) { 154 enc.AddComplex128(key, complex128(value)) 155 } 156 func (enc *TextEncoder) AddFloat32(key string, value float32) { 157 enc.AddFloat64(key, float64(value)) 158 } 159 func (enc *TextEncoder) AddInt(key string, value int) { 160 enc.AddInt64(key, int64(value)) 161 } 162 func (enc *TextEncoder) AddInt32(key string, value int32) { 163 enc.AddInt64(key, int64(value)) 164 } 165 func (enc *TextEncoder) AddInt16(key string, value int16) { 166 enc.AddInt64(key, int64(value)) 167 } 168 func (enc *TextEncoder) AddInt8(key string, value int8) { 169 enc.AddInt64(key, int64(value)) 170 } 171 func (enc *TextEncoder) AddUint32(key string, value uint32) { 172 enc.AddUint64(key, uint64(value)) 173 } 174 func (enc *TextEncoder) AddUint(key string, value uint) { 175 enc.AddUint64(key, uint64(value)) 176 } 177 func (enc *TextEncoder) AddUint16(key string, value uint16) { 178 enc.AddUint64(key, uint64(value)) 179 } 180 func (enc *TextEncoder) AddUint8(key string, value uint8) { 181 enc.AddUint64(key, uint64(value)) 182 } 183 func (enc *TextEncoder) AddUintptr(key string, value uintptr) { 184 enc.AddUint64(key, uint64(value)) 185 } 186 187 func (enc *TextEncoder) resetReflectBuf() { 188 if enc.reflectBuf == nil { 189 enc.reflectBuf = buffpoll.Get() 190 enc.reflectEnc = &encoding.Encoder{} 191 192 // For consistency with our custom JSON encoder. 193 // enc.reflectEnc.SetEscapeHTML(false) 194 } else { 195 enc.reflectBuf.Reset() 196 } 197 } 198 199 var nullLiteralBytes = []byte("null") 200 201 func (enc *TextEncoder) encodeReflected(obj interface{}) ([]byte, error) { 202 if obj == nil { 203 return nullLiteralBytes, nil 204 } 205 enc.resetReflectBuf() 206 return nil, nil 207 } 208 209 // AddReflected uses reflection to serialize arbitrary objects, so it can be 210 // slow and allocation-heavy. 211 func (enc *TextEncoder) AddReflected(key string, value interface{}) (err error) { 212 var valueBytes []byte 213 valueBytes, err = enc.encodeReflected(value) 214 if err != nil { 215 return err 216 } 217 enc.addKey(key) 218 _, err = enc.buf.Write(valueBytes) 219 return 220 } 221 222 // OpenNamespace opens an isolated namespace where all subsequent fields will 223 // be added. Applications can use namespaces to prevent key collisions when 224 // injecting loggers into sub-components or third-party libraries. 225 func (enc *TextEncoder) OpenNamespace(key string) {} 226 227 // Built-in types. 228 // for arbitrary bytes 229 func (enc *TextEncoder) AddBinary(key string, value []byte) { 230 enc.AddString(key, base64.StdEncoding.EncodeToString(value)) 231 } 232 func (enc *TextEncoder) AddDuration(key string, value time.Duration) { 233 cur := enc.buf.Len() 234 if e := enc.EncodeDuration; e != nil { 235 e(value, enc) 236 } 237 if cur == enc.buf.Len() { 238 enc.AppendInt64(int64(value)) 239 } 240 } 241 func (enc *TextEncoder) AddComplex128(key string, value complex128) { 242 enc.addElementSeparator() 243 // Cast to a platform-independent, fixed-size type. 244 r, i := float64(real(value)), float64(imag(value)) 245 enc.buf.AppendByte('"') 246 // Because we're always in a quoted string, we can use strconv without 247 // special-casing NaN and +/-Inf. 248 enc.buf.AppendFloat(r, 64) 249 enc.buf.AppendByte('+') 250 enc.buf.AppendFloat(i, 64) 251 enc.buf.AppendByte('i') 252 enc.buf.AppendByte('"') 253 } 254 func (enc *TextEncoder) AddByteString(key string, value []byte) { 255 enc.addKey(key) 256 enc.AppendByteString(value) 257 } 258 func (enc *TextEncoder) AddFloat64(key string, value float64) { 259 enc.addKey(key) 260 enc.appendFloat(value, 64) 261 } 262 func (enc *TextEncoder) AddTime(key string, value time.Time) { 263 enc.addKey(key) 264 enc.buf.AppendTime(value, time.RFC3339) 265 } 266 func (enc *TextEncoder) AddUint64(key string, value uint64) { 267 enc.addKey(key) 268 enc.buf.AppendUint(value) 269 } 270 func (enc *TextEncoder) AddInt64(key string, value int64) { 271 enc.addKey(key) 272 enc.buf.AppendInt(value) 273 } 274 func (enc *TextEncoder) AddBool(key string, value bool) { 275 enc.addKey(key) 276 enc.buf.AppendBool(value) 277 } 278 func (enc *TextEncoder) AddString(key, value string) { 279 enc.addKey(key) 280 enc.buf.AppendString(value) 281 } 282 283 // ArrayEncoder 284 285 // Time-related types. 286 func (enc *TextEncoder) AppendDuration(value time.Duration) { 287 cur := enc.buf.Len() 288 if e := enc.EncodeDuration; e != nil { 289 e(value, enc) 290 } 291 if cur == enc.buf.Len() { 292 // User-supplied EncodeDuration is a no-op. 293 enc.AppendString(value.String()) 294 } 295 } 296 297 func (enc *TextEncoder) AppendTime(value time.Time) { 298 cur := enc.buf.Len() 299 if e := enc.EncodeTime; e != nil { 300 e(value, enc) 301 } 302 if cur == enc.buf.Len() { 303 // User-supplied EncodeTime is a no-op. 304 enc.AppendString(value.Format(time.RFC3339)) 305 } 306 } 307 308 // Logging-specific marshalers.{} 309 func (enc *TextEncoder) AppendArray(arr zapcore.ArrayMarshaler) (err error) { 310 enc.addElementSeparator() 311 err = arr.MarshalLogArray(enc) 312 return 313 } 314 315 func (enc *TextEncoder) AppendObject(obj zapcore.ObjectMarshaler) (err error) { 316 enc.addElementSeparator() 317 err = obj.MarshalLogObject(enc) 318 return 319 } 320 321 // AppendReflected uses reflection to serialize arbitrary objects, so it's{} 322 // slow and allocation-heavy.{} 323 func (enc *TextEncoder) AppendReflected(value interface{}) (err error) { 324 // TODO 325 return 326 } 327 328 func (enc *TextEncoder) AppendBool(value bool) { 329 enc.addElementSeparator() 330 enc.buf.AppendBool(value) 331 } 332 333 // for UTF-8 encoded bytes 334 func (enc *TextEncoder) AppendByteString(value []byte) { 335 enc.addElementSeparator() 336 enc.buf.AppendByte('"') 337 enc.safeAddByteString(value) 338 enc.buf.AppendByte('"') 339 } 340 341 func (enc *TextEncoder) AppendComplex128(value complex128) { 342 enc.addElementSeparator() 343 // Cast to a platform-independent, fixed-size type. 344 r, i := float64(real(value)), float64(imag(value)) 345 enc.buf.AppendByte('"') 346 // Because we're always in a quoted string, we can use strconv without 347 // special-casing NaN and +/-Inf. 348 enc.buf.AppendFloat(r, 64) 349 enc.buf.AppendByte('+') 350 enc.buf.AppendFloat(i, 64) 351 enc.buf.AppendByte('i') 352 enc.buf.AppendByte('"') 353 } 354 355 func (enc *TextEncoder) AppendUint64(value uint64) { enc.buf.AppendUint(value) } 356 func (enc *TextEncoder) AppendString(value string) { enc.buf.AppendString(value) } 357 func (enc *TextEncoder) AppendInt64(value int64) { enc.buf.AppendInt(value) } 358 func (enc *TextEncoder) AppendFloat64(value float64) { enc.appendFloat(value, 64) } 359 func (enc *TextEncoder) AppendComplex64(value complex64) { enc.AppendComplex128(complex128(value)) } 360 func (enc *TextEncoder) AppendFloat32(value float32) { enc.AppendFloat64(float64(value)) } 361 func (enc *TextEncoder) AppendInt32(value int32) { enc.AppendInt64(int64(value)) } 362 func (enc *TextEncoder) AppendInt16(value int16) { enc.AppendInt64(int64(value)) } 363 func (enc *TextEncoder) AppendInt(value int) { enc.AppendInt64(int64(value)) } 364 func (enc *TextEncoder) AppendInt8(value int8) { enc.AppendInt64(int64(value)) } 365 func (enc *TextEncoder) AppendUint(value uint) { enc.AppendUint64(uint64(value)) } 366 func (enc *TextEncoder) AppendUint32(value uint32) { enc.AppendUint64(uint64(value)) } 367 func (enc *TextEncoder) AppendUint16(value uint16) { enc.AppendUint64(uint64(value)) } 368 func (enc *TextEncoder) AppendUint8(value uint8) { enc.AppendUint64(uint64(value)) } 369 func (enc *TextEncoder) AppendUintptr(value uintptr) { enc.AppendUint64(uint64(value)) }