123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- package logx
- import (
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "log"
- "os"
- "runtime"
- "strings"
- "sync"
- "sync/atomic"
- "testing"
- "time"
- "github.com/stretchr/testify/assert"
- )
- var (
- s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection")
- pool = make(chan []byte, 1)
- )
- type mockWriter struct {
- lock sync.Mutex
- builder strings.Builder
- }
- func (mw *mockWriter) Write(data []byte) (int, error) {
- mw.lock.Lock()
- defer mw.lock.Unlock()
- return mw.builder.Write(data)
- }
- func (mw *mockWriter) Close() error {
- return nil
- }
- func (mw *mockWriter) Contains(text string) bool {
- mw.lock.Lock()
- defer mw.lock.Unlock()
- return strings.Contains(mw.builder.String(), text)
- }
- func (mw *mockWriter) Reset() {
- mw.lock.Lock()
- defer mw.lock.Unlock()
- mw.builder.Reset()
- }
- func (mw *mockWriter) String() string {
- mw.lock.Lock()
- defer mw.lock.Unlock()
- return mw.builder.String()
- }
- func TestFileLineFileMode(t *testing.T) {
- writer := new(mockWriter)
- errorLog = writer
- atomic.StoreUint32(&initialized, 1)
- file, line := getFileLine()
- Error("anything")
- assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
- writer.Reset()
- file, line = getFileLine()
- Errorf("anything %s", "format")
- assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
- }
- func TestFileLineConsoleMode(t *testing.T) {
- writer := new(mockWriter)
- writeConsole = true
- errorLog = newLogWriter(log.New(writer, "[ERROR] ", flags))
- atomic.StoreUint32(&initialized, 1)
- file, line := getFileLine()
- Error("anything")
- assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
- writer.Reset()
- file, line = getFileLine()
- Errorf("anything %s", "format")
- assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
- }
- func TestStructedLogAlert(t *testing.T) {
- doTestStructedLog(t, levelAlert, func(writer io.WriteCloser) {
- errorLog = writer
- }, func(v ...interface{}) {
- Alert(fmt.Sprint(v...))
- })
- }
- func TestStructedLogInfo(t *testing.T) {
- doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
- infoLog = writer
- }, func(v ...interface{}) {
- Info(v...)
- })
- }
- func TestStructedLogSlow(t *testing.T) {
- doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
- slowLog = writer
- }, func(v ...interface{}) {
- Slow(v...)
- })
- }
- func TestStructedLogSlowf(t *testing.T) {
- doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
- slowLog = writer
- }, func(v ...interface{}) {
- Slowf(fmt.Sprint(v...))
- })
- }
- func TestStructedLogStat(t *testing.T) {
- doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
- statLog = writer
- }, func(v ...interface{}) {
- Stat(v...)
- })
- }
- func TestStructedLogStatf(t *testing.T) {
- doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
- statLog = writer
- }, func(v ...interface{}) {
- Statf(fmt.Sprint(v...))
- })
- }
- func TestStructedLogSevere(t *testing.T) {
- doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
- severeLog = writer
- }, func(v ...interface{}) {
- Severe(v...)
- })
- }
- func TestStructedLogSeveref(t *testing.T) {
- doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
- severeLog = writer
- }, func(v ...interface{}) {
- Severef(fmt.Sprint(v...))
- })
- }
- func TestStructedLogWithDuration(t *testing.T) {
- const message = "hello there"
- writer := new(mockWriter)
- infoLog = writer
- atomic.StoreUint32(&initialized, 1)
- WithDuration(time.Second).Info(message)
- var entry logEntry
- if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
- t.Error(err)
- }
- assert.Equal(t, levelInfo, entry.Level)
- assert.Equal(t, message, entry.Content)
- assert.Equal(t, "1000.0ms", entry.Duration)
- }
- func TestSetLevel(t *testing.T) {
- SetLevel(ErrorLevel)
- const message = "hello there"
- writer := new(mockWriter)
- infoLog = writer
- atomic.StoreUint32(&initialized, 1)
- Info(message)
- assert.Equal(t, 0, writer.builder.Len())
- }
- func TestSetLevelTwiceWithMode(t *testing.T) {
- testModes := []string{
- "mode",
- "console",
- "volumn",
- }
- for _, mode := range testModes {
- testSetLevelTwiceWithMode(t, mode)
- }
- }
- func TestSetLevelWithDuration(t *testing.T) {
- SetLevel(ErrorLevel)
- const message = "hello there"
- writer := new(mockWriter)
- infoLog = writer
- atomic.StoreUint32(&initialized, 1)
- WithDuration(time.Second).Info(message)
- assert.Equal(t, 0, writer.builder.Len())
- }
- func TestMustNil(t *testing.T) {
- Must(nil)
- }
- func TestSetup(t *testing.T) {
- MustSetup(LogConf{
- ServiceName: "any",
- Mode: "console",
- })
- MustSetup(LogConf{
- ServiceName: "any",
- Mode: "file",
- Path: os.TempDir(),
- })
- MustSetup(LogConf{
- ServiceName: "any",
- Mode: "volume",
- Path: os.TempDir(),
- })
- assert.NotNil(t, setupWithVolume(LogConf{}))
- assert.NotNil(t, setupWithFiles(LogConf{}))
- assert.Nil(t, setupWithFiles(LogConf{
- ServiceName: "any",
- Path: os.TempDir(),
- Compress: true,
- KeepDays: 1,
- }))
- setupLogLevel(LogConf{
- Level: levelInfo,
- })
- setupLogLevel(LogConf{
- Level: levelError,
- })
- setupLogLevel(LogConf{
- Level: levelSevere,
- })
- _, err := createOutput("")
- assert.NotNil(t, err)
- Disable()
- }
- func TestDisable(t *testing.T) {
- Disable()
- var opt logOptions
- WithKeepDays(1)(&opt)
- WithGzip()(&opt)
- assert.Nil(t, Close())
- writeConsole = false
- assert.Nil(t, Close())
- }
- func TestWithGzip(t *testing.T) {
- fn := WithGzip()
- var opt logOptions
- fn(&opt)
- assert.True(t, opt.gzipEnabled)
- }
- func TestWithKeepDays(t *testing.T) {
- fn := WithKeepDays(1)
- var opt logOptions
- fn(&opt)
- assert.Equal(t, 1, opt.keepDays)
- }
- func BenchmarkCopyByteSliceAppend(b *testing.B) {
- for i := 0; i < b.N; i++ {
- var buf []byte
- buf = append(buf, getTimestamp()...)
- buf = append(buf, ' ')
- buf = append(buf, s...)
- _ = buf
- }
- }
- func BenchmarkCopyByteSliceAllocExactly(b *testing.B) {
- for i := 0; i < b.N; i++ {
- now := []byte(getTimestamp())
- buf := make([]byte, len(now)+1+len(s))
- n := copy(buf, now)
- buf[n] = ' '
- copy(buf[n+1:], s)
- }
- }
- func BenchmarkCopyByteSlice(b *testing.B) {
- var buf []byte
- for i := 0; i < b.N; i++ {
- buf = make([]byte, len(s))
- copy(buf, s)
- }
- fmt.Fprint(ioutil.Discard, buf)
- }
- func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
- var buf []byte
- for i := 0; i < b.N; i++ {
- size := len(s)
- buf = s[:size:size]
- }
- fmt.Fprint(ioutil.Discard, buf)
- }
- func BenchmarkCacheByteSlice(b *testing.B) {
- for i := 0; i < b.N; i++ {
- dup := fetch()
- copy(dup, s)
- put(dup)
- }
- }
- func BenchmarkLogs(b *testing.B) {
- b.ReportAllocs()
- log.SetOutput(ioutil.Discard)
- for i := 0; i < b.N; i++ {
- Info(i)
- }
- }
- func fetch() []byte {
- select {
- case b := <-pool:
- return b
- default:
- }
- return make([]byte, 4096)
- }
- func getFileLine() (string, int) {
- _, file, line, _ := runtime.Caller(1)
- short := file
- for i := len(file) - 1; i > 0; i-- {
- if file[i] == '/' {
- short = file[i+1:]
- break
- }
- }
- return short, line
- }
- func put(b []byte) {
- select {
- case pool <- b:
- default:
- }
- }
- func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteCloser),
- write func(...interface{})) {
- const message = "hello there"
- writer := new(mockWriter)
- setup(writer)
- atomic.StoreUint32(&initialized, 1)
- write(message)
- var entry logEntry
- if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
- t.Error(err)
- }
- assert.Equal(t, level, entry.Level)
- assert.True(t, strings.Contains(entry.Content, message))
- }
- func testSetLevelTwiceWithMode(t *testing.T, mode string) {
- SetUp(LogConf{
- Mode: mode,
- Level: "error",
- Path: "/dev/null",
- })
- SetUp(LogConf{
- Mode: mode,
- Level: "info",
- Path: "/dev/null",
- })
- const message = "hello there"
- writer := new(mockWriter)
- infoLog = writer
- atomic.StoreUint32(&initialized, 1)
- Info(message)
- assert.Equal(t, 0, writer.builder.Len())
- Infof(message)
- assert.Equal(t, 0, writer.builder.Len())
- ErrorStack(message)
- assert.Equal(t, 0, writer.builder.Len())
- ErrorStackf(message)
- assert.Equal(t, 0, writer.builder.Len())
- }
|