github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/soliton/logutil/log_test.go (about)

     1  // Copyright 2020 WHTCORPS INC, 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  	"bufio"
    18  	"bytes"
    19  	"context"
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"runtime"
    24  	"strings"
    25  	"testing"
    26  
    27  	. "github.com/whtcorpsinc/check"
    28  	zaplog "github.com/whtcorpsinc/log"
    29  	log "github.com/sirupsen/logrus"
    30  	"go.uber.org/zap"
    31  )
    32  
    33  const (
    34  	logPattern = `\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d\.\d\d\d ([\w_%!$@.,+~-]+|\\.)+:\d+: \[(fatal|error|warning|info|debug)\] .*?\n`
    35  	// zapLogPatern is used to match the zap log format, such as the following log:
    36  	// [2020/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] ["str key"=val] ["int key"=123]
    37  	zapLogPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] (\[.*=.*\]).*\n`
    38  	// [2020/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] ["str key"=val] ["int key"=123]
    39  	zapLogWithConnIDPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[conn=.*\] (\[.*=.*\]).*\n`
    40  	// [2020/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] ["str key"=val] ["int key"=123]
    41  	zapLogWithKeyValPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[ctxKey=.*\] (\[.*=.*\]).*\n`
    42  )
    43  
    44  var PrettyPrint = prettyPrint
    45  
    46  func Test(t *testing.T) {
    47  	TestingT(t)
    48  }
    49  
    50  var _ = Suite(&testLogSuite{})
    51  
    52  type testLogSuite struct {
    53  	buf *bytes.Buffer
    54  }
    55  
    56  func (s *testLogSuite) SetUpSuite(c *C) {
    57  	s.buf = &bytes.Buffer{}
    58  }
    59  
    60  func (s *testLogSuite) SetUpTest(c *C) {
    61  	s.buf = &bytes.Buffer{}
    62  }
    63  
    64  func (s *testLogSuite) TestStringToLogLevel(c *C) {
    65  	c.Assert(stringToLogLevel("fatal"), Equals, log.FatalLevel)
    66  	c.Assert(stringToLogLevel("ERROR"), Equals, log.ErrorLevel)
    67  	c.Assert(stringToLogLevel("warn"), Equals, log.WarnLevel)
    68  	c.Assert(stringToLogLevel("warning"), Equals, log.WarnLevel)
    69  	c.Assert(stringToLogLevel("debug"), Equals, log.DebugLevel)
    70  	c.Assert(stringToLogLevel("info"), Equals, log.InfoLevel)
    71  	c.Assert(stringToLogLevel("whatever"), Equals, log.InfoLevel)
    72  }
    73  
    74  // TestLogging assure log format and log redirection works.
    75  func (s *testLogSuite) TestLogging(c *C) {
    76  	conf := NewLogConfig("warn", DefaultLogFormat, "", NewFileLogConfig(0), false)
    77  	conf.File.Filename = "log_file"
    78  	c.Assert(InitLogger(conf), IsNil)
    79  
    80  	log.SetOutput(s.buf)
    81  
    82  	log.Infof("[this message should not be sent to buf]")
    83  	c.Assert(s.buf.Len(), Equals, 0)
    84  
    85  	log.Warningf("[this message should be sent to buf]")
    86  	entry, err := s.buf.ReadString('\n')
    87  	c.Assert(err, IsNil)
    88  	c.Assert(entry, Matches, logPattern)
    89  
    90  	log.Warnf("this message comes from logrus")
    91  	entry, err = s.buf.ReadString('\n')
    92  	c.Assert(err, IsNil)
    93  	c.Assert(entry, Matches, logPattern)
    94  	c.Assert(strings.Contains(entry, "log_test.go"), IsTrue)
    95  }
    96  
    97  func (s *testLogSuite) TestSlowQueryLogger(c *C) {
    98  	fileName := "slow_query"
    99  	os.Remove(fileName)
   100  	conf := NewLogConfig("info", DefaultLogFormat, fileName, NewFileLogConfig(DefaultLogMaxSize), false)
   101  	c.Assert(conf.File.MaxSize, Equals, DefaultLogMaxSize)
   102  	err := InitLogger(conf)
   103  	c.Assert(err, IsNil)
   104  	defer os.Remove(fileName)
   105  
   106  	SlowQueryLogger.Debug("debug message")
   107  	SlowQueryLogger.Info("info message")
   108  	SlowQueryLogger.Warn("warn message")
   109  	SlowQueryLogger.Error("error message")
   110  	c.Assert(s.buf.Len(), Equals, 0)
   111  
   112  	f, err := os.Open(fileName)
   113  	c.Assert(err, IsNil)
   114  	defer f.Close()
   115  
   116  	r := bufio.NewReader(f)
   117  	for {
   118  		var str string
   119  		str, err = r.ReadString('\n')
   120  		if err != nil {
   121  			break
   122  		}
   123  		if strings.HasPrefix(str, "# ") {
   124  			c.Assert(str, Matches, `# Time: .*?\n`)
   125  		} else {
   126  			c.Assert(str, Matches, `.*? message\n`)
   127  		}
   128  	}
   129  	c.Assert(err, Equals, io.EOF)
   130  }
   131  
   132  func (s *testLogSuite) TestLoggerKeepOrder(c *C) {
   133  	conf := NewLogConfig("warn", DefaultLogFormat, "", EmptyFileLogConfig, true)
   134  	c.Assert(InitLogger(conf), IsNil)
   135  	logger := log.StandardLogger()
   136  	ft, ok := logger.Formatter.(*textFormatter)
   137  	c.Assert(ok, IsTrue)
   138  	ft.EnableEntryOrder = true
   139  	logger.Out = s.buf
   140  	logEntry := log.NewEntry(logger)
   141  	logEntry.Data = log.Fields{
   142  		"connectionId": 1,
   143  		"costTime":     "1",
   144  		"database":     "test",
   145  		"allegrosql":          "select 1",
   146  		"txnStartTS":   1,
   147  	}
   148  
   149  	_, _, line, _ := runtime.Caller(0)
   150  	logEntry.WithField("type", "slow-query").WithField("succ", true).Warnf("slow-query")
   151  	expectMsg := fmt.Sprintf("log_test.go:%v: [warning] slow-query connectionId=1 costTime=1 database=test allegrosql=select 1 succ=true txnStartTS=1 type=slow-query\n", line+1)
   152  	c.Assert(s.buf.String(), Equals, expectMsg)
   153  
   154  	s.buf.Reset()
   155  	logEntry.Data = log.Fields{
   156  		"a": "a",
   157  		"d": "d",
   158  		"e": "e",
   159  		"b": "b",
   160  		"f": "f",
   161  		"c": "c",
   162  	}
   163  
   164  	_, _, line, _ = runtime.Caller(0)
   165  	logEntry.Warnf("slow-query")
   166  	expectMsg = fmt.Sprintf("log_test.go:%v: [warning] slow-query a=a b=b c=c d=d e=e f=f\n", line+1)
   167  	c.Assert(s.buf.String(), Equals, expectMsg)
   168  }
   169  
   170  func (s *testLogSuite) TestSlowQueryZapLogger(c *C) {
   171  	if runtime.GOOS == "windows" {
   172  		// Skip this test on windows for two reasons:
   173  		// 1. The pattern match fails somehow. It seems windows treat \n as slash and character n.
   174  		// 2. Remove file doesn't work as long as the log instance hold the file.
   175  		c.Skip("skip on windows")
   176  	}
   177  
   178  	fileName := "slow_query"
   179  	conf := NewLogConfig("info", DefaultLogFormat, fileName, EmptyFileLogConfig, false)
   180  	err := InitZapLogger(conf)
   181  	c.Assert(err, IsNil)
   182  	defer os.Remove(fileName)
   183  
   184  	SlowQueryZapLogger.Debug("debug message", zap.String("str key", "val"))
   185  	SlowQueryZapLogger.Info("info message", zap.String("str key", "val"))
   186  	SlowQueryZapLogger.Warn("warn", zap.Int("int key", 123))
   187  	SlowQueryZapLogger.Error("error message", zap.Bool("bool key", true))
   188  
   189  	f, err := os.Open(fileName)
   190  	c.Assert(err, IsNil)
   191  	defer f.Close()
   192  
   193  	r := bufio.NewReader(f)
   194  	for {
   195  		var str string
   196  		str, err = r.ReadString('\n')
   197  		if err != nil {
   198  			break
   199  		}
   200  		c.Assert(str, Matches, zapLogPattern)
   201  	}
   202  	c.Assert(err, Equals, io.EOF)
   203  
   204  }
   205  
   206  func (s *testLogSuite) TestZapLoggerWithKeys(c *C) {
   207  	if runtime.GOOS == "windows" {
   208  		// Skip this test on windows for two reason:
   209  		// 1. The pattern match fails somehow. It seems windows treat \n as slash and character n.
   210  		// 2. Remove file doesn't work as long as the log instance hold the file.
   211  		c.Skip("skip on windows")
   212  	}
   213  
   214  	fileCfg := FileLogConfig{zaplog.FileLogConfig{Filename: "zap_log", MaxSize: 4096}}
   215  	conf := NewLogConfig("info", DefaultLogFormat, "", fileCfg, false)
   216  	err := InitZapLogger(conf)
   217  	c.Assert(err, IsNil)
   218  	connID := uint32(123)
   219  	ctx := WithConnID(context.Background(), connID)
   220  	s.testZapLogger(ctx, c, fileCfg.Filename, zapLogWithConnIDPattern)
   221  	os.Remove(fileCfg.Filename)
   222  
   223  	err = InitZapLogger(conf)
   224  	c.Assert(err, IsNil)
   225  	key := "ctxKey"
   226  	val := "ctxValue"
   227  	ctx1 := WithKeyValue(context.Background(), key, val)
   228  	s.testZapLogger(ctx1, c, fileCfg.Filename, zapLogWithKeyValPattern)
   229  	os.Remove(fileCfg.Filename)
   230  }
   231  
   232  func (s *testLogSuite) testZapLogger(ctx context.Context, c *C, fileName, pattern string) {
   233  	Logger(ctx).Debug("debug msg", zap.String("test with key", "true"))
   234  	Logger(ctx).Info("info msg", zap.String("test with key", "true"))
   235  	Logger(ctx).Warn("warn msg", zap.String("test with key", "true"))
   236  	Logger(ctx).Error("error msg", zap.String("test with key", "true"))
   237  
   238  	f, err := os.Open(fileName)
   239  	c.Assert(err, IsNil)
   240  	defer f.Close()
   241  
   242  	r := bufio.NewReader(f)
   243  	for {
   244  		var str string
   245  		str, err = r.ReadString('\n')
   246  		if err != nil {
   247  			break
   248  		}
   249  		c.Assert(str, Matches, pattern)
   250  		c.Assert(strings.Contains(str, "stack"), IsFalse)
   251  		c.Assert(strings.Contains(str, "errorVerbose"), IsFalse)
   252  	}
   253  	c.Assert(err, Equals, io.EOF)
   254  }
   255  
   256  func (s *testLogSuite) TestSetLevel(c *C) {
   257  	conf := NewLogConfig("info", DefaultLogFormat, "", EmptyFileLogConfig, false)
   258  	err := InitZapLogger(conf)
   259  	c.Assert(err, IsNil)
   260  
   261  	c.Assert(zaplog.GetLevel(), Equals, zap.InfoLevel)
   262  	err = SetLevel("warn")
   263  	c.Assert(err, IsNil)
   264  	c.Assert(zaplog.GetLevel(), Equals, zap.WarnLevel)
   265  	err = SetLevel("Error")
   266  	c.Assert(err, IsNil)
   267  	c.Assert(zaplog.GetLevel(), Equals, zap.ErrorLevel)
   268  	err = SetLevel("DEBUG")
   269  	c.Assert(err, IsNil)
   270  	c.Assert(zaplog.GetLevel(), Equals, zap.DebugLevel)
   271  }