From 4321da1a68bc1d89d3b5cd59656deb71d0ebb789 Mon Sep 17 00:00:00 2001 From: Inhere Date: Sun, 2 Jul 2023 11:50:00 +0800 Subject: [PATCH] :sparkles: feat: update record.Time init logic, support repeat use a Record instance --- issues_test.go | 25 +++++++++++++++++++++++++ write.go => logger_write.go | 17 ++++++++++------- record.go | 16 +++++++++++----- record_test.go | 36 +++++++++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 13 deletions(-) rename write.go => logger_write.go (85%) diff --git a/issues_test.go b/issues_test.go index a3a6c6b..a96baab 100644 --- a/issues_test.go +++ b/issues_test.go @@ -2,6 +2,7 @@ package slog_test import ( "fmt" + "sync" "testing" "time" @@ -67,3 +68,27 @@ func TestIssues_75(t *testing.T) { slog.Reset() // dump.P(slog.GetFormatter()) } + +// https://github.com/gookit/slog/issues/105 +func TestIssues_105(t *testing.T) { + t.Run("simple write", func(t *testing.T) { + for i := 0; i < 10; i++ { + slog.Error("simple error log", i) + time.Sleep(time.Millisecond * 100) + } + }) + + // test concurrent write + t.Run("concurrent write", func(t *testing.T) { + wg := sync.WaitGroup{} + for i := 0; i < 100; i++ { + wg.Add(1) + go func(i int) { + slog.Error("concurrent error log", i) + time.Sleep(time.Millisecond * 100) + wg.Done() + }(i) + } + wg.Wait() + }) +} diff --git a/write.go b/logger_write.go similarity index 85% rename from write.go rename to logger_write.go index 6225cc2..7e5f041 100644 --- a/write.go +++ b/logger_write.go @@ -21,8 +21,10 @@ package slog // r.Buffer = nil // } -// Init something for record. +// Init something for record(eg: time, level name). func (r *Record) Init(lowerLevelName bool) { + r.inited = true + // use lower level name if lowerLevelName { r.levelName = r.Level.LowerName() @@ -54,22 +56,22 @@ func (r *Record) beforeHandle(l *Logger) { } } -// do write log record +// do write record to handlers, will add lock. func (l *Logger) writeRecord(level Level, r *Record) { l.mu.Lock() defer l.mu.Unlock() + // reset init flag, useful for repeat use Record + r.inited = false - // do write log message - var inited bool for _, handler := range l.handlers { if handler.IsHandling(level) { - if !inited { - // init, call processors + // init record, call processors + if !r.inited { r.Init(l.LowerLevelName) r.beforeHandle(l) - inited = true } + // do write log message by handler if err := handler.Handle(r); err != nil { l.err = err printlnStderr("slog: failed to handle log, error:", err) @@ -78,6 +80,7 @@ func (l *Logger) writeRecord(level Level, r *Record) { } // ---- after write log ---- + r.Time = emptyTime // flush logs on level <= error level. if level <= ErrorLevel { diff --git a/record.go b/record.go index 4268419..be1f26b 100644 --- a/record.go +++ b/record.go @@ -11,8 +11,14 @@ import ( // Record a log record definition type Record struct { logger *Logger - - // Time for record log + // release flag for record. TODO + unreleased bool + // inited flag for record + inited bool + + // Time for record log, if is empty will use now. + // + // TIP: Will be emptied after each use (write) Time time.Time // Level log level for record Level Level @@ -134,9 +140,9 @@ func (r *Record) Copy() *Record { } return &Record{ - logger: r.logger, - Channel: r.Channel, - Time: r.Time, + logger: r.logger, + Channel: r.Channel, + // Time: r.Time, Level: r.Level, levelName: r.levelName, CallerFlag: r.CallerFlag, diff --git a/record_test.go b/record_test.go index 5378051..c145130 100644 --- a/record_test.go +++ b/record_test.go @@ -3,8 +3,12 @@ package slog_test import ( "context" "fmt" + "os" + "sync" "testing" + "time" + "github.com/gookit/goutil/byteutil" "github.com/gookit/goutil/errorx" "github.com/gookit/goutil/testutil/assert" "github.com/gookit/goutil/timex" @@ -187,7 +191,7 @@ func TestRecord_allLevel(t *testing.T) { }) r := l.Record() - r.WithContext(context.Background()) + r = r.WithContext(context.Background()) printAllLevelLogs(r, "a message use record.XX()") r.Log(slog.InfoLevel, "a message use record.XX()") r.Notice("a message use record.XX()") @@ -210,3 +214,33 @@ func TestRecord_allLevel(t *testing.T) { assert.Contains(t, s, "[NOTICE]") assert.Contains(t, s, "[TRACE]") } + +func TestRecord_useMultiTimes(t *testing.T) { + buf := byteutil.NewBuffer() + l := slog.NewWithHandlers( + handler.NewSimple(buf, slog.DebugLevel), + handler.NewSimple(os.Stdout, slog.DebugLevel), + ) + + r := l.Record() + t.Run("simple", func(t *testing.T) { + for i := 0; i < 10; i++ { + r.Error("simple error log", i) + time.Sleep(time.Millisecond * 100) + } + }) + + // test concurrent write + t.Run("concurrent", func(t *testing.T) { + wg := sync.WaitGroup{} + for i := 0; i < 100; i++ { + wg.Add(1) + go func(i int) { + r.Error("concurrent error log", i) + time.Sleep(time.Millisecond * 100) + wg.Done() + }(i) + } + wg.Wait() + }) +}