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  }