github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/logutil/log_test.go (about) 1 // Copyright 2020 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 logutil 15 16 import ( 17 "context" 18 "database/sql" 19 "fmt" 20 "net" 21 "os" 22 "path/filepath" 23 "regexp" 24 "runtime" 25 "sync" 26 "testing" 27 "time" 28 29 "github.com/pingcap/errors" 30 "github.com/pingcap/log" 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 TestInitLoggerAndSetLogLevel(t *testing.T) { 38 f, err := os.CreateTemp("", "init-logger-test") 39 require.Nil(t, err) 40 defer os.Remove(f.Name()) 41 42 cfg := &Config{ 43 Level: "warning", 44 File: f.Name(), 45 } 46 cfg.Adjust() 47 err = InitLogger(cfg) 48 require.NoError(t, err) 49 require.Equal(t, log.GetLevel(), zapcore.WarnLevel) 50 51 // Set a different level. 52 err = SetLogLevel("info") 53 require.NoError(t, err) 54 require.Equal(t, log.GetLevel(), zapcore.InfoLevel) 55 56 // Set the same level. 57 err = SetLogLevel("info") 58 require.NoError(t, err) 59 require.Equal(t, log.GetLevel(), zapcore.InfoLevel) 60 61 // Set an invalid level. 62 err = SetLogLevel("badlevel") 63 require.Error(t, err) 64 } 65 66 func TestZapErrorFilter(t *testing.T) { 67 var ( 68 err = errors.New("test error") 69 testCases = []struct { 70 err error 71 filters []error 72 expected zap.Field 73 }{ 74 {nil, []error{}, zap.Error(nil)}, 75 {err, []error{}, zap.Error(err)}, 76 {err, []error{context.Canceled}, zap.Error(err)}, 77 {err, []error{err}, zap.Error(nil)}, 78 {context.Canceled, []error{context.Canceled}, zap.Error(nil)}, 79 {errors.Annotate(context.Canceled, "annotate error"), []error{context.Canceled}, zap.Error(nil)}, 80 } 81 ) 82 for _, tc := range testCases { 83 require.Equal(t, ZapErrorFilter(tc.err, tc.filters...), tc.expected) 84 } 85 } 86 87 func TestZapInternalErrorOutput(t *testing.T) { 88 testCases := []struct { 89 desc string 90 errOutput string 91 error bool 92 }{ 93 {"test valid error output path", "stderr", false}, 94 {"test invalid error output path", filepath.Join(t.TempDir(), "/not-there/foo.log"), true}, 95 } 96 97 dir := t.TempDir() 98 for idx, tc := range testCases { 99 f := filepath.Join(dir, fmt.Sprintf("test-file%d", idx)) 100 cfg := &Config{ 101 Level: "info", 102 File: f, 103 ZapInternalErrOutput: tc.errOutput, 104 } 105 err := InitLogger(cfg) 106 if tc.error { 107 require.NotNil(t, err) 108 } else { 109 require.Nil(t, err) 110 } 111 } 112 } 113 114 func TestErrorFilterContextCanceled(t *testing.T) { 115 var buffer zaptest.Buffer 116 err := InitLogger(&Config{Level: "info"}, WithOutputWriteSyncer(&buffer)) 117 require.NoError(t, err) 118 119 ErrorFilterContextCanceled(log.L(), "the message", zap.Int("number", 123456), 120 zap.Ints("array", []int{7, 8, 9}), zap.Error(context.Canceled)) 121 require.Equal(t, "", buffer.Stripped()) 122 buffer.Reset() 123 124 ErrorFilterContextCanceled(log.L(), "the message", zap.Int("number", 123456), 125 zap.Ints("array", []int{7, 8, 9}), 126 ShortError(errors.Annotate(context.Canceled, "extra info"))) 127 require.Equal(t, "", buffer.Stripped()) 128 buffer.Reset() 129 130 ErrorFilterContextCanceled(log.L(), "the message", zap.Int("number", 123456), 131 zap.Ints("array", []int{7, 8, 9})) 132 require.Regexp(t, regexp.QuoteMeta("[\"the message\"]"+ 133 " [number=123456] [array=\"[7,8,9]\"]"), buffer.Stripped()) 134 } 135 136 func TestShortError(t *testing.T) { 137 var buffer zaptest.Buffer 138 err := InitLogger(&Config{Level: "info"}, WithOutputWriteSyncer(&buffer)) 139 require.NoError(t, err) 140 141 err = errors.Normalize( 142 "meta not exists in region", 143 errors.RFCCodeText("CDC:ErrMetaNotInRegion"), 144 ).GenWithStackByArgs("extra info") 145 log.L().Warn("short error", ShortError(err)) 146 require.Regexp(t, regexp.QuoteMeta("[\"short error\"] "+ 147 "[error=\"[CDC:ErrMetaNotInRegion]meta not exists in region"), buffer.Stripped()) 148 buffer.Reset() 149 150 log.L().Warn("short error", ShortError(nil)) 151 require.Regexp(t, regexp.QuoteMeta("[\"short error\"] []"), buffer.Stripped()) 152 buffer.Reset() 153 154 log.L().Warn("short error", zap.Error(err)) 155 require.Regexp(t, regexp.QuoteMeta("errors.AddStack"), buffer.Stripped()) 156 buffer.Reset() 157 } 158 159 func TestLoggerOption(t *testing.T) { 160 t.Parallel() 161 162 var op loggerOp 163 require.False(t, op.isInitGRPCLogger) 164 require.False(t, op.isInitSaramaLogger) 165 require.Nil(t, op.output) 166 167 op.applyOpts([]LoggerOpt{WithInitGRPCLogger(), WithInitSaramaLogger()}) 168 require.True(t, op.isInitGRPCLogger) 169 require.True(t, op.isInitSaramaLogger) 170 require.Nil(t, op.output) 171 172 var buffer zaptest.Buffer 173 op.applyOpts([]LoggerOpt{WithOutputWriteSyncer(&buffer)}) 174 require.Equal(t, &buffer, op.output) 175 } 176 177 func TestWithComponent(t *testing.T) { 178 var buffer zaptest.Buffer 179 err := InitLogger(&Config{Level: "info"}, WithOutputWriteSyncer(&buffer)) 180 require.NoError(t, err) 181 182 lg := WithComponent("grpc") 183 lg.Warn("component test", zap.String("other", "other")) 184 require.Regexp(t, regexp.QuoteMeta("[\"component test\"] [component=grpc] [other=other]"), buffer.Stripped()) 185 buffer.Reset() 186 } 187 188 func TestCallerSkip(t *testing.T) { 189 // Using log before init logger should not affect 190 // any other log after init logger. 191 // 192 // See https://github.com/pingcap/log/issues/30. 193 log.Debug("debug") 194 log.L().Debug("debug") 195 196 var buffer zaptest.Buffer 197 err := InitLogger(&Config{Level: "info"}, WithOutputWriteSyncer(&buffer)) 198 require.NoError(t, err) 199 200 _, file, line, _ := runtime.Caller(0) 201 _, filename := filepath.Split(file) 202 log.Info("caller skip test", zap.String("other", "other")) 203 require.Contains(t, buffer.Stripped(), fmt.Sprintf("%s:%d", filename, line+2)) 204 205 buffer.Reset() 206 _, file, line, _ = runtime.Caller(0) 207 _, filename = filepath.Split(file) 208 log.L().Info("caller skip test", zap.String("other", "other")) 209 require.Contains(t, buffer.Stripped(), fmt.Sprintf("%s:%d", filename, line+2)) 210 } 211 212 func TestMySQLLogger(t *testing.T) { 213 var buffer zaptest.Buffer 214 err := InitLogger(&Config{Level: "info"}, WithOutputWriteSyncer(&buffer)) 215 require.NoError(t, err) 216 217 require.Nil(t, initMySQLLogger()) 218 219 // Mock MySQL server 220 ms, err := net.Listen("tcp4", "127.0.0.1:0") 221 require.NoError(t, err) 222 wg := sync.WaitGroup{} 223 wg.Add(1) 224 go func() { 225 defer wg.Done() 226 conn, err := ms.Accept() 227 require.NoError(t, err) 228 err = conn.Close() 229 require.NoError(t, err) 230 }() 231 232 dsnStr := fmt.Sprintf("root:@tcp(%s)/", ms.Addr().String()) 233 db, err := sql.Open("mysql", dsnStr) 234 require.Nil(t, err) 235 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 236 defer cancel() 237 err = db.PingContext(ctx) 238 require.Error(t, err) 239 // Log: [ERROR] [packets.go:37] ["unexpected EOF"] [component="[mysql]"] 240 require.Contains(t, buffer.Stripped(), "[ERROR]") 241 require.Contains(t, buffer.Stripped(), "packets.go") 242 require.Contains(t, buffer.Stripped(), "unexpected EOF") 243 require.Contains(t, buffer.Stripped(), "[mysql]") 244 wg.Wait() 245 }