...

Source file src/go.uber.org/zap/zapcore/console_encoder.go

Documentation: go.uber.org/zap/zapcore

     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  	"go.uber.org/zap/buffer"
    27  	"go.uber.org/zap/internal/bufferpool"
    28  	"go.uber.org/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  }
   158  

View as plain text