github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/flogging/logging.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 "fmt" 11 "io" 12 "os" 13 "sync" 14 15 "github.com/hyperledger/fabric/common/flogging/fabenc" 16 zaplogfmt "github.com/sykesm/zap-logfmt" 17 "go.uber.org/zap" 18 "go.uber.org/zap/zapcore" 19 ) 20 21 // Config is used to provide dependencies to a Logging instance. 22 type Config struct { 23 // Format is the log record format specifier for the Logging instance. If the 24 // spec is the string "json", log records will be formatted as JSON. Any 25 // other string will be provided to the FormatEncoder. Please see 26 // fabenc.ParseFormat for details on the supported verbs. 27 // 28 // If Format is not provided, a default format that provides basic information will 29 // be used. 30 Format string 31 32 // LogSpec determines the log levels that are enabled for the logging system. The 33 // spec must be in a format that can be processed by ActivateSpec. 34 // 35 // If LogSpec is not provided, loggers will be enabled at the INFO level. 36 LogSpec string 37 38 // Writer is the sink for encoded and formatted log records. 39 // 40 // If a Writer is not provided, os.Stderr will be used as the log sink. 41 Writer io.Writer 42 } 43 44 // Logging maintains the state associated with the fabric logging system. It is 45 // intended to bridge between the legacy logging infrastructure built around 46 // go-logging and the structured, level logging provided by zap. 47 type Logging struct { 48 *LoggerLevels 49 50 mutex sync.RWMutex 51 encoding Encoding 52 encoderConfig zapcore.EncoderConfig 53 multiFormatter *fabenc.MultiFormatter 54 writer zapcore.WriteSyncer 55 observer Observer 56 } 57 58 // New creates a new logging system and initializes it with the provided 59 // configuration. 60 func New(c Config) (*Logging, error) { 61 encoderConfig := zap.NewProductionEncoderConfig() 62 encoderConfig.NameKey = "name" 63 64 l := &Logging{ 65 LoggerLevels: &LoggerLevels{ 66 defaultLevel: defaultLevel, 67 }, 68 encoderConfig: encoderConfig, 69 multiFormatter: fabenc.NewMultiFormatter(), 70 } 71 72 err := l.Apply(c) 73 if err != nil { 74 return nil, err 75 } 76 return l, nil 77 } 78 79 // Apply applies the provided configuration to the logging system. 80 func (l *Logging) Apply(c Config) error { 81 err := l.SetFormat(c.Format) 82 if err != nil { 83 return err 84 } 85 86 if c.LogSpec == "" { 87 c.LogSpec = os.Getenv("FABRIC_LOGGING_SPEC") 88 } 89 if c.LogSpec == "" { 90 c.LogSpec = defaultLevel.String() 91 } 92 93 err = l.LoggerLevels.ActivateSpec(c.LogSpec) 94 if err != nil { 95 return err 96 } 97 98 if c.Writer == nil { 99 c.Writer = os.Stderr 100 } 101 l.SetWriter(c.Writer) 102 103 return nil 104 } 105 106 // SetFormat updates how log records are formatted and encoded. Log entries 107 // created after this method has completed will use the new format. 108 // 109 // An error is returned if the log format specification cannot be parsed. 110 func (l *Logging) SetFormat(format string) error { 111 l.mutex.Lock() 112 defer l.mutex.Unlock() 113 if format == "" { 114 format = defaultFormat 115 } 116 117 if format == "json" { 118 l.encoding = JSON 119 return nil 120 } 121 122 if format == "logfmt" { 123 l.encoding = LOGFMT 124 return nil 125 } 126 127 formatters, err := fabenc.ParseFormat(format) 128 if err != nil { 129 return err 130 } 131 l.multiFormatter.SetFormatters(formatters) 132 l.encoding = CONSOLE 133 134 return nil 135 } 136 137 // SetWriter controls which writer formatted log records are written to. 138 // Writers, with the exception of an *os.File, need to be safe for concurrent 139 // use by multiple go routines. 140 func (l *Logging) SetWriter(w io.Writer) io.Writer { 141 var sw zapcore.WriteSyncer 142 switch t := w.(type) { 143 case *os.File: 144 sw = zapcore.Lock(t) 145 case zapcore.WriteSyncer: 146 sw = t 147 default: 148 sw = zapcore.AddSync(w) 149 } 150 151 l.mutex.Lock() 152 ow := l.writer 153 l.writer = sw 154 l.mutex.Unlock() 155 156 return ow 157 } 158 159 // SetObserver is used to provide a log observer that will be called as log 160 // levels are checked or written.. Only a single observer is supported. 161 func (l *Logging) SetObserver(observer Observer) Observer { 162 l.mutex.Lock() 163 so := l.observer 164 l.observer = observer 165 l.mutex.Unlock() 166 167 return so 168 } 169 170 // Write satisfies the io.Write contract. It delegates to the writer argument 171 // of SetWriter or the Writer field of Config. The Core uses this when encoding 172 // log records. 173 func (l *Logging) Write(b []byte) (int, error) { 174 l.mutex.RLock() 175 w := l.writer 176 l.mutex.RUnlock() 177 178 return w.Write(b) 179 } 180 181 // Sync satisfies the zapcore.WriteSyncer interface. It is used by the Core to 182 // flush log records before terminating the process. 183 func (l *Logging) Sync() error { 184 l.mutex.RLock() 185 w := l.writer 186 l.mutex.RUnlock() 187 188 return w.Sync() 189 } 190 191 // Encoding satisfies the Encoding interface. It determines whether the JSON or 192 // CONSOLE encoder should be used by the Core when log records are written. 193 func (l *Logging) Encoding() Encoding { 194 l.mutex.RLock() 195 e := l.encoding 196 l.mutex.RUnlock() 197 return e 198 } 199 200 // ZapLogger instantiates a new zap.Logger with the specified name. The name is 201 // used to determine which log levels are enabled. 202 func (l *Logging) ZapLogger(name string) *zap.Logger { 203 if !isValidLoggerName(name) { 204 panic(fmt.Sprintf("invalid logger name: %s", name)) 205 } 206 207 l.mutex.RLock() 208 core := &Core{ 209 LevelEnabler: l.LoggerLevels, 210 Levels: l.LoggerLevels, 211 Encoders: map[Encoding]zapcore.Encoder{ 212 JSON: zapcore.NewJSONEncoder(l.encoderConfig), 213 CONSOLE: fabenc.NewFormatEncoder(l.multiFormatter), 214 LOGFMT: zaplogfmt.NewEncoder(l.encoderConfig), 215 }, 216 Selector: l, 217 Output: l, 218 Observer: l, 219 } 220 l.mutex.RUnlock() 221 222 return NewZapLogger(core).Named(name) 223 } 224 225 func (l *Logging) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) { 226 l.mutex.RLock() 227 observer := l.observer 228 l.mutex.RUnlock() 229 230 if observer != nil { 231 observer.Check(e, ce) 232 } 233 } 234 235 func (l *Logging) WriteEntry(e zapcore.Entry, fields []zapcore.Field) { 236 l.mutex.RLock() 237 observer := l.observer 238 l.mutex.RUnlock() 239 240 if observer != nil { 241 observer.WriteEntry(e, fields) 242 } 243 } 244 245 // Logger instantiates a new FabricLogger with the specified name. The name is 246 // used to determine which log levels are enabled. 247 func (l *Logging) Logger(name string) *FabricLogger { 248 zl := l.ZapLogger(name) 249 return NewFabricLogger(zl) 250 }