github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/log/log_test.go (about)

     1  // Copyright 2019 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package log
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"encoding/json"
    20  	"io"
    21  	"os"
    22  	"path/filepath"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/pingcap/errors"
    27  	pclog "github.com/pingcap/log"
    28  	lightningLog "github.com/pingcap/tidb/pkg/lightning/log"
    29  	"github.com/pingcap/tidb/pkg/util/logutil"
    30  	"github.com/pingcap/tiflow/pkg/version"
    31  	"github.com/stretchr/testify/require"
    32  	"go.uber.org/zap"
    33  	"go.uber.org/zap/zapcore"
    34  	"go.uber.org/zap/zaptest"
    35  )
    36  
    37  func TestTestLogger(t *testing.T) {
    38  	logger, buffer := makeTestLogger()
    39  	logger.Warn("the message", zap.Int("number", 123456), zap.Ints("array", []int{7, 8, 9}))
    40  	require.Equal(t, `{"$lvl":"WARN","$msg":"the message","number":123456,"array":[7,8,9]}`, buffer.Stripped())
    41  	buffer.Reset()
    42  	logger.ErrorFilterContextCanceled("the message", zap.Int("number", 123456),
    43  		zap.Ints("array", []int{7, 8, 9}), zap.Error(context.Canceled))
    44  	require.Empty(t, buffer.Stripped())
    45  	buffer.Reset()
    46  	logger.ErrorFilterContextCanceled("the message", zap.Int("number", 123456),
    47  		zap.Ints("array", []int{7, 8, 9}), ShortError(errors.Annotate(context.Canceled, "extra info")))
    48  	require.Empty(t, buffer.Stripped())
    49  }
    50  
    51  // makeTestLogger creates a Logger instance which produces JSON logs.
    52  func makeTestLogger() (Logger, *zaptest.Buffer) {
    53  	buffer := new(zaptest.Buffer)
    54  	logger := zap.New(zapcore.NewCore(
    55  		zapcore.NewJSONEncoder(zapcore.EncoderConfig{
    56  			LevelKey:       "$lvl",
    57  			MessageKey:     "$msg",
    58  			EncodeLevel:    zapcore.CapitalLevelEncoder,
    59  			EncodeDuration: zapcore.StringDurationEncoder,
    60  		}),
    61  		buffer,
    62  		zap.DebugLevel,
    63  	))
    64  	return Logger{Logger: logger}, buffer
    65  }
    66  
    67  func TestLogLevel(t *testing.T) {
    68  	logLevel := "warning"
    69  	cfg := &Config{
    70  		Level: logLevel,
    71  	}
    72  	cfg.Adjust()
    73  
    74  	require.NoError(t, InitLogger(cfg))
    75  	require.Equal(t, zap.WarnLevel.String(), Props().Level.String())
    76  	require.Nil(t, L().Check(zap.InfoLevel, "This is an info log"))
    77  	require.NotNil(t, L().Check(zap.ErrorLevel, "This is an error log"))
    78  
    79  	SetLevel(zap.InfoLevel)
    80  	require.Equal(t, zap.InfoLevel.String(), Props().Level.String())
    81  	require.NotNil(t, L().Check(zap.WarnLevel, "This is a warn log"))
    82  	require.Nil(t, L().Check(zap.DebugLevel, "This is a debug log"))
    83  }
    84  
    85  func captureStdout(f func()) ([]string, error) {
    86  	r, w, _ := os.Pipe()
    87  	stdout := os.Stdout
    88  	os.Stdout = w
    89  
    90  	f()
    91  
    92  	var buf bytes.Buffer
    93  	output := make(chan string, 1)
    94  	errs := make(chan error, 1)
    95  
    96  	go func() {
    97  		_, err := io.Copy(&buf, r)
    98  		output <- buf.String()
    99  		errs <- err
   100  		r.Close()
   101  	}()
   102  
   103  	os.Stdout = stdout
   104  	w.Close()
   105  	return strings.Split(<-output, "\n"), <-errs
   106  }
   107  
   108  func TestInitSlowQueryLoggerInDebugLevel(t *testing.T) {
   109  	// test slow query logger can write debug log
   110  	logLevel := "debug"
   111  	cfg := &Config{Level: logLevel, Format: "json"}
   112  	cfg.Adjust()
   113  	output, err := captureStdout(func() {
   114  		require.NoError(t, InitLogger(cfg))
   115  		logutil.SlowQueryLogger.Debug("this is test info")
   116  		appLogger.Debug("this is from applogger")
   117  	})
   118  	require.NoError(t, err)
   119  	require.Regexp(t, "this is test info.*component.*slow query logger", output[0])
   120  	require.Contains(t, output[1], "this is from applogger")
   121  	// test log is json formart
   122  	type jsonLog struct {
   123  		Component string `json:"component"`
   124  	}
   125  	oneLog := jsonLog{}
   126  	require.NoError(t, json.Unmarshal([]byte(output[0]), &oneLog))
   127  	require.Equal(t, "slow query logger", oneLog.Component)
   128  }
   129  
   130  func TestInitSlowQueryLoggerNotInDebugLevel(t *testing.T) {
   131  	// test slow query logger can not write log in other log level
   132  	logLevel := "info"
   133  	cfg := &Config{Level: logLevel, Format: "json"}
   134  	cfg.Adjust()
   135  	output, err := captureStdout(func() {
   136  		require.NoError(t, InitLogger(cfg))
   137  		logutil.SlowQueryLogger.Info("this is test info")
   138  		appLogger.Info("this is from applogger")
   139  	})
   140  	require.NoError(t, err)
   141  	require.Len(t, output, 2)
   142  	require.Contains(t, output[0], "this is from applogger")
   143  	require.Empty(t, output[1]) // no output
   144  }
   145  
   146  func TestWithCtx(t *testing.T) {
   147  	// test slow query logger can write debug log
   148  	logLevel := "debug"
   149  	cfg := &Config{Level: logLevel, Format: "json"}
   150  	cfg.Adjust()
   151  
   152  	ctx := context.Background()
   153  	ctx = AppendZapFieldToCtx(ctx, zap.String("key1", "value1"))
   154  	ctx = AppendZapFieldToCtx(ctx, zap.String("key2", "value2"))
   155  
   156  	output, err := captureStdout(func() {
   157  		require.NoError(t, InitLogger(cfg))
   158  		WithCtx(ctx).Info("test1")
   159  	})
   160  	require.NoError(t, err)
   161  	require.Regexp(t, `"key1":"value1".*"key2":"value2"`, output[0])
   162  }
   163  
   164  func TestLogToFile(t *testing.T) {
   165  	d := t.TempDir()
   166  
   167  	logFile := filepath.Join(d, "test.log")
   168  
   169  	logLevel := "debug"
   170  	cfg := &Config{
   171  		Level:  logLevel,
   172  		Format: "json",
   173  		File:   logFile,
   174  	}
   175  	cfg.Adjust()
   176  	require.NoError(t, InitLogger(cfg))
   177  
   178  	var lastOff int64
   179  	newLog := func() string {
   180  		require.NoError(t, L().Sync())
   181  		data, err := os.ReadFile(logFile)
   182  		require.NoError(t, err)
   183  		require.Greater(t, len(data), int(lastOff))
   184  		result := string(data[lastOff:])
   185  		lastOff = int64(len(data))
   186  		return strings.TrimSpace(result)
   187  	}
   188  
   189  	L().Info("test dm log to file")
   190  	require.Contains(t, newLog(), `"message":"test dm log to file"`)
   191  	lightningLog.L().Info("test lightning log to file")
   192  	require.Contains(t, newLog(), `"message":"test lightning log to file"`)
   193  	pclog.Info("test pingcap/log to file")
   194  	require.Contains(t, newLog(), `"message":"test pingcap/log to file"`)
   195  	version.LogVersionInfo("DM")
   196  	require.Contains(t, newLog(), `"message":"Welcome to DM"`)
   197  }
   198  
   199  func BenchmarkBaseline(b *testing.B) {
   200  	logger := L().With(zap.String("key1", "value1"))
   201  	for i := 0; i < b.N; i++ {
   202  		subLogger := logger.With(zap.String("key2", "value2"))
   203  		subLogger.Info("test-test-test")
   204  	}
   205  }
   206  
   207  func BenchmarkWithCtx(b *testing.B) {
   208  	ctx := AppendZapFieldToCtx(context.Background(), zap.String("key1", "value1"))
   209  	for i := 0; i < b.N; i++ {
   210  		subCtx := AppendZapFieldToCtx(ctx, zap.String("key2", "value2"))
   211  		WithCtx(subCtx).Info("test-test-test")
   212  	}
   213  }