// Package logdaily 提供按天滚动的日志写入器(供 go-zero logx.SetWriter 使用) package logdaily import ( "io" "os" "path/filepath" "strings" "sync" "time" ) type dailyWriter struct { mu sync.Mutex dir string base string keepDays int date string f *os.File } // New 创建按天滚动的写入器:文件名 dir/base-2006-01-02.log,跨天自动切换, // 超过 keepDays 天的旧日志在跨天时清理(keepDays<=0 表示永久保留)。 func New(dir, base string, keepDays int) io.WriteCloser { return &dailyWriter{dir: dir, base: base, keepDays: keepDays} } func (w *dailyWriter) filename(date string) string { return filepath.Join(w.dir, w.base+"-"+date+".log") } func (w *dailyWriter) Write(p []byte) (int, error) { w.mu.Lock() defer w.mu.Unlock() today := time.Now().Format("2006-01-02") if w.f == nil || today != w.date { if w.f != nil { _ = w.f.Close() } if err := os.MkdirAll(w.dir, 0o755); err != nil { return 0, err } f, err := os.OpenFile(w.filename(today), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644) if err != nil { return 0, err } w.f = f w.date = today w.cleanup() } return w.f.Write(p) } func (w *dailyWriter) Close() error { w.mu.Lock() defer w.mu.Unlock() if w.f != nil { return w.f.Close() } return nil } func (w *dailyWriter) cleanup() { if w.keepDays <= 0 { return } entries, err := os.ReadDir(w.dir) if err != nil { return } cutoff := time.Now().AddDate(0, 0, -w.keepDays) for _, e := range entries { if e.IsDir() || !strings.HasPrefix(e.Name(), w.base+"-") || !strings.HasSuffix(e.Name(), ".log") { continue } datePart := strings.TrimSuffix(strings.TrimPrefix(e.Name(), w.base+"-"), ".log") t, err := time.ParseInLocation("2006-01-02", datePart, time.Local) if err != nil { continue } if t.Before(cutoff) { _ = os.Remove(filepath.Join(w.dir, e.Name())) } } } var ( _ io.WriteCloser = (*dailyWriter)(nil) _ io.Writer = (*dailyWriter)(nil) )