github.com/shuguocloud/go-zero@v1.3.0/core/logx/logs_test.go (about)

     1  package logx
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"log"
    10  	"os"
    11  	"runtime"
    12  	"strings"
    13  	"sync"
    14  	"sync/atomic"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/stretchr/testify/assert"
    19  )
    20  
    21  var (
    22  	s    = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection")
    23  	pool = make(chan []byte, 1)
    24  )
    25  
    26  type mockWriter struct {
    27  	lock    sync.Mutex
    28  	builder strings.Builder
    29  }
    30  
    31  func (mw *mockWriter) Write(data []byte) (int, error) {
    32  	mw.lock.Lock()
    33  	defer mw.lock.Unlock()
    34  	return mw.builder.Write(data)
    35  }
    36  
    37  func (mw *mockWriter) Close() error {
    38  	return nil
    39  }
    40  
    41  func (mw *mockWriter) Contains(text string) bool {
    42  	mw.lock.Lock()
    43  	defer mw.lock.Unlock()
    44  	return strings.Contains(mw.builder.String(), text)
    45  }
    46  
    47  func (mw *mockWriter) Reset() {
    48  	mw.lock.Lock()
    49  	defer mw.lock.Unlock()
    50  	mw.builder.Reset()
    51  }
    52  
    53  func (mw *mockWriter) String() string {
    54  	mw.lock.Lock()
    55  	defer mw.lock.Unlock()
    56  	return mw.builder.String()
    57  }
    58  
    59  func TestFileLineFileMode(t *testing.T) {
    60  	writer := new(mockWriter)
    61  	errorLog = writer
    62  	atomic.StoreUint32(&initialized, 1)
    63  	file, line := getFileLine()
    64  	Error("anything")
    65  	assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
    66  
    67  	writer.Reset()
    68  	file, line = getFileLine()
    69  	Errorf("anything %s", "format")
    70  	assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
    71  }
    72  
    73  func TestFileLineConsoleMode(t *testing.T) {
    74  	writer := new(mockWriter)
    75  	writeConsole = true
    76  	errorLog = newLogWriter(log.New(writer, "[ERROR] ", flags))
    77  	atomic.StoreUint32(&initialized, 1)
    78  	file, line := getFileLine()
    79  	Error("anything")
    80  	assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
    81  
    82  	writer.Reset()
    83  	file, line = getFileLine()
    84  	Errorf("anything %s", "format")
    85  	assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
    86  }
    87  
    88  func TestStructedLogAlert(t *testing.T) {
    89  	doTestStructedLog(t, levelAlert, func(writer io.WriteCloser) {
    90  		errorLog = writer
    91  	}, func(v ...interface{}) {
    92  		Alert(fmt.Sprint(v...))
    93  	})
    94  }
    95  
    96  func TestStructedLogError(t *testing.T) {
    97  	doTestStructedLog(t, levelError, func(writer io.WriteCloser) {
    98  		errorLog = writer
    99  	}, func(v ...interface{}) {
   100  		Error(v...)
   101  	})
   102  }
   103  
   104  func TestStructedLogErrorf(t *testing.T) {
   105  	doTestStructedLog(t, levelError, func(writer io.WriteCloser) {
   106  		errorLog = writer
   107  	}, func(v ...interface{}) {
   108  		Errorf("%s", fmt.Sprint(v...))
   109  	})
   110  }
   111  
   112  func TestStructedLogErrorv(t *testing.T) {
   113  	doTestStructedLog(t, levelError, func(writer io.WriteCloser) {
   114  		errorLog = writer
   115  	}, func(v ...interface{}) {
   116  		Errorv(fmt.Sprint(v...))
   117  	})
   118  }
   119  
   120  func TestStructedLogInfo(t *testing.T) {
   121  	doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
   122  		infoLog = writer
   123  	}, func(v ...interface{}) {
   124  		Info(v...)
   125  	})
   126  }
   127  
   128  func TestStructedLogInfof(t *testing.T) {
   129  	doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
   130  		infoLog = writer
   131  	}, func(v ...interface{}) {
   132  		Infof("%s", fmt.Sprint(v...))
   133  	})
   134  }
   135  
   136  func TestStructedLogInfov(t *testing.T) {
   137  	doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
   138  		infoLog = writer
   139  	}, func(v ...interface{}) {
   140  		Infov(fmt.Sprint(v...))
   141  	})
   142  }
   143  
   144  func TestStructedLogInfoConsoleAny(t *testing.T) {
   145  	doTestStructedLogConsole(t, func(writer io.WriteCloser) {
   146  		infoLog = writer
   147  	}, func(v ...interface{}) {
   148  		old := encoding
   149  		encoding = plainEncodingType
   150  		defer func() {
   151  			encoding = old
   152  		}()
   153  
   154  		Infov(v)
   155  	})
   156  }
   157  
   158  func TestStructedLogInfoConsoleAnyString(t *testing.T) {
   159  	doTestStructedLogConsole(t, func(writer io.WriteCloser) {
   160  		infoLog = writer
   161  	}, func(v ...interface{}) {
   162  		old := encoding
   163  		encoding = plainEncodingType
   164  		defer func() {
   165  			encoding = old
   166  		}()
   167  
   168  		Infov(fmt.Sprint(v...))
   169  	})
   170  }
   171  
   172  func TestStructedLogInfoConsoleAnyError(t *testing.T) {
   173  	doTestStructedLogConsole(t, func(writer io.WriteCloser) {
   174  		infoLog = writer
   175  	}, func(v ...interface{}) {
   176  		old := encoding
   177  		encoding = plainEncodingType
   178  		defer func() {
   179  			encoding = old
   180  		}()
   181  
   182  		Infov(errors.New(fmt.Sprint(v...)))
   183  	})
   184  }
   185  
   186  func TestStructedLogInfoConsoleAnyStringer(t *testing.T) {
   187  	doTestStructedLogConsole(t, func(writer io.WriteCloser) {
   188  		infoLog = writer
   189  	}, func(v ...interface{}) {
   190  		old := encoding
   191  		encoding = plainEncodingType
   192  		defer func() {
   193  			encoding = old
   194  		}()
   195  
   196  		Infov(ValStringer{
   197  			val: fmt.Sprint(v...),
   198  		})
   199  	})
   200  }
   201  
   202  func TestStructedLogInfoConsoleText(t *testing.T) {
   203  	doTestStructedLogConsole(t, func(writer io.WriteCloser) {
   204  		infoLog = writer
   205  	}, func(v ...interface{}) {
   206  		old := encoding
   207  		encoding = plainEncodingType
   208  		defer func() {
   209  			encoding = old
   210  		}()
   211  
   212  		Info(fmt.Sprint(v...))
   213  	})
   214  }
   215  
   216  func TestStructedLogSlow(t *testing.T) {
   217  	doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
   218  		slowLog = writer
   219  	}, func(v ...interface{}) {
   220  		Slow(v...)
   221  	})
   222  }
   223  
   224  func TestStructedLogSlowf(t *testing.T) {
   225  	doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
   226  		slowLog = writer
   227  	}, func(v ...interface{}) {
   228  		Slowf(fmt.Sprint(v...))
   229  	})
   230  }
   231  
   232  func TestStructedLogSlowv(t *testing.T) {
   233  	doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
   234  		slowLog = writer
   235  	}, func(v ...interface{}) {
   236  		Slowv(fmt.Sprint(v...))
   237  	})
   238  }
   239  
   240  func TestStructedLogStat(t *testing.T) {
   241  	doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
   242  		statLog = writer
   243  	}, func(v ...interface{}) {
   244  		Stat(v...)
   245  	})
   246  }
   247  
   248  func TestStructedLogStatf(t *testing.T) {
   249  	doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
   250  		statLog = writer
   251  	}, func(v ...interface{}) {
   252  		Statf(fmt.Sprint(v...))
   253  	})
   254  }
   255  
   256  func TestStructedLogSevere(t *testing.T) {
   257  	doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
   258  		severeLog = writer
   259  	}, func(v ...interface{}) {
   260  		Severe(v...)
   261  	})
   262  }
   263  
   264  func TestStructedLogSeveref(t *testing.T) {
   265  	doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
   266  		severeLog = writer
   267  	}, func(v ...interface{}) {
   268  		Severef(fmt.Sprint(v...))
   269  	})
   270  }
   271  
   272  func TestStructedLogWithDuration(t *testing.T) {
   273  	const message = "hello there"
   274  	writer := new(mockWriter)
   275  	infoLog = writer
   276  	atomic.StoreUint32(&initialized, 1)
   277  	WithDuration(time.Second).Info(message)
   278  	var entry logEntry
   279  	if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
   280  		t.Error(err)
   281  	}
   282  	assert.Equal(t, levelInfo, entry.Level)
   283  	assert.Equal(t, message, entry.Content)
   284  	assert.Equal(t, "1000.0ms", entry.Duration)
   285  }
   286  
   287  func TestSetLevel(t *testing.T) {
   288  	SetLevel(ErrorLevel)
   289  	const message = "hello there"
   290  	writer := new(mockWriter)
   291  	infoLog = writer
   292  	atomic.StoreUint32(&initialized, 1)
   293  	Info(message)
   294  	assert.Equal(t, 0, writer.builder.Len())
   295  }
   296  
   297  func TestSetLevelTwiceWithMode(t *testing.T) {
   298  	testModes := []string{
   299  		"mode",
   300  		"console",
   301  		"volumn",
   302  	}
   303  	for _, mode := range testModes {
   304  		testSetLevelTwiceWithMode(t, mode)
   305  	}
   306  }
   307  
   308  func TestSetLevelWithDuration(t *testing.T) {
   309  	SetLevel(ErrorLevel)
   310  	const message = "hello there"
   311  	writer := new(mockWriter)
   312  	infoLog = writer
   313  	atomic.StoreUint32(&initialized, 1)
   314  	WithDuration(time.Second).Info(message)
   315  	assert.Equal(t, 0, writer.builder.Len())
   316  }
   317  
   318  func TestErrorfWithWrappedError(t *testing.T) {
   319  	SetLevel(ErrorLevel)
   320  	const message = "there"
   321  	writer := new(mockWriter)
   322  	errorLog = writer
   323  	atomic.StoreUint32(&initialized, 1)
   324  	Errorf("hello %w", errors.New(message))
   325  	assert.True(t, strings.Contains(writer.builder.String(), "hello there"))
   326  }
   327  
   328  func TestMustNil(t *testing.T) {
   329  	Must(nil)
   330  }
   331  
   332  func TestSetup(t *testing.T) {
   333  	MustSetup(LogConf{
   334  		ServiceName: "any",
   335  		Mode:        "console",
   336  	})
   337  	MustSetup(LogConf{
   338  		ServiceName: "any",
   339  		Mode:        "file",
   340  		Path:        os.TempDir(),
   341  	})
   342  	MustSetup(LogConf{
   343  		ServiceName: "any",
   344  		Mode:        "volume",
   345  		Path:        os.TempDir(),
   346  	})
   347  	assert.NotNil(t, setupWithVolume(LogConf{}))
   348  	assert.NotNil(t, setupWithFiles(LogConf{}))
   349  	assert.Nil(t, setupWithFiles(LogConf{
   350  		ServiceName: "any",
   351  		Path:        os.TempDir(),
   352  		Compress:    true,
   353  		KeepDays:    1,
   354  	}))
   355  	setupLogLevel(LogConf{
   356  		Level: levelInfo,
   357  	})
   358  	setupLogLevel(LogConf{
   359  		Level: levelError,
   360  	})
   361  	setupLogLevel(LogConf{
   362  		Level: levelSevere,
   363  	})
   364  	_, err := createOutput("")
   365  	assert.NotNil(t, err)
   366  	Disable()
   367  }
   368  
   369  func TestDisable(t *testing.T) {
   370  	Disable()
   371  
   372  	var opt logOptions
   373  	WithKeepDays(1)(&opt)
   374  	WithGzip()(&opt)
   375  	assert.Nil(t, Close())
   376  	writeConsole = false
   377  	assert.Nil(t, Close())
   378  }
   379  
   380  func TestDisableStat(t *testing.T) {
   381  	DisableStat()
   382  
   383  	const message = "hello there"
   384  	writer := new(mockWriter)
   385  	statLog = writer
   386  	atomic.StoreUint32(&initialized, 1)
   387  	Stat(message)
   388  	assert.Equal(t, 0, writer.builder.Len())
   389  }
   390  
   391  func TestWithGzip(t *testing.T) {
   392  	fn := WithGzip()
   393  	var opt logOptions
   394  	fn(&opt)
   395  	assert.True(t, opt.gzipEnabled)
   396  }
   397  
   398  func TestWithKeepDays(t *testing.T) {
   399  	fn := WithKeepDays(1)
   400  	var opt logOptions
   401  	fn(&opt)
   402  	assert.Equal(t, 1, opt.keepDays)
   403  }
   404  
   405  func BenchmarkCopyByteSliceAppend(b *testing.B) {
   406  	for i := 0; i < b.N; i++ {
   407  		var buf []byte
   408  		buf = append(buf, getTimestamp()...)
   409  		buf = append(buf, ' ')
   410  		buf = append(buf, s...)
   411  		_ = buf
   412  	}
   413  }
   414  
   415  func BenchmarkCopyByteSliceAllocExactly(b *testing.B) {
   416  	for i := 0; i < b.N; i++ {
   417  		now := []byte(getTimestamp())
   418  		buf := make([]byte, len(now)+1+len(s))
   419  		n := copy(buf, now)
   420  		buf[n] = ' '
   421  		copy(buf[n+1:], s)
   422  	}
   423  }
   424  
   425  func BenchmarkCopyByteSlice(b *testing.B) {
   426  	var buf []byte
   427  	for i := 0; i < b.N; i++ {
   428  		buf = make([]byte, len(s))
   429  		copy(buf, s)
   430  	}
   431  	fmt.Fprint(ioutil.Discard, buf)
   432  }
   433  
   434  func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
   435  	var buf []byte
   436  	for i := 0; i < b.N; i++ {
   437  		size := len(s)
   438  		buf = s[:size:size]
   439  	}
   440  	fmt.Fprint(ioutil.Discard, buf)
   441  }
   442  
   443  func BenchmarkCacheByteSlice(b *testing.B) {
   444  	for i := 0; i < b.N; i++ {
   445  		dup := fetch()
   446  		copy(dup, s)
   447  		put(dup)
   448  	}
   449  }
   450  
   451  func BenchmarkLogs(b *testing.B) {
   452  	b.ReportAllocs()
   453  
   454  	log.SetOutput(ioutil.Discard)
   455  	for i := 0; i < b.N; i++ {
   456  		Info(i)
   457  	}
   458  }
   459  
   460  func fetch() []byte {
   461  	select {
   462  	case b := <-pool:
   463  		return b
   464  	default:
   465  	}
   466  	return make([]byte, 4096)
   467  }
   468  
   469  func getFileLine() (string, int) {
   470  	_, file, line, _ := runtime.Caller(1)
   471  	short := file
   472  
   473  	for i := len(file) - 1; i > 0; i-- {
   474  		if file[i] == '/' {
   475  			short = file[i+1:]
   476  			break
   477  		}
   478  	}
   479  
   480  	return short, line
   481  }
   482  
   483  func put(b []byte) {
   484  	select {
   485  	case pool <- b:
   486  	default:
   487  	}
   488  }
   489  
   490  func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteCloser),
   491  	write func(...interface{})) {
   492  	const message = "hello there"
   493  	writer := new(mockWriter)
   494  	setup(writer)
   495  	atomic.StoreUint32(&initialized, 1)
   496  	write(message)
   497  	var entry logEntry
   498  	if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
   499  		t.Error(err)
   500  	}
   501  	assert.Equal(t, level, entry.Level)
   502  	val, ok := entry.Content.(string)
   503  	assert.True(t, ok)
   504  	assert.True(t, strings.Contains(val, message))
   505  }
   506  
   507  func doTestStructedLogConsole(t *testing.T, setup func(writer io.WriteCloser),
   508  	write func(...interface{})) {
   509  	const message = "hello there"
   510  	writer := new(mockWriter)
   511  	setup(writer)
   512  	atomic.StoreUint32(&initialized, 1)
   513  	write(message)
   514  	println(writer.String())
   515  	assert.True(t, strings.Contains(writer.String(), message))
   516  }
   517  
   518  func testSetLevelTwiceWithMode(t *testing.T, mode string) {
   519  	SetUp(LogConf{
   520  		Mode:  mode,
   521  		Level: "error",
   522  		Path:  "/dev/null",
   523  	})
   524  	SetUp(LogConf{
   525  		Mode:  mode,
   526  		Level: "info",
   527  		Path:  "/dev/null",
   528  	})
   529  	const message = "hello there"
   530  	writer := new(mockWriter)
   531  	infoLog = writer
   532  	atomic.StoreUint32(&initialized, 1)
   533  	Info(message)
   534  	assert.Equal(t, 0, writer.builder.Len())
   535  	Infof(message)
   536  	assert.Equal(t, 0, writer.builder.Len())
   537  	ErrorStack(message)
   538  	assert.Equal(t, 0, writer.builder.Len())
   539  	ErrorStackf(message)
   540  	assert.Equal(t, 0, writer.builder.Len())
   541  }
   542  
   543  type ValStringer struct {
   544  	val string
   545  }
   546  
   547  func (v ValStringer) String() string {
   548  	return v.val
   549  }