trpc.group/trpc-go/trpc-go@v1.0.2/log/zaplogger_test.go (about)

     1  //
     2  //
     3  // Tencent is pleased to support the open source community by making tRPC available.
     4  //
     5  // Copyright (C) 2023 THL A29 Limited, a Tencent company.
     6  // All rights reserved.
     7  //
     8  // If you have downloaded a copy of the tRPC source code from Tencent,
     9  // please note that tRPC source code is licensed under the  Apache 2.0 License,
    10  // A copy of the Apache 2.0 License is included in this file.
    11  //
    12  //
    13  
    14  package log_test
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"runtime"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  	"github.com/stretchr/testify/require"
    25  	"go.uber.org/zap"
    26  	"go.uber.org/zap/zapcore"
    27  	"go.uber.org/zap/zaptest/observer"
    28  
    29  	"trpc.group/trpc-go/trpc-go/log"
    30  	"trpc.group/trpc-go/trpc-go/plugin"
    31  )
    32  
    33  func TestNewZapLog(t *testing.T) {
    34  	logger := log.NewZapLog(defaultConfig)
    35  	assert.NotNil(t, logger)
    36  
    37  	logger.SetLevel("0", log.LevelInfo)
    38  	lvl := logger.GetLevel("0")
    39  	assert.Equal(t, lvl, log.LevelInfo)
    40  
    41  	l := logger.With(log.Field{Key: "test", Value: "a"})
    42  	l.SetLevel("output", log.LevelDebug)
    43  	assert.Equal(t, log.LevelDebug, l.GetLevel("output"))
    44  }
    45  
    46  func TestNewZapLog_WriteMode(t *testing.T) {
    47  	logDir := t.TempDir()
    48  	t.Run("invalid write mode", func(t *testing.T) {
    49  		const invalidWriteMode = 4
    50  		require.Panics(t, func() {
    51  			log.NewZapLog([]log.OutputConfig{{
    52  				Writer: log.OutputFile,
    53  				WriteConfig: log.WriteConfig{
    54  					LogPath:   logDir,
    55  					Filename:  "trpc.log",
    56  					WriteMode: invalidWriteMode,
    57  				},
    58  			}})
    59  		})
    60  	})
    61  	t.Run("valid write mode", func(t *testing.T) {
    62  		const (
    63  			syncFileName  = "trpc.syncLog"
    64  			asyncFileName = "trpc.asyncLog"
    65  			fastFileName  = "trpc.fastLog"
    66  		)
    67  		tests := []struct {
    68  			name   string
    69  			config log.OutputConfig
    70  		}{
    71  			{"sync", log.OutputConfig{
    72  				Writer: log.OutputFile,
    73  				WriteConfig: log.WriteConfig{
    74  					LogPath:   logDir,
    75  					Filename:  syncFileName,
    76  					WriteMode: log.WriteSync,
    77  				},
    78  			}},
    79  			{"async", log.OutputConfig{
    80  				Writer: log.OutputFile,
    81  				WriteConfig: log.WriteConfig{
    82  					LogPath:   logDir,
    83  					Filename:  asyncFileName,
    84  					WriteMode: log.WriteAsync,
    85  				},
    86  			}},
    87  			{"fast", log.OutputConfig{
    88  				Writer: log.OutputFile,
    89  				WriteConfig: log.WriteConfig{
    90  					LogPath:   logDir,
    91  					Filename:  fastFileName,
    92  					WriteMode: log.WriteFast,
    93  				},
    94  			}},
    95  		}
    96  		for _, tt := range tests {
    97  			t.Run(tt.name, func(t *testing.T) {
    98  				require.NotNil(t, log.NewZapLog([]log.OutputConfig{tt.config}))
    99  			})
   100  		}
   101  	})
   102  }
   103  
   104  func TestZapLogWithLevel(t *testing.T) {
   105  	logger := log.NewZapLog(defaultConfig)
   106  	assert.NotNil(t, logger)
   107  
   108  	l := logger.With(log.Field{Key: "test", Value: "a"})
   109  	l.SetLevel("0", log.LevelFatal)
   110  	assert.Equal(t, log.LevelFatal, l.GetLevel("0"))
   111  
   112  	l = l.With(log.Field{Key: "key1", Value: "val1"})
   113  	l.SetLevel("0", log.LevelError)
   114  	assert.Equal(t, log.LevelError, l.GetLevel("0"))
   115  }
   116  
   117  func BenchmarkDefaultTimeFormat(b *testing.B) {
   118  	t := time.Now()
   119  	for i := 0; i < b.N; i++ {
   120  		log.DefaultTimeFormat(t)
   121  	}
   122  }
   123  
   124  func BenchmarkCustomTimeFormat(b *testing.B) {
   125  	t := time.Now()
   126  	for i := 0; i < b.N; i++ {
   127  		log.CustomTimeFormat(t, "2006-01-02 15:04:05.000")
   128  	}
   129  }
   130  
   131  func TestCustomTimeFormat(t *testing.T) {
   132  	date := time.Date(2006, 1, 2, 15, 4, 5, 0, time.Local)
   133  	dateStr := log.CustomTimeFormat(date, "2006-01-02 15:04:05.000")
   134  	assert.Equal(t, dateStr, "2006-01-02 15:04:05.000")
   135  }
   136  
   137  func TestDefaultTimeFormat(t *testing.T) {
   138  	date := time.Date(2006, 1, 2, 15, 4, 5, 0, time.Local)
   139  	dateStr := string(log.DefaultTimeFormat(date))
   140  	assert.Equal(t, dateStr, "2006-01-02 15:04:05.000")
   141  }
   142  
   143  func TestGetLogEncoderKey(t *testing.T) {
   144  	tests := []struct {
   145  		name   string
   146  		defKey string
   147  		key    string
   148  		want   string
   149  	}{
   150  		{"custom", "T", "Time", "Time"},
   151  		{"default", "T", "", "T"},
   152  	}
   153  
   154  	for _, tt := range tests {
   155  		t.Run(tt.name, func(t *testing.T) {
   156  			if got := log.GetLogEncoderKey(tt.defKey, tt.key); got != tt.want {
   157  				assert.Equal(t, got, tt.want)
   158  			}
   159  		})
   160  	}
   161  }
   162  
   163  func TestNewTimeEncoder(t *testing.T) {
   164  	encoder := log.NewTimeEncoder("")
   165  	assert.NotNil(t, encoder)
   166  
   167  	encoder = log.NewTimeEncoder("2006-01-02 15:04:05")
   168  	assert.NotNil(t, encoder)
   169  
   170  	tests := []struct {
   171  		name string
   172  		fmt  string
   173  	}{
   174  		{"seconds timestamp", "seconds"},
   175  		{"milliseconds timestamp", "milliseconds"},
   176  		{"nanoseconds timestamp", "nanoseconds"},
   177  	}
   178  
   179  	for _, tt := range tests {
   180  		t.Run(tt.name, func(t *testing.T) {
   181  			got := log.NewTimeEncoder(tt.fmt)
   182  			assert.NotNil(t, got)
   183  		})
   184  	}
   185  }
   186  
   187  func TestWithFields(t *testing.T) {
   188  	// register Writer.
   189  	// use zap observer to support test.
   190  	core, ob := observer.New(zap.InfoLevel)
   191  	log.RegisterWriter(observewriter, &observeWriter{core: core})
   192  
   193  	// config is configuration.
   194  	cfg := []log.OutputConfig{
   195  		{
   196  			Writer: observewriter,
   197  		},
   198  	}
   199  
   200  	// create a zap logger.
   201  	zl := log.NewZapLog(cfg)
   202  	assert.NotNil(t, zl)
   203  
   204  	// test With.
   205  	field := log.Field{Key: "abc", Value: int32(123)}
   206  	logger := zl.With(field)
   207  	assert.NotNil(t, logger)
   208  	log.SetLogger(logger)
   209  	log.Warn("with fields warning")
   210  	assert.Equal(t, 1, ob.Len())
   211  	entry := ob.All()[0]
   212  	assert.Equal(t, zap.WarnLevel, entry.Level)
   213  	assert.Equal(t, "with fields warning", entry.Message)
   214  	assert.Equal(t, []zapcore.Field{{Key: "abc", Type: zapcore.Int32Type, Integer: 123}}, entry.Context)
   215  }
   216  
   217  func TestOptionLogger2(t *testing.T) {
   218  	t.Run("test option logger add caller skip", func(t *testing.T) {
   219  		core, ob := observer.New(zap.InfoLevel)
   220  		log.RegisterWriter(observewriter, &observeWriter{core: core})
   221  		cfg := []log.OutputConfig{{Writer: observewriter}}
   222  
   223  		l := log.NewZapLogWithCallerSkip(cfg, 1)
   224  		l.Info("this is option logger test, the current caller skip is correct")
   225  
   226  		_, file, _, ok := runtime.Caller(0)
   227  		require.True(t, ok)
   228  		require.Equal(t, file, ob.All()[0].Caller.File)
   229  
   230  		ol, ok := l.(log.OptionLogger)
   231  		require.True(t, ok)
   232  		l = ol.WithOptions(log.WithAdditionalCallerSkip(1))
   233  		l.Info("this is option logger test, the current caller skip is incorrect(added 1)")
   234  
   235  		_, file, _, ok = runtime.Caller(1)
   236  		require.True(t, ok)
   237  		require.Equal(t, file, ob.All()[1].Caller.File)
   238  	})
   239  	t.Run("test option logger wrapper add caller skip", func(t *testing.T) {
   240  		core, ob := observer.New(zap.InfoLevel)
   241  		log.RegisterWriter(observewriter, &observeWriter{core: core})
   242  		cfg := []log.OutputConfig{{Writer: observewriter}}
   243  
   244  		l := log.NewZapLogWithCallerSkip(cfg, 1)
   245  		l = l.With(log.Field{Key: "k", Value: "v"})
   246  		l.Info("this is option logger wrapper test, the current caller skip is correct")
   247  
   248  		_, file, _, ok := runtime.Caller(0)
   249  		require.True(t, ok)
   250  		require.Equal(t, file, ob.All()[0].Caller.File)
   251  
   252  		ol, ok := l.(log.OptionLogger)
   253  		require.True(t, ok)
   254  		l = ol.WithOptions(log.WithAdditionalCallerSkip(1))
   255  		l.Info("this is option logger wrapper test, the current caller skip is incorrect(added 1)")
   256  
   257  		_, file, _, ok = runtime.Caller(1)
   258  		require.True(t, ok)
   259  		require.Equal(t, file, ob.All()[1].Caller.File)
   260  	})
   261  }
   262  
   263  const observewriter = "observewriter"
   264  
   265  type observeWriter struct {
   266  	core zapcore.Core
   267  }
   268  
   269  func (f *observeWriter) Type() string { return "log" }
   270  
   271  func (f *observeWriter) Setup(name string, dec plugin.Decoder) error {
   272  	if dec == nil {
   273  		return errors.New("empty decoder")
   274  	}
   275  	decoder, ok := dec.(*log.Decoder)
   276  	if !ok {
   277  		return errors.New("invalid decoder")
   278  	}
   279  	decoder.Core = f.core
   280  	decoder.ZapLevel = zap.NewAtomicLevel()
   281  	return nil
   282  }
   283  
   284  func TestLogLevel(t *testing.T) {
   285  	config := []log.OutputConfig{
   286  		{
   287  			Writer: "console",
   288  			Level:  "",
   289  		},
   290  		{
   291  			Writer: "console",
   292  			Level:  "trace",
   293  		},
   294  		{
   295  			Writer: "console",
   296  			Level:  "debug",
   297  		},
   298  		{
   299  			Writer: "console",
   300  			Level:  "info",
   301  		},
   302  		{
   303  			Writer: "console",
   304  			Level:  "warn",
   305  		},
   306  		{
   307  			Writer: "console",
   308  			Level:  "error",
   309  		},
   310  		{
   311  			Writer: "console",
   312  			Level:  "fatal",
   313  		},
   314  	}
   315  	l := log.NewZapLog(config)
   316  
   317  	var (
   318  		got  []string
   319  		want []string
   320  	)
   321  	for i, c := range config {
   322  		got = append(got, log.LevelStrings[l.GetLevel(fmt.Sprint(i))])
   323  		want = append(want, log.Levels[c.Level].String())
   324  	}
   325  	require.Equal(t, want, got)
   326  }
   327  
   328  func TestLogEnableColor(t *testing.T) {
   329  	cfg := []log.OutputConfig{{Writer: "console", Level: "trace", EnableColor: true}}
   330  	l := log.NewZapLog(cfg)
   331  	l.Trace("hello")
   332  	l.Debug("hello")
   333  	l.Info("hello")
   334  	l.Warn("hello")
   335  	l.Error("hello")
   336  }