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 }