github.com/pingcap/br@v5.3.0-alpha.0.20220125034240-ec59c7b6ce30+incompatible/pkg/logutil/logging_test.go (about) 1 // Copyright 2021 PingCAP, Inc. Licensed under Apache-2.0. 2 3 package logutil_test 4 5 import ( 6 "context" 7 "fmt" 8 "math" 9 "strings" 10 "testing" 11 "time" 12 13 . "github.com/pingcap/check" 14 "github.com/pingcap/errors" 15 backuppb "github.com/pingcap/kvproto/pkg/backup" 16 "github.com/pingcap/kvproto/pkg/import_sstpb" 17 "github.com/pingcap/kvproto/pkg/metapb" 18 "github.com/prometheus/client_golang/prometheus" 19 "go.uber.org/zap" 20 "go.uber.org/zap/zapcore" 21 "go.uber.org/zap/zaptest/observer" 22 23 berrors "github.com/pingcap/br/pkg/errors" 24 "github.com/pingcap/br/pkg/logutil" 25 ) 26 27 func Test(t *testing.T) { 28 TestingT(t) 29 } 30 31 var _ = Suite(&testLoggingSuite{}) 32 33 type testLoggingSuite struct{} 34 35 func assertTrimEqual(c *C, f zapcore.Field, expect string) { 36 encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{}) 37 out, err := encoder.EncodeEntry(zapcore.Entry{}, []zap.Field{f}) 38 c.Assert(err, IsNil) 39 c.Assert(strings.TrimRight(out.String(), "\n"), Equals, expect) 40 } 41 42 func newFile(j int) *backuppb.File { 43 return &backuppb.File{ 44 Name: fmt.Sprint(j), 45 StartKey: []byte(fmt.Sprint(j)), 46 EndKey: []byte(fmt.Sprint(j + 1)), 47 TotalKvs: uint64(j), 48 TotalBytes: uint64(j), 49 StartVersion: uint64(j), 50 EndVersion: uint64(j + 1), 51 Crc64Xor: uint64(j), 52 Sha256: []byte(fmt.Sprint(j)), 53 Cf: "write", 54 Size_: uint64(j), 55 } 56 } 57 58 type isAbout struct{} 59 60 func (isAbout) Info() *CheckerInfo { 61 return &CheckerInfo{ 62 Name: "isAbout", 63 Params: []string{ 64 "actual", 65 "expect", 66 }, 67 } 68 } 69 70 func (isAbout) Check(params []interface{}, names []string) (result bool, error string) { 71 actual := params[0].(float64) 72 expect := params[1].(float64) 73 74 if diff := math.Abs(1 - (actual / expect)); diff > 0.1 { 75 return false, fmt.Sprintf("The diff(%.2f) between actual(%.2f) and expect(%.2f) is too huge.", diff, actual, expect) 76 } 77 return true, "" 78 } 79 80 func (s *testLoggingSuite) TestRater(c *C) { 81 m := prometheus.NewCounter(prometheus.CounterOpts{ 82 Namespace: "testing", 83 Name: "rater", 84 Help: "A testing counter for the rater", 85 }) 86 m.Add(42) 87 88 rater := logutil.TraceRateOver(m) 89 timePass := time.Now() 90 rater.Inc() 91 c.Assert(rater.RateAt(timePass.Add(100*time.Millisecond)), isAbout{}, 10.0) 92 rater.Inc() 93 c.Assert(rater.RateAt(timePass.Add(150*time.Millisecond)), isAbout{}, 13.0) 94 rater.Add(18) 95 c.Assert(rater.RateAt(timePass.Add(200*time.Millisecond)), isAbout{}, 100.0) 96 } 97 98 func (s *testLoggingSuite) TestFile(c *C) { 99 assertTrimEqual(c, logutil.File(newFile(1)), 100 `{"file": {"name": "1", "CF": "write", "sha256": "31", "startKey": "31", "endKey": "32", "startVersion": 1, "endVersion": 2, "totalKvs": 1, "totalBytes": 1, "CRC64Xor": 1}}`) 101 } 102 103 func (s *testLoggingSuite) TestFiles(c *C) { 104 cases := []struct { 105 count int 106 expect string 107 }{ 108 {0, `{"files": {"total": 0, "files": [], "totalKVs": 0, "totalBytes": 0, "totalSize": 0}}`}, 109 {1, `{"files": {"total": 1, "files": ["0"], "totalKVs": 0, "totalBytes": 0, "totalSize": 0}}`}, 110 {2, `{"files": {"total": 2, "files": ["0", "1"], "totalKVs": 1, "totalBytes": 1, "totalSize": 1}}`}, 111 {3, `{"files": {"total": 3, "files": ["0", "1", "2"], "totalKVs": 3, "totalBytes": 3, "totalSize": 3}}`}, 112 {4, `{"files": {"total": 4, "files": ["0", "1", "2", "3"], "totalKVs": 6, "totalBytes": 6, "totalSize": 6}}`}, 113 {5, `{"files": {"total": 5, "files": ["0", "(skip 3)", "4"], "totalKVs": 10, "totalBytes": 10, "totalSize": 10}}`}, 114 {6, `{"files": {"total": 6, "files": ["0", "(skip 4)", "5"], "totalKVs": 15, "totalBytes": 15, "totalSize": 15}}`}, 115 {1024, `{"files": {"total": 1024, "files": ["0", "(skip 1022)", "1023"], "totalKVs": 523776, "totalBytes": 523776, "totalSize": 523776}}`}, 116 } 117 118 for _, cs := range cases { 119 ranges := make([]*backuppb.File, cs.count) 120 for j := 0; j < cs.count; j++ { 121 ranges[j] = newFile(j) 122 } 123 assertTrimEqual(c, logutil.Files(ranges), cs.expect) 124 } 125 } 126 127 func (s *testLoggingSuite) TestKey(c *C) { 128 encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{}) 129 out, err := encoder.EncodeEntry(zapcore.Entry{}, []zap.Field{logutil.Key("test", []byte{0, 1, 2, 3})}) 130 c.Assert(err, IsNil) 131 c.Assert(strings.Trim(out.String(), "\n"), Equals, `{"test": "00010203"}`) 132 } 133 134 func (s *testLoggingSuite) TestKeys(c *C) { 135 cases := []struct { 136 count int 137 expect string 138 }{ 139 {0, `{"keys": {"total": 0, "keys": []}}`}, 140 {1, `{"keys": {"total": 1, "keys": ["30303030"]}}`}, 141 {2, `{"keys": {"total": 2, "keys": ["30303030", "30303031"]}}`}, 142 {3, `{"keys": {"total": 3, "keys": ["30303030", "30303031", "30303032"]}}`}, 143 {4, `{"keys": {"total": 4, "keys": ["30303030", "30303031", "30303032", "30303033"]}}`}, 144 {5, `{"keys": {"total": 5, "keys": ["30303030", "(skip 3)", "30303034"]}}`}, 145 {6, `{"keys": {"total": 6, "keys": ["30303030", "(skip 4)", "30303035"]}}`}, 146 {1024, `{"keys": {"total": 1024, "keys": ["30303030", "(skip 1022)", "31303233"]}}`}, 147 } 148 149 for _, cs := range cases { 150 keys := make([][]byte, cs.count) 151 for j := 0; j < cs.count; j++ { 152 keys[j] = []byte(fmt.Sprintf("%04d", j)) 153 } 154 assertTrimEqual(c, logutil.Keys(keys), cs.expect) 155 } 156 } 157 158 func (s *testLoggingSuite) TestRewriteRule(c *C) { 159 rule := &import_sstpb.RewriteRule{ 160 OldKeyPrefix: []byte("old"), 161 NewKeyPrefix: []byte("new"), 162 NewTimestamp: 0x555555, 163 } 164 165 encoder := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{}) 166 out, err := encoder.EncodeEntry(zapcore.Entry{}, []zap.Field{logutil.RewriteRule(rule)}) 167 c.Assert(err, IsNil) 168 c.Assert(strings.Trim(out.String(), "\n"), Equals, `{"rewriteRule": {"oldKeyPrefix": "6f6c64", "newKeyPrefix": "6e6577", "newTimestamp": 5592405}}`) 169 } 170 171 func (s *testLoggingSuite) TestRegion(c *C) { 172 region := &metapb.Region{ 173 Id: 1, 174 StartKey: []byte{0x00, 0x01}, 175 EndKey: []byte{0x00, 0x02}, 176 RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, 177 Peers: []*metapb.Peer{{Id: 2, StoreId: 3}, {Id: 4, StoreId: 5}}, 178 } 179 180 assertTrimEqual(c, logutil.Region(region), 181 `{"region": {"ID": 1, "startKey": "0001", "endKey": "0002", "epoch": "conf_ver:1 version:1 ", "peers": "id:2 store_id:3 ,id:4 store_id:5 "}}`) 182 } 183 184 func (s *testLoggingSuite) TestLeader(c *C) { 185 leader := &metapb.Peer{Id: 2, StoreId: 3} 186 187 assertTrimEqual(c, logutil.Leader(leader), `{"leader": "id:2 store_id:3 "}`) 188 } 189 190 func (s *testLoggingSuite) TestSSTMeta(c *C) { 191 meta := &import_sstpb.SSTMeta{ 192 Uuid: []byte("mock uuid"), 193 Range: &import_sstpb.Range{ 194 Start: []byte{0x00, 0x01}, 195 End: []byte{0x00, 0x02}, 196 }, 197 Crc32: uint32(0x555555), 198 Length: 1, 199 CfName: "default", 200 RegionId: 1, 201 RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, 202 } 203 204 assertTrimEqual(c, logutil.SSTMeta(meta), 205 `{"sstMeta": {"CF": "default", "endKeyExclusive": false, "CRC32": 5592405, "length": 1, "regionID": 1, "regionEpoch": "conf_ver:1 version:1 ", "startKey": "0001", "endKey": "0002", "UUID": "invalid UUID 6d6f636b2075756964"}}`) 206 } 207 208 func (s *testLoggingSuite) TestShortError(c *C) { 209 err := errors.Annotate(berrors.ErrInvalidArgument, "test") 210 211 assertTrimEqual(c, logutil.ShortError(err), `{"error": "test: [BR:Common:ErrInvalidArgument]invalid argument"}`) 212 } 213 214 type FieldEquals struct{} 215 216 func (f FieldEquals) Info() *CheckerInfo { 217 return &CheckerInfo{ 218 Name: "FieldEquals", 219 Params: []string{ 220 "expected", 221 "actual", 222 }, 223 } 224 } 225 226 func (f FieldEquals) Check(params []interface{}, names []string) (result bool, err string) { 227 expected := params[0].(zap.Field) 228 actual := params[1].(zap.Field) 229 230 if !expected.Equals(actual) { 231 return false, "Field not match." 232 } 233 return true, "" 234 } 235 236 func (s *testLoggingSuite) TestContextual(c *C) { 237 testCore, logs := observer.New(zap.InfoLevel) 238 logutil.ResetGlobalLogger(zap.New(testCore)) 239 240 ctx := context.Background() 241 l0 := logutil.LoggerFromContext(ctx) 242 l0.Info("going to take an adventure?", zap.Int("HP", 50), zap.Int("HP-MAX", 50), zap.String("character", "solte")) 243 lctx := logutil.ContextWithField(ctx, zap.Strings("firends", []string{"firo", "seren", "black"})) 244 l := logutil.LoggerFromContext(lctx) 245 l.Info("let's go!", zap.String("character", "solte")) 246 247 observedLogs := logs.TakeAll() 248 checkLog(c, observedLogs[0], 249 "going to take an adventure?", zap.Int("HP", 50), zap.Int("HP-MAX", 50), zap.String("character", "solte")) 250 checkLog(c, observedLogs[1], 251 "let's go!", zap.Strings("firends", []string{"firo", "seren", "black"}), zap.String("character", "solte")) 252 } 253 254 func checkLog(c *C, actual observer.LoggedEntry, message string, fields ...zap.Field) { 255 c.Assert(message, Equals, actual.Message) 256 for i, f := range fields { 257 c.Assert(f, FieldEquals{}, actual.Context[i]) 258 } 259 }