From 70031a7ded4c36d3d37baf954dff1b2ac9feaa84 Mon Sep 17 00:00:00 2001 From: Inhere Date: Tue, 26 Mar 2024 17:03:37 +0800 Subject: [PATCH] :necktie: up: add new handler option: TimeClock, fix some test error - add rotatefile.MockClocker for mock test rotating file --- handler/config.go | 39 ++++++++++++++---------- handler/rotatefile_test.go | 55 ++++++++-------------------------- handler/write_close_flusher.go | 2 +- rotatefile/issues_test.go | 2 +- rotatefile/util.go | 29 ++++++++++++++++++ rotatefile/writer_test.go | 26 ---------------- 6 files changed, 66 insertions(+), 87 deletions(-) diff --git a/handler/config.go b/handler/config.go index a1ca038..48dd78c 100644 --- a/handler/config.go +++ b/handler/config.go @@ -11,7 +11,7 @@ import ( "github.com/gookit/slog/rotatefile" ) -// the buff mode consts +// the buff mode constants const ( BuffModeLine = "line" BuffModeBite = "bite" @@ -59,6 +59,9 @@ type Config struct { // RotateMode for rotate file by time. default rotatefile.ModeRename RotateMode rotatefile.RotateMode `json:"rotate_mode" yaml:"rotate_mode"` + // TimeClock for rotate file by time. + TimeClock rotatefile.Clocker `json:"-" yaml:"-"` + // MaxSize on rotate file by size, unit is bytes. MaxSize uint64 `json:"max_size" yaml:"max_size"` @@ -148,7 +151,7 @@ func (c *Config) CreateHandler() (*SyncCloseHandler, error) { // RotateWriter build rotate writer by config func (c *Config) RotateWriter() (output SyncCloseWriter, err error) { if c.MaxSize == 0 && c.RotateTime == 0 { - return nil, errorx.Raw("slog: cannot create rotate writer, MaxSize and RotateTime both is 0") + return nil, errorx.E("slog: cannot create rotate writer, MaxSize and RotateTime both is 0") } return c.CreateWriter() @@ -163,7 +166,7 @@ func (c *Config) CreateWriter() (output SyncCloseWriter, err error) { c.FilePerm = rotatefile.DefaultFilePerm } - // create a rotate config. + // create a rotated writer by config. if c.MaxSize > 0 || c.RotateTime > 0 { rc := rotatefile.EmptyConfigWith() @@ -184,10 +187,13 @@ func (c *Config) CreateWriter() (output SyncCloseWriter, err error) { if c.RenameFunc != nil { rc.RenameFunc = c.RenameFunc } + if c.TimeClock != nil { + rc.TimeClock = c.TimeClock + } - // create a rotating writer output, err = rc.Create() } else { + // create a file writer output, err = fsutil.OpenAppendFile(c.Logfile, c.FilePerm) } @@ -209,10 +215,6 @@ type flushSyncCloseWriter interface { // wrap buffer for the writer func (c *Config) wrapBuffer(w io.Writer) (bw flushSyncCloseWriter) { - // if c.BuffSize == 0 { - // panic("slog: buff size cannot be zero on wrap buffer") - // } - if c.BuffMode == BuffModeLine { bw = bufwrite.NewLineWriterSize(w, c.BuffSize) } else { @@ -264,47 +266,52 @@ func WithLevelNames(names []string) ConfigFn { } } -// WithRotateTime setting +// WithRotateTime setting rotate time func WithRotateTime(rt rotatefile.RotateTime) ConfigFn { return func(c *Config) { c.RotateTime = rt } } -// WithRotateMode setting +// WithRotateMode setting rotate mode func WithRotateMode(m rotatefile.RotateMode) ConfigFn { return func(c *Config) { c.RotateMode = m } } +// WithTimeClock setting +func WithTimeClock(clock rotatefile.Clocker) ConfigFn { + return func(c *Config) { c.TimeClock = clock } +} + // WithBackupNum setting func WithBackupNum(n uint) ConfigFn { return func(c *Config) { c.BackupNum = n } } -// WithBackupTime setting +// WithBackupTime setting backup time func WithBackupTime(bt uint) ConfigFn { return func(c *Config) { c.BackupTime = bt } } -// WithBuffMode setting +// WithBuffMode setting buffer mode func WithBuffMode(buffMode string) ConfigFn { return func(c *Config) { c.BuffMode = buffMode } } -// WithBuffSize setting +// WithBuffSize setting buffer size func WithBuffSize(buffSize int) ConfigFn { return func(c *Config) { c.BuffSize = buffSize } } -// WithMaxSize setting +// WithMaxSize setting max size for rotate file func WithMaxSize(maxSize uint64) ConfigFn { return func(c *Config) { c.MaxSize = maxSize } } -// WithCompress setting +// WithCompress setting compress func WithCompress(compress bool) ConfigFn { return func(c *Config) { c.Compress = compress } } -// WithUseJSON setting +// WithUseJSON setting use json format func WithUseJSON(useJSON bool) ConfigFn { return func(c *Config) { c.UseJSON = useJSON } } diff --git a/handler/rotatefile_test.go b/handler/rotatefile_test.go index bfb3109..2b26024 100644 --- a/handler/rotatefile_test.go +++ b/handler/rotatefile_test.go @@ -88,25 +88,12 @@ func TestNewSizeRotateFileHandler(t *testing.T) { func TestNewTimeRotateFileHandler_EveryDay(t *testing.T) { logfile := "./testdata/time-rotate_EveryDay.log" - newFile := logfile + timex.Now().DateFormat(".Ymd") - - sec := -2 - // set current time to today 23:59:57 - testClock := func() time.Time { - // dump.P(sec) - return timex.Now().DayEnd().AddSeconds(sec).Time - } - assert.Eq(t, "23:59:57", timex.Date(testClock(), "H:I:S")) - - // backup - bckFn := rotatefile.DefaultTimeClockFn - rotatefile.DefaultTimeClockFn = testClock - defer func() { - rotatefile.DefaultTimeClockFn = bckFn - }() + newFile := logfile + ".20221116" + clock := rotatefile.NewMockClock("2022-11-16 23:59:57") options := []handler.ConfigFn{ handler.WithBuffSize(128), + handler.WithTimeClock(clock), } h := handler.MustTimeRotateFile(logfile, handler.EveryDay, options...) @@ -114,14 +101,13 @@ func TestNewTimeRotateFileHandler_EveryDay(t *testing.T) { l := slog.NewWithHandlers(h) l.ReportCaller = true - l.TimeClock = testClock + l.TimeClock = clock.Now - for i := 0; i < 4; i++ { + for i := 0; i < 6; i++ { l.WithData(sampleData).Info("the th:", i, "info message") l.Warnf("the th:%d warn message text", i) - sec++ fmt.Println("log number ", (i+1)*2) - // time.Sleep(time.Second * 1) + clock.Add(time.Second * 1) } l.MustClose() @@ -130,29 +116,12 @@ func TestNewTimeRotateFileHandler_EveryDay(t *testing.T) { } func TestNewTimeRotateFileHandler_EveryHour(t *testing.T) { + clock := rotatefile.NewMockClock("2022-04-28 20:59:58") logfile := "./testdata/time-rotate_EveryHour.log" - assert.NoErr(t, fsutil.DeleteIfExist(logfile)) - - hourStart := timex.Now().HourStart() - newFile := logfile + hourStart.DateFormat(".Ymd_H00") - assert.NoErr(t, fsutil.DeleteIfFileExist(newFile)) - - sec := -2 - // set current time to hour end 59:58 - testClock := func() time.Time { - // dump.P(sec) - return hourStart.AddHour(1).AddSeconds(sec).Time - } - assert.Eq(t, "59:58", timex.Date(testClock(), "I:S")) - - // backup - bckFn := rotatefile.DefaultTimeClockFn - rotatefile.DefaultTimeClockFn = testClock - defer func() { - rotatefile.DefaultTimeClockFn = bckFn - }() + newFile := logfile + timex.DateFormat(clock.Now(), ".Ymd_H00") options := []handler.ConfigFn{ + handler.WithTimeClock(clock), handler.WithBuffSize(0), } h, err := handler.NewTimeRotateFile(logfile, rotatefile.EveryHour, options...) @@ -162,13 +131,13 @@ func TestNewTimeRotateFileHandler_EveryHour(t *testing.T) { l := slog.NewWithHandlers(h) l.ReportCaller = true - l.TimeClock = testClock + l.TimeClock = clock.Now - for i := 0; i < 3; i++ { + for i := 0; i < 6; i++ { l.WithData(sampleData).Info("the th:", i, "info message") l.Warnf("the th:%d warn message text", i) - sec++ fmt.Println("log number ", (i+1)*2) + clock.Add(time.Second * 1) } l.MustClose() diff --git a/handler/write_close_flusher.go b/handler/write_close_flusher.go index a9694a7..bb23810 100644 --- a/handler/write_close_flusher.go +++ b/handler/write_close_flusher.go @@ -46,7 +46,7 @@ func FlushCloserWithLevels(out FlushCloseWriter, levels []slog.Level) *FlushClos // // Usage: // -// buf := new(bytes.Buffer) +// buf := new(byteutil.Buffer) // h := handler.NewFlushCloseHandler(&buf, slog.AllLevels) // // f, err := os.OpenFile("my.log", ...) diff --git a/rotatefile/issues_test.go b/rotatefile/issues_test.go index 60f1105..4e2fac0 100644 --- a/rotatefile/issues_test.go +++ b/rotatefile/issues_test.go @@ -15,7 +15,7 @@ import ( func TestIssues_138(t *testing.T) { logfile := "testdata/rotate_day.log" - mt := newMockTime("2023-11-16 23:59:55") + mt := rotatefile.NewMockClock("2023-11-16 23:59:55") w, err := rotatefile.NewWriterWith(rotatefile.WithDebugMode, func(c *rotatefile.Config) { c.TimeClock = mt // c.MaxSize = 128 diff --git a/rotatefile/util.go b/rotatefile/util.go index 4cb0a72..0906de1 100644 --- a/rotatefile/util.go +++ b/rotatefile/util.go @@ -6,8 +6,11 @@ import ( "io" "io/fs" "os" + "time" + "github.com/gookit/goutil" "github.com/gookit/goutil/fsutil" + "github.com/gookit/goutil/timex" ) const compressSuffix = ".gz" @@ -82,3 +85,29 @@ func (fis modTimeFInfos) Swap(i, j int) { func (fis modTimeFInfos) Len() int { return len(fis) } + +// MockClocker mock clock for test +type MockClocker struct { + tt time.Time +} + +// NewMockClock create a mock time instance from datetime string. +func NewMockClock(datetime string) *MockClocker { + nt := goutil.Must(timex.FromString(datetime)) + return &MockClocker{tt: nt.Time} +} + +// Now get current time. +func (mt *MockClocker) Now() time.Time { + return mt.tt +} + +// Add progresses time by the given duration. +func (mt *MockClocker) Add(d time.Duration) { + mt.tt = mt.tt.Add(d) +} + +// Datetime returns the current time in the format "2006-01-02 15:04:05". +func (mt *MockClocker) Datetime() string { + return mt.tt.Format("2006-01-02 15:04:05") +} diff --git a/rotatefile/writer_test.go b/rotatefile/writer_test.go index d14bfb8..f3b178b 100644 --- a/rotatefile/writer_test.go +++ b/rotatefile/writer_test.go @@ -5,12 +5,10 @@ import ( "testing" "time" - "github.com/gookit/goutil" "github.com/gookit/goutil/dump" "github.com/gookit/goutil/fsutil" "github.com/gookit/goutil/mathutil" "github.com/gookit/goutil/testutil/assert" - "github.com/gookit/goutil/timex" "github.com/gookit/slog/rotatefile" ) @@ -140,27 +138,3 @@ func (c constantClock) Now() time.Time { return time.Time(c) } func (c constantClock) NewTicker(d time.Duration) *time.Ticker { return &time.Ticker{} } - -type mockTime struct { - tt time.Time -} - -// newMockTime create a mock time instance from datetime string. -func newMockTime(datetime string) *mockTime { - nt := goutil.Must(timex.FromString(datetime)) - return &mockTime{tt: nt.Time} -} - -func (mt *mockTime) Now() time.Time { - return mt.tt -} - -// Add progresses time by the given duration. -func (mt *mockTime) Add(d time.Duration) { - mt.tt = mt.tt.Add(d) -} - -// Datetime returns the current time in the format "2006-01-02 15:04:05". -func (mt *mockTime) Datetime() string { - return mt.tt.Format("2006-01-02 15:04:05") -}