github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/common/flogging/zap_test.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package flogging_test 8 9 import ( 10 "bytes" 11 "errors" 12 "io/ioutil" 13 "testing" 14 15 "github.com/hyperledger/fabric/common/flogging" 16 "github.com/hyperledger/fabric/common/flogging/fabenc" 17 "github.com/hyperledger/fabric/common/flogging/mock" 18 "github.com/stretchr/testify/assert" 19 "go.uber.org/zap" 20 "go.uber.org/zap/zapcore" 21 "go.uber.org/zap/zaptest/observer" 22 "google.golang.org/grpc/grpclog" 23 ) 24 25 func TestFabricLoggerEncoding(t *testing.T) { 26 formatters, err := fabenc.ParseFormat("%{color}[%{module}] %{shortfunc} -> %{level:.4s}%{color:reset} %{message}") 27 assert.NoError(t, err) 28 enc := fabenc.NewFormatEncoder(formatters...) 29 30 buf := &bytes.Buffer{} 31 core := zapcore.NewCore(enc, zapcore.AddSync(buf), zap.NewAtomicLevel()) 32 zl := flogging.NewZapLogger(core).Named("test").With(zap.String("extra", "field")) 33 fl := flogging.NewFabricLogger(zl) 34 35 buf.Reset() 36 fl.Info("string value", 0, 1.23, struct{}{}) 37 assert.Equal(t, "\x1b[34m[test] TestFabricLoggerEncoding -> INFO\x1b[0m string value 0 1.23 {} extra=field\n", buf.String()) 38 39 buf.Reset() 40 fl.Infof("string %s, %d, %.3f, %v", "strval", 0, 1.23, struct{}{}) 41 assert.Equal(t, "\x1b[34m[test] TestFabricLoggerEncoding -> INFO\x1b[0m string strval, 0, 1.230, {} extra=field\n", buf.String()) 42 43 buf.Reset() 44 fl.Infow("this is a message", "int", 0, "float", 1.23, "struct", struct{}{}) 45 assert.Equal(t, "\x1b[34m[test] TestFabricLoggerEncoding -> INFO\x1b[0m this is a message extra=field int=0 float=1.23 struct={}\n", buf.String()) 46 } 47 48 func TestFabricLogger(t *testing.T) { 49 var enabler zap.LevelEnablerFunc = func(l zapcore.Level) bool { return true } 50 51 var tests = []struct { 52 desc string 53 f func(fl *flogging.FabricLogger) 54 level zapcore.Level 55 message string 56 fields []zapcore.Field 57 panics bool 58 }{ 59 { 60 desc: "DPanic", 61 f: func(fl *flogging.FabricLogger) { fl.DPanic("arg1", "arg2") }, 62 level: zapcore.DPanicLevel, 63 message: "arg1 arg2", 64 fields: []zapcore.Field{}, 65 }, 66 { 67 desc: "DPanicf", 68 f: func(fl *flogging.FabricLogger) { fl.DPanicf("panic: %s, %d", "reason", 99) }, 69 level: zapcore.DPanicLevel, 70 message: "panic: reason, 99", 71 fields: []zapcore.Field{}, 72 }, 73 { 74 desc: "DPanicw", 75 f: func(fl *flogging.FabricLogger) { fl.DPanicw("I'm in a panic", "reason", "something", "code", 99) }, 76 level: zapcore.DPanicLevel, 77 message: "I'm in a panic", 78 fields: []zapcore.Field{zap.String("reason", "something"), zap.Int("code", 99)}, 79 }, 80 { 81 desc: "Debug", 82 f: func(fl *flogging.FabricLogger) { fl.Debug("arg1", "arg2") }, 83 level: zapcore.DebugLevel, 84 message: "arg1 arg2", 85 fields: []zapcore.Field{}, 86 }, 87 { 88 desc: "Debugf", 89 f: func(fl *flogging.FabricLogger) { fl.Debugf("debug: %s, %d", "goo", 99) }, 90 level: zapcore.DebugLevel, 91 message: "debug: goo, 99", 92 fields: []zapcore.Field{}, 93 }, 94 { 95 desc: "Debugw", 96 f: func(fl *flogging.FabricLogger) { fl.Debugw("debug data", "key", "value") }, 97 level: zapcore.DebugLevel, 98 message: "debug data", 99 fields: []zapcore.Field{zap.String("key", "value")}, 100 }, 101 { 102 desc: "Error", 103 f: func(fl *flogging.FabricLogger) { fl.Error("oh noes", errors.New("bananas")) }, 104 level: zapcore.ErrorLevel, 105 message: "oh noes bananas", 106 fields: []zapcore.Field{}, 107 }, 108 { 109 desc: "Errorf", 110 f: func(fl *flogging.FabricLogger) { fl.Errorf("error: %s", errors.New("bananas")) }, 111 level: zapcore.ErrorLevel, 112 message: "error: bananas", 113 fields: []zapcore.Field{}, 114 }, 115 { 116 desc: "Errorw", 117 f: func(fl *flogging.FabricLogger) { fl.Errorw("something failed", "err", errors.New("bananas")) }, 118 level: zapcore.ErrorLevel, 119 message: "something failed", 120 fields: []zapcore.Field{zap.NamedError("err", errors.New("bananas"))}, 121 }, 122 { 123 desc: "Info", 124 f: func(fl *flogging.FabricLogger) { fl.Info("fyi", "things are great") }, 125 level: zapcore.InfoLevel, 126 message: "fyi things are great", 127 fields: []zapcore.Field{}, 128 }, 129 { 130 desc: "Infof", 131 f: func(fl *flogging.FabricLogger) { fl.Infof("fyi: %s", "things are great") }, 132 level: zapcore.InfoLevel, 133 message: "fyi: things are great", 134 fields: []zapcore.Field{}, 135 }, 136 { 137 desc: "Infow", 138 f: func(fl *flogging.FabricLogger) { fl.Infow("fyi", "fish", "are smelly", "fruit", "is sweet") }, 139 level: zapcore.InfoLevel, 140 message: "fyi", 141 fields: []zapcore.Field{zap.String("fish", "are smelly"), zap.String("fruit", "is sweet")}, 142 }, 143 { 144 desc: "Panic", 145 f: func(fl *flogging.FabricLogger) { fl.Panic("oh noes", errors.New("platypus")) }, 146 level: zapcore.PanicLevel, 147 message: "oh noes platypus", 148 fields: []zapcore.Field{}, 149 panics: true, 150 }, 151 { 152 desc: "Panicf", 153 f: func(fl *flogging.FabricLogger) { fl.Panicf("error: %s", errors.New("platypus")) }, 154 level: zapcore.PanicLevel, 155 message: "error: platypus", 156 fields: []zapcore.Field{}, 157 panics: true, 158 }, 159 { 160 desc: "Panicw", 161 f: func(fl *flogging.FabricLogger) { fl.Panicw("something failed", "err", errors.New("platypus")) }, 162 level: zapcore.PanicLevel, 163 message: "something failed", 164 fields: []zapcore.Field{zap.NamedError("err", errors.New("platypus"))}, 165 panics: true, 166 }, 167 { 168 desc: "Warn", 169 f: func(fl *flogging.FabricLogger) { fl.Warn("oh noes", errors.New("monkeys")) }, 170 level: zapcore.WarnLevel, 171 message: "oh noes monkeys", 172 fields: []zapcore.Field{}, 173 }, 174 { 175 desc: "Warnf", 176 f: func(fl *flogging.FabricLogger) { fl.Warnf("error: %s", errors.New("monkeys")) }, 177 level: zapcore.WarnLevel, 178 message: "error: monkeys", 179 fields: []zapcore.Field{}, 180 }, 181 { 182 desc: "Warnw", 183 f: func(fl *flogging.FabricLogger) { fl.Warnw("something is weird", "err", errors.New("monkeys")) }, 184 level: zapcore.WarnLevel, 185 message: "something is weird", 186 fields: []zapcore.Field{zap.NamedError("err", errors.New("monkeys"))}, 187 }, 188 { 189 desc: "Warning", 190 f: func(fl *flogging.FabricLogger) { fl.Warning("oh noes", errors.New("monkeys")) }, 191 level: zapcore.WarnLevel, 192 message: "oh noes monkeys", 193 fields: []zapcore.Field{}, 194 }, 195 { 196 desc: "Warningf", 197 f: func(fl *flogging.FabricLogger) { fl.Warningf("error: %s", errors.New("monkeys")) }, 198 level: zapcore.WarnLevel, 199 message: "error: monkeys", 200 fields: []zapcore.Field{}, 201 }, 202 { 203 desc: "With", 204 f: func(fl *flogging.FabricLogger) { fl.With("key", "value").Debug("cool messages", "and stuff") }, 205 level: zapcore.DebugLevel, 206 message: "cool messages and stuff", 207 fields: []zapcore.Field{zap.String("key", "value")}, 208 }, 209 { 210 desc: "WithOptions", 211 f: func(fl *flogging.FabricLogger) { 212 fl.WithOptions(zap.Fields(zap.String("optionkey", "optionvalue"))).Debug("cool messages", "and stuff") 213 }, 214 level: zapcore.DebugLevel, 215 message: "cool messages and stuff", 216 fields: []zapcore.Field{zap.String("optionkey", "optionvalue")}, 217 }, 218 { 219 desc: "Critical", 220 f: func(fl *flogging.FabricLogger) { fl.Critical("critical as error", errors.New("kiwi")) }, 221 level: zapcore.ErrorLevel, 222 message: "critical as error kiwi", 223 fields: []zapcore.Field{}, 224 }, 225 { 226 desc: "Criticalf", 227 f: func(fl *flogging.FabricLogger) { fl.Criticalf("critical: %s", errors.New("kiwi")) }, 228 level: zapcore.ErrorLevel, 229 message: "critical: kiwi", 230 fields: []zapcore.Field{}, 231 }, 232 { 233 desc: "Notice", 234 f: func(fl *flogging.FabricLogger) { fl.Notice("notice", "as info") }, 235 level: zapcore.InfoLevel, 236 message: "notice as info", 237 fields: []zapcore.Field{}, 238 }, 239 { 240 desc: "Noticef", 241 f: func(fl *flogging.FabricLogger) { fl.Noticef("notice: %s", "this is info") }, 242 level: zapcore.InfoLevel, 243 message: "notice: this is info", 244 fields: []zapcore.Field{}, 245 }, 246 } 247 248 for _, tc := range tests { 249 t.Run(tc.desc, func(t *testing.T) { 250 core, logs := observer.New(enabler) 251 fl := flogging.NewFabricLogger(zap.New(core)).Named("lname") 252 253 if tc.panics { 254 assert.Panics(t, func() { tc.f(fl) }) 255 } else { 256 tc.f(fl) 257 } 258 259 err := fl.Sync() 260 assert.NoError(t, err) 261 262 entries := logs.All() 263 assert.Len(t, entries, 1) 264 entry := entries[0] 265 266 assert.Equal(t, tc.level, entry.Level) 267 assert.Equal(t, tc.message, entry.Message) 268 assert.Equal(t, tc.fields, entry.Context) 269 assert.Equal(t, "lname", entry.LoggerName) 270 }) 271 } 272 } 273 274 func TestIsEnabledFor(t *testing.T) { 275 formatters, err := fabenc.ParseFormat("%{color}[%{module}] %{shortfunc} -> %{level:.4s}%{color:reset} %{message}") 276 assert.NoError(t, err) 277 enc := fabenc.NewFormatEncoder(formatters...) 278 279 enablerCallCount := 0 280 enabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool { 281 enablerCallCount++ 282 return l == zapcore.ErrorLevel 283 }) 284 285 core := zapcore.NewCore(enc, zapcore.AddSync(ioutil.Discard), enabler) 286 zl := zap.New(core).Named("test") 287 fl := flogging.NewFabricLogger(zl) 288 289 assert.True(t, fl.IsEnabledFor(zapcore.ErrorLevel)) 290 assert.False(t, fl.IsEnabledFor(zapcore.PanicLevel)) 291 assert.Equal(t, 2, enablerCallCount) 292 } 293 294 func logCaller(l grpclog.Logger, msg string) { l.Println(msg) } 295 func callWrapper(l grpclog.Logger, msg string) { logCaller(l, msg) } 296 297 func TestGRPCLogger(t *testing.T) { 298 // ensure it includes the name as module, logs at debug level, and the caller with appropriate skip level 299 formatters, err := fabenc.ParseFormat("%{module} %{level} %{shortfunc} %{message}") 300 assert.NoError(t, err) 301 enc := fabenc.NewFormatEncoder(formatters...) 302 303 buf := &bytes.Buffer{} 304 enabler := zap.LevelEnablerFunc(func(l zapcore.Level) bool { return true }) 305 core := zapcore.NewCore(enc, zapcore.AddSync(buf), enabler) 306 zl := zap.New(core).Named("grpc") 307 gl := flogging.NewGRPCLogger(zl) 308 309 callWrapper(gl, "message") 310 assert.Equal(t, "grpc DEBUG TestGRPCLogger message\n", buf.String()) 311 } 312 313 // FAB-15432 314 // 315 // When the FabricLogger is used, the zap Core check function should not be 316 // driven if the minimum log level is above the level we are logging at. 317 // In other words, with a log spec of "info", logging at Debug should prevent 318 // a call to Check while logging at Info will not. 319 func TestEnabledLevelCheck(t *testing.T) { 320 buf := &bytes.Buffer{} 321 logging, err := flogging.New(flogging.Config{ 322 LogSpec: "info", 323 Writer: buf, 324 }) 325 assert.NoError(t, err) 326 327 fakeObserver := &mock.Observer{} 328 logging.SetObserver(fakeObserver) 329 330 logger := logging.ZapLogger("foo") 331 fabricLogger := flogging.NewFabricLogger(logger) 332 333 fabricLogger.Debug("debug message") 334 assert.Equal(t, 0, fakeObserver.CheckCallCount(), "Check should not have been called") 335 336 fabricLogger.Info("info message") 337 assert.Equal(t, 1, fakeObserver.CheckCallCount(), "Check should have been called") 338 }