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  }