feat: add fields with logx methods, support using third party logging libs. (#1847)

* backup

* simplify

* chore: remove unused pool

* chore: fix lint errors

* chore: use strings.Builder instead of bytes.Buffer

* test: add more tests

* chore: fix reviewdog

* test: fix data race

* feat: make logger customizable

* chore: fix reviewdog

* test: fix fails

* chore: fix set writer twice

* chore: use context instead of golang.org context

* chore: specify uint32 for level types
This commit is contained in:
Kevin Wan
2022-05-03 17:34:26 +08:00
committed by GitHub
parent ae2c76765c
commit ac321fc146
21 changed files with 1551 additions and 792 deletions

View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
@@ -19,8 +18,9 @@ import (
)
var (
s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection")
pool = make(chan []byte, 1)
s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection")
pool = make(chan []byte, 1)
_ Writer = (*mockWriter)(nil)
)
type mockWriter struct {
@@ -28,10 +28,46 @@ type mockWriter struct {
builder strings.Builder
}
func (mw *mockWriter) Write(data []byte) (int, error) {
func (mw *mockWriter) Alert(v interface{}) {
mw.lock.Lock()
defer mw.lock.Unlock()
return mw.builder.Write(data)
output(&mw.builder, levelAlert, v)
}
func (mw *mockWriter) Error(v interface{}, fields ...LogField) {
mw.lock.Lock()
defer mw.lock.Unlock()
output(&mw.builder, levelError, v, fields...)
}
func (mw *mockWriter) Info(v interface{}, fields ...LogField) {
mw.lock.Lock()
defer mw.lock.Unlock()
output(&mw.builder, levelInfo, v, fields...)
}
func (mw *mockWriter) Severe(v interface{}) {
mw.lock.Lock()
defer mw.lock.Unlock()
output(&mw.builder, levelSevere, v)
}
func (mw *mockWriter) Slow(v interface{}, fields ...LogField) {
mw.lock.Lock()
defer mw.lock.Unlock()
output(&mw.builder, levelSlow, v, fields...)
}
func (mw *mockWriter) Stack(v interface{}) {
mw.lock.Lock()
defer mw.lock.Unlock()
output(&mw.builder, levelError, v)
}
func (mw *mockWriter) Stat(v interface{}, fields ...LogField) {
mw.lock.Lock()
defer mw.lock.Unlock()
output(&mw.builder, levelStat, v, fields...)
}
func (mw *mockWriter) Close() error {
@@ -57,94 +93,130 @@ func (mw *mockWriter) String() string {
}
func TestFileLineFileMode(t *testing.T) {
writer := new(mockWriter)
errorLog = writer
atomic.StoreUint32(&initialized, 1)
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
file, line := getFileLine()
Error("anything")
assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
assert.True(t, w.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)))
assert.True(t, w.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)
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
file, line := getFileLine()
Error("anything")
assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
writer.Reset()
w.Reset()
file, line = getFileLine()
Errorf("anything %s", "format")
assert.True(t, writer.Contains(fmt.Sprintf("%s:%d", file, line+1)))
assert.True(t, w.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{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelAlert, w, func(v ...interface{}) {
Alert(fmt.Sprint(v...))
})
}
func TestStructedLogError(t *testing.T) {
doTestStructedLog(t, levelError, func(writer io.WriteCloser) {
errorLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) {
Error(v...)
})
}
func TestStructedLogErrorf(t *testing.T) {
doTestStructedLog(t, levelError, func(writer io.WriteCloser) {
errorLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) {
Errorf("%s", fmt.Sprint(v...))
})
}
func TestStructedLogErrorv(t *testing.T) {
doTestStructedLog(t, levelError, func(writer io.WriteCloser) {
errorLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) {
Errorv(fmt.Sprint(v...))
})
}
func TestStructedLogErrorw(t *testing.T) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelError, w, func(v ...interface{}) {
Errorw(fmt.Sprint(v...), Field("foo", "bar"))
})
}
func TestStructedLogInfo(t *testing.T) {
doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
Info(v...)
})
}
func TestStructedLogInfof(t *testing.T) {
doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
Infof("%s", fmt.Sprint(v...))
})
}
func TestStructedLogInfov(t *testing.T) {
doTestStructedLog(t, levelInfo, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
Infov(fmt.Sprint(v...))
})
}
func TestStructedLogInfow(t *testing.T) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelInfo, w, func(v ...interface{}) {
Infow(fmt.Sprint(v...), Field("foo", "bar"))
})
}
func TestStructedLogInfoConsoleAny(t *testing.T) {
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) {
old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType)
defer func() {
@@ -156,9 +228,11 @@ func TestStructedLogInfoConsoleAny(t *testing.T) {
}
func TestStructedLogInfoConsoleAnyString(t *testing.T) {
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) {
old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType)
defer func() {
@@ -170,9 +244,11 @@ func TestStructedLogInfoConsoleAnyString(t *testing.T) {
}
func TestStructedLogInfoConsoleAnyError(t *testing.T) {
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) {
old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType)
defer func() {
@@ -184,9 +260,11 @@ func TestStructedLogInfoConsoleAnyError(t *testing.T) {
}
func TestStructedLogInfoConsoleAnyStringer(t *testing.T) {
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) {
old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType)
defer func() {
@@ -200,9 +278,11 @@ func TestStructedLogInfoConsoleAnyStringer(t *testing.T) {
}
func TestStructedLogInfoConsoleText(t *testing.T) {
doTestStructedLogConsole(t, func(writer io.WriteCloser) {
infoLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLogConsole(t, w, func(v ...interface{}) {
old := atomic.LoadUint32(&encoding)
atomic.StoreUint32(&encoding, plainEncodingType)
defer func() {
@@ -214,69 +294,94 @@ func TestStructedLogInfoConsoleText(t *testing.T) {
}
func TestStructedLogSlow(t *testing.T) {
doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
slowLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
Slow(v...)
})
}
func TestStructedLogSlowf(t *testing.T) {
doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
slowLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
Slowf(fmt.Sprint(v...))
})
}
func TestStructedLogSlowv(t *testing.T) {
doTestStructedLog(t, levelSlow, func(writer io.WriteCloser) {
slowLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
Slowv(fmt.Sprint(v...))
})
}
func TestStructedLogSloww(t *testing.T) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelSlow, w, func(v ...interface{}) {
Sloww(fmt.Sprint(v...), Field("foo", time.Second))
})
}
func TestStructedLogStat(t *testing.T) {
doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
statLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelStat, w, func(v ...interface{}) {
Stat(v...)
})
}
func TestStructedLogStatf(t *testing.T) {
doTestStructedLog(t, levelStat, func(writer io.WriteCloser) {
statLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelStat, w, func(v ...interface{}) {
Statf(fmt.Sprint(v...))
})
}
func TestStructedLogSevere(t *testing.T) {
doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
severeLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelSevere, w, func(v ...interface{}) {
Severe(v...)
})
}
func TestStructedLogSeveref(t *testing.T) {
doTestStructedLog(t, levelSevere, func(writer io.WriteCloser) {
severeLog = writer
}, func(v ...interface{}) {
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
doTestStructedLog(t, levelSevere, w, 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)
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
WithDuration(time.Second).Info(message)
var entry logEntry
if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
if err := json.Unmarshal([]byte(w.builder.String()), &entry); err != nil {
t.Error(err)
}
assert.Equal(t, levelInfo, entry.Level)
@@ -287,11 +392,12 @@ func TestStructedLogWithDuration(t *testing.T) {
func TestSetLevel(t *testing.T) {
SetLevel(ErrorLevel)
const message = "hello there"
writer := new(mockWriter)
infoLog = writer
atomic.StoreUint32(&initialized, 1)
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
Info(message)
assert.Equal(t, 0, writer.builder.Len())
assert.Equal(t, 0, w.builder.Len())
}
func TestSetLevelTwiceWithMode(t *testing.T) {
@@ -300,29 +406,35 @@ func TestSetLevelTwiceWithMode(t *testing.T) {
"console",
"volumn",
}
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
for _, mode := range testModes {
testSetLevelTwiceWithMode(t, mode)
testSetLevelTwiceWithMode(t, mode, w)
}
}
func TestSetLevelWithDuration(t *testing.T) {
SetLevel(ErrorLevel)
const message = "hello there"
writer := new(mockWriter)
infoLog = writer
atomic.StoreUint32(&initialized, 1)
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
WithDuration(time.Second).Info(message)
assert.Equal(t, 0, writer.builder.Len())
assert.Equal(t, 0, w.builder.Len())
}
func TestErrorfWithWrappedError(t *testing.T) {
SetLevel(ErrorLevel)
const message = "there"
writer := new(mockWriter)
errorLog = writer
atomic.StoreUint32(&initialized, 1)
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
Errorf("hello %w", errors.New(message))
assert.True(t, strings.Contains(writer.builder.String(), "hello there"))
assert.True(t, strings.Contains(w.builder.String(), "hello there"))
}
func TestMustNil(t *testing.T) {
@@ -330,6 +442,11 @@ func TestMustNil(t *testing.T) {
}
func TestSetup(t *testing.T) {
defer func() {
SetLevel(InfoLevel)
atomic.StoreUint32(&encoding, jsonEncodingType)
}()
MustSetup(LogConf{
ServiceName: "any",
Mode: "console",
@@ -344,6 +461,17 @@ func TestSetup(t *testing.T) {
Mode: "volume",
Path: os.TempDir(),
})
MustSetup(LogConf{
ServiceName: "any",
Mode: "console",
TimeFormat: timeFormat,
})
MustSetup(LogConf{
ServiceName: "any",
Mode: "console",
Encoding: plainEncoding,
})
assert.NotNil(t, setupWithVolume(LogConf{}))
assert.NotNil(t, setupWithFiles(LogConf{}))
assert.Nil(t, setupWithFiles(LogConf{
@@ -364,6 +492,8 @@ func TestSetup(t *testing.T) {
_, err := createOutput("")
assert.NotNil(t, err)
Disable()
SetLevel(InfoLevel)
atomic.StoreUint32(&encoding, jsonEncodingType)
}
func TestDisable(t *testing.T) {
@@ -373,7 +503,6 @@ func TestDisable(t *testing.T) {
WithKeepDays(1)(&opt)
WithGzip()(&opt)
assert.Nil(t, Close())
writeConsole = false
assert.Nil(t, Close())
}
@@ -381,11 +510,20 @@ func TestDisableStat(t *testing.T) {
DisableStat()
const message = "hello there"
writer := new(mockWriter)
statLog = writer
atomic.StoreUint32(&initialized, 1)
w := new(mockWriter)
old := writer.Swap(w)
defer writer.Store(old)
Stat(message)
assert.Equal(t, 0, writer.builder.Len())
assert.Equal(t, 0, w.builder.Len())
}
func TestSetWriter(t *testing.T) {
Reset()
SetWriter(nopWriter{})
assert.NotNil(t, writer.Load())
assert.True(t, writer.Load() == nopWriter{})
SetWriter(new(mockWriter))
assert.True(t, writer.Load() == nopWriter{})
}
func TestWithGzip(t *testing.T) {
@@ -487,15 +625,12 @@ func put(b []byte) {
}
}
func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteCloser),
write func(...interface{})) {
func doTestStructedLog(t *testing.T, level string, w *mockWriter, write func(...interface{})) {
const message = "hello there"
writer := new(mockWriter)
setup(writer)
atomic.StoreUint32(&initialized, 1)
write(message)
fmt.Println(w.String())
var entry logEntry
if err := json.Unmarshal([]byte(writer.builder.String()), &entry); err != nil {
if err := json.Unmarshal([]byte(w.String()), &entry); err != nil {
t.Error(err)
}
assert.Equal(t, level, entry.Level)
@@ -504,18 +639,14 @@ func doTestStructedLog(t *testing.T, level string, setup func(writer io.WriteClo
assert.True(t, strings.Contains(val, message))
}
func doTestStructedLogConsole(t *testing.T, setup func(writer io.WriteCloser),
write func(...interface{})) {
func doTestStructedLogConsole(t *testing.T, w *mockWriter, write func(...interface{})) {
const message = "hello there"
writer := new(mockWriter)
setup(writer)
atomic.StoreUint32(&initialized, 1)
write(message)
println(writer.String())
assert.True(t, strings.Contains(writer.String(), message))
assert.True(t, strings.Contains(w.String(), message))
}
func testSetLevelTwiceWithMode(t *testing.T, mode string) {
func testSetLevelTwiceWithMode(t *testing.T, mode string, w *mockWriter) {
writer.Store(nil)
SetUp(LogConf{
Mode: mode,
Level: "error",
@@ -527,17 +658,14 @@ func testSetLevelTwiceWithMode(t *testing.T, mode string) {
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())
assert.Equal(t, 0, w.builder.Len())
Infof(message)
assert.Equal(t, 0, writer.builder.Len())
assert.Equal(t, 0, w.builder.Len())
ErrorStack(message)
assert.Equal(t, 0, writer.builder.Len())
assert.Equal(t, 0, w.builder.Len())
ErrorStackf(message)
assert.Equal(t, 0, writer.builder.Len())
assert.Equal(t, 0, w.builder.Len())
}
type ValStringer struct {