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)) }