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