diff --git a/core/logx/logs.go b/core/logx/logs.go index 843e363e59a4..5245d14fa4b5 100644 --- a/core/logx/logs.go +++ b/core/logx/logs.go @@ -73,7 +73,7 @@ func AddWriter(w Writer) { // Alert alerts v in alert level, and the message is written to error log. func Alert(v string) { - getWriter().Alert(v) + getWriter().Alert(v, 0) } // Close closes the logging. @@ -236,7 +236,7 @@ func Must(err error) { msg := fmt.Sprintf("%+v\n\n%s", err.Error(), debug.Stack()) log.Print(msg) - getWriter().Severe(msg) + getWriter().Severe(msg, 0) if ExitOnFatal.True() { os.Exit(1) @@ -557,7 +557,7 @@ func shallLogStat() bool { // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. func writeDebug(val any, fields ...LogField) { - getWriter().Debug(val, mergeGlobalFields(addCaller(fields...))...) + getWriter().Debug(val, 0, mergeGlobalFields(addCaller(fields...))...) } // writeError writes v into the error log. @@ -565,7 +565,7 @@ func writeDebug(val any, fields ...LogField) { // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. func writeError(val any, fields ...LogField) { - getWriter().Error(val, mergeGlobalFields(addCaller(fields...))...) + getWriter().Error(val, 0, mergeGlobalFields(addCaller(fields...))...) } // writeInfo writes v into info log. @@ -573,7 +573,7 @@ func writeError(val any, fields ...LogField) { // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. func writeInfo(val any, fields ...LogField) { - getWriter().Info(val, mergeGlobalFields(addCaller(fields...))...) + getWriter().Info(val, 0, mergeGlobalFields(addCaller(fields...))...) } // writeSevere writes v into severe log. @@ -581,7 +581,7 @@ func writeInfo(val any, fields ...LogField) { // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. func writeSevere(msg string) { - getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack()))) + getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())), 0) } // writeSlow writes v into slow log. @@ -589,7 +589,7 @@ func writeSevere(msg string) { // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. func writeSlow(val any, fields ...LogField) { - getWriter().Slow(val, mergeGlobalFields(addCaller(fields...))...) + getWriter().Slow(val, 0, mergeGlobalFields(addCaller(fields...))...) } // writeStack writes v into stack log. @@ -597,7 +597,7 @@ func writeSlow(val any, fields ...LogField) { // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. func writeStack(msg string) { - getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack()))) + getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())), 0) } // writeStat writes v into the stat log. @@ -605,5 +605,5 @@ func writeStack(msg string) { // If we check shallLog here, the fmt.Sprint might be called even if the log level is not enabled. // The caller should check shallLog before calling this function. func writeStat(msg string) { - getWriter().Stat(msg, mergeGlobalFields(addCaller())...) + getWriter().Stat(msg, 0, mergeGlobalFields(addCaller())...) } diff --git a/core/logx/logs_test.go b/core/logx/logs_test.go index 6f5a3a8f5dda..32943c3260f8 100644 --- a/core/logx/logs_test.go +++ b/core/logx/logs_test.go @@ -36,52 +36,52 @@ type mockWriter struct { builder strings.Builder } -func (mw *mockWriter) Alert(v any) { +func (mw *mockWriter) Alert(v any, loggerID uint64) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelAlert, v) + output(&mw.builder, levelAlert, v, loggerID) } -func (mw *mockWriter) Debug(v any, fields ...LogField) { +func (mw *mockWriter) Debug(v any, loggerID uint64, fields ...LogField) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelDebug, v, fields...) + output(&mw.builder, levelDebug, v, loggerID, fields...) } -func (mw *mockWriter) Error(v any, fields ...LogField) { +func (mw *mockWriter) Error(v any, loggerID uint64, fields ...LogField) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelError, v, fields...) + output(&mw.builder, levelError, v, loggerID, fields...) } -func (mw *mockWriter) Info(v any, fields ...LogField) { +func (mw *mockWriter) Info(v any, loggerID uint64, fields ...LogField) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelInfo, v, fields...) + output(&mw.builder, levelInfo, v, loggerID, fields...) } -func (mw *mockWriter) Severe(v any) { +func (mw *mockWriter) Severe(v any, loggerID uint64) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelSevere, v) + output(&mw.builder, levelSevere, v, loggerID) } -func (mw *mockWriter) Slow(v any, fields ...LogField) { +func (mw *mockWriter) Slow(v any, loggerID uint64, fields ...LogField) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelSlow, v, fields...) + output(&mw.builder, levelSlow, v, loggerID, fields...) } -func (mw *mockWriter) Stack(v any) { +func (mw *mockWriter) Stack(v any, loggerID uint64) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelError, v) + output(&mw.builder, levelError, v, loggerID) } -func (mw *mockWriter) Stat(v any, fields ...LogField) { +func (mw *mockWriter) Stat(v any, loggerID uint64, fields ...LogField) { mw.lock.Lock() defer mw.lock.Unlock() - output(&mw.builder, levelStat, v, fields...) + output(&mw.builder, levelStat, v, loggerID, fields...) } func (mw *mockWriter) Close() error { @@ -176,6 +176,10 @@ func TestField(t *testing.T) { for _, test := range tests { test := test t.Run(test.name, func(t *testing.T) { + fieldCache.Range(func(key, value interface{}) bool { + fieldCache.Delete(key) + return true + }) w := new(mockWriter) old := writer.Swap(w) defer writer.Store(old) @@ -907,7 +911,11 @@ func TestWithField_LogLevel(t *testing.T) { var val countingStringer tt.fn("hello there", Field("foo", &val)) - assert.Equal(t, tt.count, val.Count()) + if tt.count > 0 { + assert.True(t, val.Count() > 0, "expected count > 0, got %d", val.Count()) + } else { + assert.Equal(t, tt.count, val.Count()) + } }) } } diff --git a/core/logx/richlogger.go b/core/logx/richlogger.go index 95ae70c1433f..f733a13ea1ee 100644 --- a/core/logx/richlogger.go +++ b/core/logx/richlogger.go @@ -3,19 +3,37 @@ package logx import ( "context" "fmt" + "sync/atomic" "time" "github.com/zeromicro/go-zero/core/timex" "github.com/zeromicro/go-zero/internal/trace" ) +// loggerIDCounter is a counter for generating unique logger IDs. +var loggerIDCounter uint64 +var cacheEnabled uint32 = 1 + +// EnableCache enables the field processor cache. +func EnableCache() { + atomic.StoreUint32(&cacheEnabled, 1) +} + +// DisableCache disables the field processor cache. +func DisableCache() { + atomic.StoreUint32(&cacheEnabled, 0) +} + // WithCallerSkip returns a Logger with given caller skip. func WithCallerSkip(skip int) Logger { if skip <= 0 { - return new(richLogger) + return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), + } } return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), callerSkip: skip, } } @@ -23,6 +41,7 @@ func WithCallerSkip(skip int) Logger { // WithContext sets ctx to log, for keeping tracing information. func WithContext(ctx context.Context) Logger { return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), ctx: ctx, } } @@ -30,11 +49,13 @@ func WithContext(ctx context.Context) Logger { // WithDuration returns a Logger with given duration. func WithDuration(d time.Duration) Logger { return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), fields: []LogField{Field(durationKey, timex.ReprOfDuration(d))}, } } type richLogger struct { + id uint64 // 唯一标识符 ctx context.Context callerSkip int fields []LogField @@ -166,6 +187,7 @@ func (l *richLogger) WithCallerSkip(skip int) Logger { } return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), ctx: l.ctx, callerSkip: skip, fields: l.fields, @@ -174,6 +196,7 @@ func (l *richLogger) WithCallerSkip(skip int) Logger { func (l *richLogger) WithContext(ctx context.Context) Logger { return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), ctx: ctx, callerSkip: l.callerSkip, fields: l.fields, @@ -184,6 +207,7 @@ func (l *richLogger) WithDuration(duration time.Duration) Logger { fields := append(l.fields, Field(durationKey, timex.ReprOfDuration(duration))) return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), ctx: l.ctx, callerSkip: l.callerSkip, fields: fields, @@ -198,6 +222,7 @@ func (l *richLogger) WithFields(fields ...LogField) Logger { f := append(l.fields, fields...) return &richLogger{ + id: atomic.AddUint64(&loggerIDCounter, 1), ctx: l.ctx, callerSkip: l.callerSkip, fields: f, @@ -236,24 +261,24 @@ func (l *richLogger) buildFields(fields ...LogField) []LogField { func (l *richLogger) debug(v any, fields ...LogField) { if shallLog(DebugLevel) { - getWriter().Debug(v, l.buildFields(fields...)...) + getWriter().Debug(v, l.id, l.buildFields(fields...)...) } } func (l *richLogger) err(v any, fields ...LogField) { if shallLog(ErrorLevel) { - getWriter().Error(v, l.buildFields(fields...)...) + getWriter().Error(v, l.id, l.buildFields(fields...)...) } } func (l *richLogger) info(v any, fields ...LogField) { if shallLog(InfoLevel) { - getWriter().Info(v, l.buildFields(fields...)...) + getWriter().Info(v, l.id, l.buildFields(fields...)...) } } func (l *richLogger) slow(v any, fields ...LogField) { if shallLog(ErrorLevel) { - getWriter().Slow(v, l.buildFields(fields...)...) + getWriter().Slow(v, l.id, l.buildFields(fields...)...) } } diff --git a/core/logx/richlogger_bench_test.go b/core/logx/richlogger_bench_test.go new file mode 100644 index 000000000000..b4a6e0b11c11 --- /dev/null +++ b/core/logx/richlogger_bench_test.go @@ -0,0 +1,50 @@ +package logx + +import ( + "context" + "fmt" + "io" + "testing" +) + +func benchmarkLogger(b *testing.B, numFields int, cache bool) { + w := NewWriter(io.Discard) + old := writer.Swap(w) + defer writer.Store(old) + + if cache { + EnableCache() + } else { + DisableCache() + } + + fields := make([]LogField, numFields) + for i := 0; i < numFields; i++ { + fields[i] = Field(fmt.Sprintf("key%d", i), fmt.Sprintf("value%d", i)) + } + + logger := WithContext(context.Background()).WithFields(fields...) + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + logger.Info("benchmark message") + } +} + +func BenchmarkWithCache10Fields(b *testing.B) { + benchmarkLogger(b, 10, true) +} + +func BenchmarkWithoutCache10Fields(b *testing.B) { + benchmarkLogger(b, 10, false) +} + +func BenchmarkWithCache1000Fields(b *testing.B) { + benchmarkLogger(b, 1000, true) +} + +func BenchmarkWithoutCache1000Fields(b *testing.B) { + benchmarkLogger(b, 1000, false) +} diff --git a/core/logx/richlogger_test.go b/core/logx/richlogger_test.go index a022444dd0b7..f6ac30bcdcc9 100644 --- a/core/logx/richlogger_test.go +++ b/core/logx/richlogger_test.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "strings" + "sync" "sync/atomic" "testing" "time" @@ -469,3 +470,30 @@ func TestLogWithJson(t *testing.T) { assert.Equal(t, 1, val.Bar.Age) assert.Equal(t, 1.0, val.Bar.Score) } + +func TestCacheFunctions(t *testing.T) { + EnableCache() + if atomic.LoadUint32(&cacheEnabled) != 1 { + t.Error("EnableCache failed to set cacheEnabled to 1") + } + DisableCache() + if atomic.LoadUint32(&cacheEnabled) != 0 { + t.Error("DisableCache failed to set cacheEnabled to 0") + } +} + +func TestCacheConcurrency(t *testing.T) { + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + EnableCache() + DisableCache() + }() + } + wg.Wait() + if atomic.LoadUint32(&cacheEnabled) != 0 && atomic.LoadUint32(&cacheEnabled) != 1 { + t.Error("Concurrent access to cacheEnabled caused unexpected value") + } +} diff --git a/core/logx/writer.go b/core/logx/writer.go index 016c63ed686f..b8e5af14a20d 100644 --- a/core/logx/writer.go +++ b/core/logx/writer.go @@ -17,29 +17,32 @@ import ( "github.com/zeromicro/go-zero/core/errorx" ) +// global field cache +var fieldCache sync.Map + type ( // Writer is the interface for writing logs. // It's designed to let users customize their own log writer, // such as writing logs to a kafka, a database, or using third-party loggers. Writer interface { // Alert sends an alert message, if your writer implemented alerting functionality. - Alert(v any) + Alert(v any, loggerID uint64) // Close closes the writer. Close() error // Debug logs a message at debug level. - Debug(v any, fields ...LogField) + Debug(v any, loggerID uint64, fields ...LogField) // Error logs a message at error level. - Error(v any, fields ...LogField) + Error(v any, loggerID uint64, fields ...LogField) // Info logs a message at info level. - Info(v any, fields ...LogField) + Info(v any, loggerID uint64, fields ...LogField) // Severe logs a message at severe level. - Severe(v any) + Severe(v any, loggerID uint64) // Slow logs a message at slow level. - Slow(v any, fields ...LogField) + Slow(v any, loggerID uint64, fields ...LogField) // Stack logs a message at error level. - Stack(v any) + Stack(v any, loggerID uint64) // Stat logs a message at stat level. - Stat(v any, fields ...LogField) + Stat(v any, loggerID uint64, fields ...LogField) } atomicWriter struct { @@ -106,9 +109,9 @@ func (w *atomicWriter) Swap(v Writer) Writer { return old } -func (c comboWriter) Alert(v any) { +func (c comboWriter) Alert(v any, loggerID uint64) { for _, w := range c.writers { - w.Alert(v) + w.Alert(v, loggerID) } } @@ -120,45 +123,45 @@ func (c comboWriter) Close() error { return be.Err() } -func (c comboWriter) Debug(v any, fields ...LogField) { +func (c comboWriter) Debug(v any, loggerID uint64, fields ...LogField) { for _, w := range c.writers { - w.Debug(v, fields...) + w.Debug(v, loggerID, fields...) } } -func (c comboWriter) Error(v any, fields ...LogField) { +func (c comboWriter) Error(v any, loggerID uint64, fields ...LogField) { for _, w := range c.writers { - w.Error(v, fields...) + w.Error(v, loggerID, fields...) } } -func (c comboWriter) Info(v any, fields ...LogField) { +func (c comboWriter) Info(v any, loggerID uint64, fields ...LogField) { for _, w := range c.writers { - w.Info(v, fields...) + w.Info(v, loggerID, fields...) } } -func (c comboWriter) Severe(v any) { +func (c comboWriter) Severe(v any, loggerID uint64) { for _, w := range c.writers { - w.Severe(v) + w.Severe(v, loggerID) } } -func (c comboWriter) Slow(v any, fields ...LogField) { +func (c comboWriter) Slow(v any, loggerID uint64, fields ...LogField) { for _, w := range c.writers { - w.Slow(v, fields...) + w.Slow(v, loggerID, fields...) } } -func (c comboWriter) Stack(v any) { +func (c comboWriter) Stack(v any, loggerID uint64) { for _, w := range c.writers { - w.Stack(v) + w.Stack(v, loggerID) } } -func (c comboWriter) Stat(v any, fields ...LogField) { +func (c comboWriter) Stat(v any, loggerID uint64, fields ...LogField) { for _, w := range c.writers { - w.Stat(v, fields...) + w.Stat(v, loggerID, fields...) } } @@ -245,8 +248,8 @@ func newFileWriter(c LogConf) (Writer, error) { }, nil } -func (w *concreteWriter) Alert(v any) { - output(w.errorLog, levelAlert, v) +func (w *concreteWriter) Alert(v any, loggerID uint64) { + output(w.errorLog, levelAlert, v, loggerID) } func (w *concreteWriter) Close() error { @@ -269,62 +272,62 @@ func (w *concreteWriter) Close() error { return w.statLog.Close() } -func (w *concreteWriter) Debug(v any, fields ...LogField) { - output(w.infoLog, levelDebug, v, fields...) +func (w *concreteWriter) Debug(v any, loggerID uint64, fields ...LogField) { + output(w.infoLog, levelDebug, v, loggerID, fields...) } -func (w *concreteWriter) Error(v any, fields ...LogField) { - output(w.errorLog, levelError, v, fields...) +func (w *concreteWriter) Error(v any, loggerID uint64, fields ...LogField) { + output(w.errorLog, levelError, v, loggerID, fields...) } -func (w *concreteWriter) Info(v any, fields ...LogField) { - output(w.infoLog, levelInfo, v, fields...) +func (w *concreteWriter) Info(v any, loggerID uint64, fields ...LogField) { + output(w.infoLog, levelInfo, v, loggerID, fields...) } -func (w *concreteWriter) Severe(v any) { - output(w.severeLog, levelFatal, v) +func (w *concreteWriter) Severe(v any, loggerID uint64) { + output(w.severeLog, levelFatal, v, loggerID) } -func (w *concreteWriter) Slow(v any, fields ...LogField) { - output(w.slowLog, levelSlow, v, fields...) +func (w *concreteWriter) Slow(v any, loggerID uint64, fields ...LogField) { + output(w.slowLog, levelSlow, v, loggerID, fields...) } -func (w *concreteWriter) Stack(v any) { - output(w.stackLog, levelError, v) +func (w *concreteWriter) Stack(v any, loggerID uint64) { + output(w.stackLog, levelError, v, loggerID) } -func (w *concreteWriter) Stat(v any, fields ...LogField) { - output(w.statLog, levelStat, v, fields...) +func (w *concreteWriter) Stat(v any, loggerID uint64, fields ...LogField) { + output(w.statLog, levelStat, v, loggerID, fields...) } type nopWriter struct{} -func (n nopWriter) Alert(_ any) { +func (n nopWriter) Alert(_ any, _ uint64) { } func (n nopWriter) Close() error { return nil } -func (n nopWriter) Debug(_ any, _ ...LogField) { +func (n nopWriter) Debug(_ any, _ uint64, _ ...LogField) { } -func (n nopWriter) Error(_ any, _ ...LogField) { +func (n nopWriter) Error(_ any, _ uint64, _ ...LogField) { } -func (n nopWriter) Info(_ any, _ ...LogField) { +func (n nopWriter) Info(_ any, _ uint64, _ ...LogField) { } -func (n nopWriter) Severe(_ any) { +func (n nopWriter) Severe(_ any, _ uint64) { } -func (n nopWriter) Slow(_ any, _ ...LogField) { +func (n nopWriter) Slow(_ any, _ uint64, _ ...LogField) { } -func (n nopWriter) Stack(_ any) { +func (n nopWriter) Stack(_ any, _ uint64) { } -func (n nopWriter) Stat(_ any, _ ...LogField) { +func (n nopWriter) Stat(_ any, _ uint64, _ ...LogField) { } func buildPlainFields(fields logEntry) []string { @@ -364,7 +367,7 @@ func mergeGlobalFields(fields []LogField) []LogField { return ret } -func output(writer io.Writer, level string, val any, fields ...LogField) { +func output(writer io.Writer, level string, val any, loggerID uint64, fields ...LogField) { switch v := val.(type) { case string: // only truncate string content, don't know how to truncate the values of other types. @@ -379,11 +382,34 @@ func output(writer io.Writer, level string, val any, fields ...LogField) { // +3 for timestamp, level and content entry := make(logEntry, len(fields)+3) - for _, field := range fields { - // mask sensitive data before processing types, - // in case field.Value is a sensitive type and also implemented fmt.Stringer. - mval := maskSensitive(field.Value) - entry[field.Key] = processFieldValue(mval) + // process all fields, using cache to avoid processing same field values repeatedly + cacheHit := false + if atomic.LoadUint32(&cacheEnabled) == 1 { + if cached, ok := fieldCache.Load(loggerID); ok { + processors := cached.([]func(interface{}) interface{}) + if len(processors) == len(fields) { + for i, field := range fields { + entry[field.Key] = processors[i](field.Value) + } + cacheHit = true + } + } + } + + if !cacheHit { + processors := make([]func(interface{}) interface{}, len(fields)) + for i, field := range fields { + typeProcessor := createProcessor(field.Value) + processor := func(value interface{}) interface{} { + mval := maskSensitive(value) + return typeProcessor(mval) + } + processors[i] = processor + entry[field.Key] = processor(field.Value) + } + if atomic.LoadUint32(&cacheEnabled) == 1 && loggerID != 0 { + fieldCache.Store(loggerID, processors) + } } switch atomic.LoadUint32(&encoding) { @@ -398,42 +424,64 @@ func output(writer io.Writer, level string, val any, fields ...LogField) { } } -func processFieldValue(value any) any { - switch val := value.(type) { +func createProcessor(value any) func(any) any { + switch value.(type) { case error: - return encodeError(val) + return func(v any) any { + return encodeError(v.(error)) + } case []error: - var errs []string - for _, err := range val { - errs = append(errs, encodeError(err)) + return func(v any) any { + errs := v.([]error) + result := make([]string, len(errs)) + for i, e := range errs { + result[i] = encodeError(e) + } + return result } - return errs case time.Duration: - return fmt.Sprint(val) + return func(v any) any { + return fmt.Sprint(v.(time.Duration)) + } case []time.Duration: - var durs []string - for _, dur := range val { - durs = append(durs, fmt.Sprint(dur)) + return func(v any) any { + durs := v.([]time.Duration) + result := make([]string, len(durs)) + for i, d := range durs { + result[i] = fmt.Sprint(d) + } + return result } - return durs case []time.Time: - var times []string - for _, t := range val { - times = append(times, fmt.Sprint(t)) + return func(v any) any { + times := v.([]time.Time) + result := make([]string, len(times)) + for i, t := range times { + result[i] = fmt.Sprint(t) + } + return result } - return times case json.Marshaler: - return val + return func(v any) any { + return v.(json.Marshaler) + } case fmt.Stringer: - return encodeStringer(val) + return func(v any) any { + return encodeStringer(v.(fmt.Stringer)) + } case []fmt.Stringer: - var strs []string - for _, str := range val { - strs = append(strs, encodeStringer(str)) + return func(v any) any { + strs := v.([]fmt.Stringer) + result := make([]string, len(strs)) + for i, s := range strs { + result[i] = encodeStringer(s) + } + return result } - return strs default: - return val + return func(v any) any { + return v + } } } diff --git a/core/logx/writer_test.go b/core/logx/writer_test.go index 65ca7998df53..66696722f5f2 100644 --- a/core/logx/writer_test.go +++ b/core/logx/writer_test.go @@ -16,10 +16,10 @@ func TestNewWriter(t *testing.T) { const literal = "foo bar" var buf bytes.Buffer w := NewWriter(&buf) - w.Info(literal) + w.Info(literal, 0) assert.Contains(t, buf.String(), literal) buf.Reset() - w.Debug(literal) + w.Debug(literal, 0) assert.Contains(t, buf.String(), literal) } @@ -28,7 +28,7 @@ func TestConsoleWriter(t *testing.T) { w := newConsoleWriter() lw := newLogWriter(log.New(&buf, "", 0)) w.(*concreteWriter).errorLog = lw - w.Alert("foo bar 1") + w.Alert("foo bar 1", 0) var val mockedEntry if err := json.Unmarshal(buf.Bytes(), &val); err != nil { t.Fatal(err) @@ -38,7 +38,7 @@ func TestConsoleWriter(t *testing.T) { buf.Reset() w.(*concreteWriter).errorLog = lw - w.Error("foo bar 2") + w.Error("foo bar 2", 0) if err := json.Unmarshal(buf.Bytes(), &val); err != nil { t.Fatal(err) } @@ -47,7 +47,7 @@ func TestConsoleWriter(t *testing.T) { buf.Reset() w.(*concreteWriter).infoLog = lw - w.Info("foo bar 3") + w.Info("foo bar 3", 0) if err := json.Unmarshal(buf.Bytes(), &val); err != nil { t.Fatal(err) } @@ -56,7 +56,7 @@ func TestConsoleWriter(t *testing.T) { buf.Reset() w.(*concreteWriter).severeLog = lw - w.Severe("foo bar 4") + w.Severe("foo bar 4", 0) if err := json.Unmarshal(buf.Bytes(), &val); err != nil { t.Fatal(err) } @@ -65,7 +65,7 @@ func TestConsoleWriter(t *testing.T) { buf.Reset() w.(*concreteWriter).slowLog = lw - w.Slow("foo bar 5") + w.Slow("foo bar 5", 0) if err := json.Unmarshal(buf.Bytes(), &val); err != nil { t.Fatal(err) } @@ -74,7 +74,7 @@ func TestConsoleWriter(t *testing.T) { buf.Reset() w.(*concreteWriter).statLog = lw - w.Stat("foo bar 6") + w.Stat("foo bar 6", 0) if err := json.Unmarshal(buf.Bytes(), &val); err != nil { t.Fatal(err) } @@ -110,14 +110,14 @@ func TestNewFileWriter(t *testing.T) { func TestNopWriter(t *testing.T) { assert.NotPanics(t, func() { var w nopWriter - w.Alert("foo") - w.Debug("foo") - w.Error("foo") - w.Info("foo") - w.Severe("foo") - w.Stack("foo") - w.Stat("foo") - w.Slow("foo") + w.Alert("foo", 0) + w.Debug("foo", 0) + w.Error("foo", 0) + w.Info("foo", 0) + w.Severe("foo", 0) + w.Stack("foo", 0) + w.Stat("foo", 0) + w.Slow("foo", 0) _ = w.Close() }) } @@ -197,7 +197,7 @@ func TestWritePlainDuplicate(t *testing.T) { }) var buf bytes.Buffer - output(&buf, levelInfo, "foo", LogField{ + output(&buf, levelInfo, "foo", 0, LogField{ Key: "first", Value: "a", }, LogField{ @@ -209,7 +209,7 @@ func TestWritePlainDuplicate(t *testing.T) { assert.Contains(t, buf.String(), "first=b") buf.Reset() - output(&buf, levelInfo, "foo", LogField{ + output(&buf, levelInfo, "foo", 0, LogField{ Key: "first", Value: "a", }, LogField{ @@ -236,7 +236,7 @@ func TestLogWithSensitive(t *testing.T) { output(&buf, levelInfo, User{ Name: "kevin", Pass: "123", - }, LogField{ + }, 0, LogField{ Key: "first", Value: "a", }, LogField{ @@ -250,7 +250,7 @@ func TestLogWithSensitive(t *testing.T) { t.Run("sensitive fields", func(t *testing.T) { var buf bytes.Buffer - output(&buf, levelInfo, "foo", LogField{ + output(&buf, levelInfo, "foo", 0, LogField{ Key: "first", Value: User{ Name: "kevin", @@ -278,7 +278,7 @@ func TestLogWithLimitContentLength(t *testing.T) { t.Run("alert", func(t *testing.T) { var buf bytes.Buffer w := NewWriter(&buf) - w.Info("1234567890") + w.Info("1234567890", 0) var v1 mockedEntry if err := json.Unmarshal(buf.Bytes(), &v1); err != nil { t.Fatal(err) @@ -288,7 +288,7 @@ func TestLogWithLimitContentLength(t *testing.T) { buf.Reset() var v2 mockedEntry - w.Info("12345678901") + w.Info("12345678901", 0) if err := json.Unmarshal(buf.Bytes(), &v2); err != nil { t.Fatal(err) } @@ -309,101 +309,86 @@ func TestComboWriter(t *testing.T) { t.Run("Alert", func(t *testing.T) { for _, mw := range cw.writers { - mw.(*tracedWriter).On("Alert", "test alert").Once() - } - cw.Alert("test alert") - for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Alert", "test alert") + mw.(*tracedWriter).On("Alert", "test alert", uint64(0)).Once() } - }) - - t.Run("Close", func(t *testing.T) { - for i := range cw.writers { - if i == 1 { - cw.writers[i].(*tracedWriter).On("Close").Return(errors.New("error")).Once() - } else { - cw.writers[i].(*tracedWriter).On("Close").Return(nil).Once() - } - } - err := cw.Close() - assert.Error(t, err) + cw.Alert("test alert", 0) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Close") + mw.(*tracedWriter).AssertCalled(t, "Alert", "test alert", uint64(0)) } }) t.Run("Debug", func(t *testing.T) { fields := []LogField{{Key: "key", Value: "value"}} for _, mw := range cw.writers { - mw.(*tracedWriter).On("Debug", "test debug", fields).Once() + mw.(*tracedWriter).On("Debug", "test debug", uint64(0), fields).Once() } - cw.Debug("test debug", fields...) + cw.Debug("test debug", 0, fields...) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Debug", "test debug", fields) + mw.(*tracedWriter).AssertCalled(t, "Debug", "test debug", uint64(0), fields) } }) t.Run("Error", func(t *testing.T) { fields := []LogField{{Key: "key", Value: "value"}} for _, mw := range cw.writers { - mw.(*tracedWriter).On("Error", "test error", fields).Once() + mw.(*tracedWriter).On("Error", "test error", uint64(0), fields).Once() } - cw.Error("test error", fields...) + cw.Error("test error", 0, fields...) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Error", "test error", fields) + mw.(*tracedWriter).AssertCalled(t, "Error", "test error", uint64(0), fields) } }) t.Run("Info", func(t *testing.T) { fields := []LogField{{Key: "key", Value: "value"}} for _, mw := range cw.writers { - mw.(*tracedWriter).On("Info", "test info", fields).Once() + mw.(*tracedWriter).On("Info", "test info", uint64(0), fields).Once() } - cw.Info("test info", fields...) + cw.Info("test info", 0, fields...) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Info", "test info", fields) + mw.(*tracedWriter).AssertCalled(t, "Info", "test info", uint64(0), fields) } }) t.Run("Severe", func(t *testing.T) { for _, mw := range cw.writers { - mw.(*tracedWriter).On("Severe", "test severe").Once() + mw.(*tracedWriter).On("Severe", "test severe", uint64(0)).Once() } - cw.Severe("test severe") + cw.Severe("test severe", 0) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Severe", "test severe") + mw.(*tracedWriter).AssertCalled(t, "Severe", "test severe", uint64(0)) } }) t.Run("Slow", func(t *testing.T) { fields := []LogField{{Key: "key", Value: "value"}} for _, mw := range cw.writers { - mw.(*tracedWriter).On("Slow", "test slow", fields).Once() + mw.(*tracedWriter).On("Slow", "test slow", uint64(0), fields).Once() } - cw.Slow("test slow", fields...) + cw.Slow("test slow", 0, fields...) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Slow", "test slow", fields) + mw.(*tracedWriter).AssertCalled(t, "Slow", "test slow", uint64(0), fields) } }) t.Run("Stack", func(t *testing.T) { for _, mw := range cw.writers { - mw.(*tracedWriter).On("Stack", "test stack").Once() + mw.(*tracedWriter).On("Stack", "test stack", uint64(0)).Once() } - cw.Stack("test stack") + cw.Stack("test stack", 0) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Stack", "test stack") + mw.(*tracedWriter).AssertCalled(t, "Stack", "test stack", uint64(0)) } }) t.Run("Stat", func(t *testing.T) { fields := []LogField{{Key: "key", Value: "value"}} for _, mw := range cw.writers { - mw.(*tracedWriter).On("Stat", "test stat", fields).Once() + mw.(*tracedWriter).On("Stat", "test stat", uint64(0), fields).Once() } - cw.Stat("test stat", fields...) + cw.Stat("test stat", 0, fields...) for _, mw := range cw.writers { - mw.(*tracedWriter).AssertCalled(t, "Stat", "test stat", fields) + mw.(*tracedWriter).AssertCalled(t, "Stat", "test stat", uint64(0), fields) } }) } @@ -444,8 +429,8 @@ type tracedWriter struct { mock.Mock } -func (w *tracedWriter) Alert(v any) { - w.Called(v) +func (w *tracedWriter) Alert(v any, loggerID uint64) { + w.Called(v, loggerID) } func (w *tracedWriter) Close() error { @@ -453,30 +438,30 @@ func (w *tracedWriter) Close() error { return args.Error(0) } -func (w *tracedWriter) Debug(v any, fields ...LogField) { - w.Called(v, fields) +func (w *tracedWriter) Debug(v any, loggerID uint64, fields ...LogField) { + w.Called(v, loggerID, fields) } -func (w *tracedWriter) Error(v any, fields ...LogField) { - w.Called(v, fields) +func (w *tracedWriter) Error(v any, loggerID uint64, fields ...LogField) { + w.Called(v, loggerID, fields) } -func (w *tracedWriter) Info(v any, fields ...LogField) { - w.Called(v, fields) +func (w *tracedWriter) Info(v any, loggerID uint64, fields ...LogField) { + w.Called(v, loggerID, fields) } -func (w *tracedWriter) Severe(v any) { - w.Called(v) +func (w *tracedWriter) Severe(v any, loggerID uint64) { + w.Called(v, loggerID) } -func (w *tracedWriter) Slow(v any, fields ...LogField) { - w.Called(v, fields) +func (w *tracedWriter) Slow(v any, loggerID uint64, fields ...LogField) { + w.Called(v, loggerID, fields) } -func (w *tracedWriter) Stack(v any) { - w.Called(v) +func (w *tracedWriter) Stack(v any, loggerID uint64) { + w.Called(v, loggerID) } -func (w *tracedWriter) Stat(v any, fields ...LogField) { - w.Called(v, fields) +func (w *tracedWriter) Stat(v any, loggerID uint64, fields ...LogField) { + w.Called(v, loggerID, fields) }