github.com/Laisky/zap@v1.27.0/zapcore/console_encoder.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package zapcore 22 23 import ( 24 "fmt" 25 26 "github.com/Laisky/zap/buffer" 27 "github.com/Laisky/zap/internal/bufferpool" 28 "github.com/Laisky/zap/internal/pool" 29 ) 30 31 var _sliceEncoderPool = pool.New(func() *sliceArrayEncoder { 32 return &sliceArrayEncoder{ 33 elems: make([]interface{}, 0, 2), 34 } 35 }) 36 37 func getSliceEncoder() *sliceArrayEncoder { 38 return _sliceEncoderPool.Get() 39 } 40 41 func putSliceEncoder(e *sliceArrayEncoder) { 42 e.elems = e.elems[:0] 43 _sliceEncoderPool.Put(e) 44 } 45 46 type consoleEncoder struct { 47 *jsonEncoder 48 } 49 50 // NewConsoleEncoder creates an encoder whose output is designed for human - 51 // rather than machine - consumption. It serializes the core log entry data 52 // (message, level, timestamp, etc.) in a plain-text format and leaves the 53 // structured context as JSON. 54 // 55 // Note that although the console encoder doesn't use the keys specified in the 56 // encoder configuration, it will omit any element whose key is set to the empty 57 // string. 58 func NewConsoleEncoder(cfg EncoderConfig) Encoder { 59 if cfg.ConsoleSeparator == "" { 60 // Use a default delimiter of '\t' for backwards compatibility 61 cfg.ConsoleSeparator = "\t" 62 } 63 return consoleEncoder{newJSONEncoder(cfg, true)} 64 } 65 66 func (c consoleEncoder) Clone() Encoder { 67 return consoleEncoder{c.jsonEncoder.Clone().(*jsonEncoder)} 68 } 69 70 func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, error) { 71 line := bufferpool.Get() 72 73 // We don't want the entry's metadata to be quoted and escaped (if it's 74 // encoded as strings), which means that we can't use the JSON encoder. The 75 // simplest option is to use the memory encoder and fmt.Fprint. 76 // 77 // If this ever becomes a performance bottleneck, we can implement 78 // ArrayEncoder for our plain-text format. 79 arr := getSliceEncoder() 80 if c.TimeKey != "" && c.EncodeTime != nil && !ent.Time.IsZero() { 81 c.EncodeTime(ent.Time, arr) 82 } 83 if c.LevelKey != "" && c.EncodeLevel != nil { 84 c.EncodeLevel(ent.Level, arr) 85 } 86 if ent.LoggerName != "" && c.NameKey != "" { 87 nameEncoder := c.EncodeName 88 89 if nameEncoder == nil { 90 // Fall back to FullNameEncoder for backward compatibility. 91 nameEncoder = FullNameEncoder 92 } 93 94 nameEncoder(ent.LoggerName, arr) 95 } 96 if ent.Caller.Defined { 97 if c.CallerKey != "" && c.EncodeCaller != nil { 98 c.EncodeCaller(ent.Caller, arr) 99 } 100 if c.FunctionKey != "" { 101 arr.AppendString(ent.Caller.Function) 102 } 103 } 104 for i := range arr.elems { 105 if i > 0 { 106 line.AppendString(c.ConsoleSeparator) 107 } 108 fmt.Fprint(line, arr.elems[i]) 109 } 110 putSliceEncoder(arr) 111 112 // Add the message itself. 113 if c.MessageKey != "" { 114 c.addSeparatorIfNecessary(line) 115 line.AppendString(ent.Message) 116 } 117 118 // Add any structured context. 119 c.writeContext(line, fields) 120 121 // If there's no stacktrace key, honor that; this allows users to force 122 // single-line output. 123 if ent.Stack != "" && c.StacktraceKey != "" { 124 line.AppendByte('\n') 125 line.AppendString(ent.Stack) 126 } 127 128 line.AppendString(c.LineEnding) 129 return line, nil 130 } 131 132 func (c consoleEncoder) writeContext(line *buffer.Buffer, extra []Field) { 133 context := c.jsonEncoder.Clone().(*jsonEncoder) 134 defer func() { 135 // putJSONEncoder assumes the buffer is still used, but we write out the buffer so 136 // we can free it. 137 context.buf.Free() 138 putJSONEncoder(context) 139 }() 140 141 addFields(context, extra) 142 context.closeOpenNamespaces() 143 if context.buf.Len() == 0 { 144 return 145 } 146 147 c.addSeparatorIfNecessary(line) 148 line.AppendByte('{') 149 line.Write(context.buf.Bytes()) 150 line.AppendByte('}') 151 } 152 153 func (c consoleEncoder) addSeparatorIfNecessary(line *buffer.Buffer) { 154 if line.Len() > 0 { 155 line.AppendString(c.ConsoleSeparator) 156 } 157 }