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 }