package logx import ( "io" "os" "path/filepath" "strings" "sync" "time" "gopkg.in/natefinch/lumberjack.v2" ) // dailyWriter 按天滚动的日志写入器:文件名 base-2006-01-02.log,跨天自动切换; // 超过 MaxAge 天的日志文件在跨天时清理。 type dailyWriter struct { mu sync.Mutex conf LogConf date string lj *lumberjack.Logger } func newDailyWriter(conf LogConf) *dailyWriter { return &dailyWriter{conf: conf} } func (w *dailyWriter) filename(date string) string { ext := filepath.Ext(w.conf.Filename) base := strings.TrimSuffix(w.conf.Filename, ext) return base + "-" + date + ext } func (w *dailyWriter) Write(p []byte) (int, error) { w.mu.Lock() defer w.mu.Unlock() today := time.Now().Format("2006-01-02") if w.lj == nil || today != w.date { if w.lj != nil { _ = w.lj.Close() } w.date = today w.lj = &lumberjack.Logger{ Filename: w.filename(today), MaxSize: w.conf.MaxSize, MaxBackups: w.conf.MaxBackups, MaxAge: w.conf.MaxAge, Compress: w.conf.Compress, } w.cleanup() } return w.lj.Write(p) } func (w *dailyWriter) Close() error { w.mu.Lock() defer w.mu.Unlock() if w.lj != nil { return w.lj.Close() } return nil } // cleanup 清理超过 MaxAge 天的按天日志文件 func (w *dailyWriter) cleanup() { if w.conf.MaxAge <= 0 { return } dir := filepath.Dir(w.conf.Filename) ext := filepath.Ext(w.conf.Filename) base := strings.TrimSuffix(filepath.Base(w.conf.Filename), ext) entries, err := os.ReadDir(dir) if err != nil { return } cutoff := time.Now().AddDate(0, 0, -w.conf.MaxAge) for _, e := range entries { if e.IsDir() || !strings.HasPrefix(e.Name(), base+"-") || !strings.HasSuffix(e.Name(), ext) { continue } datePart := strings.TrimSuffix(strings.TrimPrefix(e.Name(), base+"-"), ext) t, err := time.ParseInLocation("2006-01-02", datePart, time.Local) if err != nil { continue } if t.Before(cutoff) { _ = os.Remove(filepath.Join(dir, e.Name())) } } } var ( _ io.WriteCloser = (*dailyWriter)(nil) _ io.Writer = (*dailyWriter)(nil) )