github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/flogging/core.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package flogging
     8  
     9  import (
    10  	"go.uber.org/zap/zapcore"
    11  )
    12  
    13  type Encoding int8
    14  
    15  const (
    16  	CONSOLE = iota
    17  	JSON
    18  	LOGFMT
    19  )
    20  
    21  // EncodingSelector is used to determine whether log records are
    22  // encoded as JSON or in human readable CONSOLE or LOGFMT formats.
    23  type EncodingSelector interface {
    24  	Encoding() Encoding
    25  }
    26  
    27  // Core is a custom implementation of a zapcore.Core. It's a terrible hack that
    28  // only exists to work around the intersection of state associated with
    29  // encoders, implementation hiding in zapcore, and implicit, ad-hoc logger
    30  // initialization within fabric.
    31  //
    32  // In addition to encoding log entries and fields to a buffer, zap Encoder
    33  // implementations also need to maintain field state. When zapcore.Core.With is
    34  // used, the associated encoder is cloned and the fields are added to the
    35  // encoder. This means that encoder instances cannot be shared across cores.
    36  //
    37  // In terms of implementation hiding, it's difficult for our FormatEncoder to
    38  // cleanly wrap the JSON and console implementations from zap as all methods
    39  // from the zapcore.ObjectEncoder would need to be implemented to delegate to
    40  // the correct backend.
    41  //
    42  // This implementation works by associating multiple encoders with a core. When
    43  // fields are added to the core, the fields are added to all of the encoder
    44  // implementations. The core also references the logging configuration to
    45  // determine the proper encoding to use, the writer to delegate to, and the
    46  // enabled levels.
    47  type Core struct {
    48  	zapcore.LevelEnabler
    49  	Levels   *LoggerLevels
    50  	Encoders map[Encoding]zapcore.Encoder
    51  	Selector EncodingSelector
    52  	Output   zapcore.WriteSyncer
    53  	Observer Observer
    54  }
    55  
    56  //go:generate counterfeiter -o mock/observer.go -fake-name Observer . Observer
    57  
    58  type Observer interface {
    59  	Check(e zapcore.Entry, ce *zapcore.CheckedEntry)
    60  	WriteEntry(e zapcore.Entry, fields []zapcore.Field)
    61  }
    62  
    63  func (c *Core) With(fields []zapcore.Field) zapcore.Core {
    64  	clones := map[Encoding]zapcore.Encoder{}
    65  	for name, enc := range c.Encoders {
    66  		clone := enc.Clone()
    67  		addFields(clone, fields)
    68  		clones[name] = clone
    69  	}
    70  
    71  	return &Core{
    72  		LevelEnabler: c.LevelEnabler,
    73  		Levels:       c.Levels,
    74  		Encoders:     clones,
    75  		Selector:     c.Selector,
    76  		Output:       c.Output,
    77  		Observer:     c.Observer,
    78  	}
    79  }
    80  
    81  func (c *Core) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
    82  	if c.Observer != nil {
    83  		c.Observer.Check(e, ce)
    84  	}
    85  
    86  	if c.Enabled(e.Level) && c.Levels.Level(e.LoggerName).Enabled(e.Level) {
    87  		return ce.AddCore(e, c)
    88  	}
    89  	return ce
    90  }
    91  
    92  func (c *Core) Write(e zapcore.Entry, fields []zapcore.Field) error {
    93  	encoding := c.Selector.Encoding()
    94  	enc := c.Encoders[encoding]
    95  
    96  	buf, err := enc.EncodeEntry(e, fields)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	_, err = c.Output.Write(buf.Bytes())
   101  	buf.Free()
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	if e.Level >= zapcore.PanicLevel {
   107  		c.Sync()
   108  	}
   109  
   110  	if c.Observer != nil {
   111  		c.Observer.WriteEntry(e, fields)
   112  	}
   113  
   114  	return nil
   115  }
   116  
   117  func (c *Core) Sync() error {
   118  	return c.Output.Sync()
   119  }
   120  
   121  func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
   122  	for i := range fields {
   123  		fields[i].AddTo(enc)
   124  	}
   125  }