79 lines
1.7 KiB
Go
79 lines
1.7 KiB
Go
package logging
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// DailyWriter writes log entries to <root>/<yyyy-mm-dd>/<name>. It keeps the
|
|
// file handle open for the active day and rotates on the first write after
|
|
// midnight, matching the directory layout consumed by the log viewer.
|
|
type DailyWriter struct {
|
|
mu sync.Mutex
|
|
root string
|
|
name string
|
|
date string
|
|
file *os.File
|
|
retentionDay int
|
|
}
|
|
|
|
func NewDailyWriter(root, name string, retentionDay int) *DailyWriter {
|
|
w := &DailyWriter{root: root, name: name, retentionDay: retentionDay}
|
|
w.removeExpired()
|
|
return w
|
|
}
|
|
|
|
func (w *DailyWriter) removeExpired() {
|
|
if w.retentionDay <= 0 {
|
|
return
|
|
}
|
|
entries, err := os.ReadDir(w.root)
|
|
if err != nil {
|
|
return
|
|
}
|
|
cutoff := time.Now().AddDate(0, 0, -w.retentionDay)
|
|
for _, entry := range entries {
|
|
if !entry.IsDir() {
|
|
continue
|
|
}
|
|
date, err := time.Parse("2006-01-02", entry.Name())
|
|
if err == nil && date.Before(cutoff) {
|
|
_ = os.RemoveAll(filepath.Join(w.root, entry.Name()))
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *DailyWriter) Write(value []byte) (int, error) {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
date := time.Now().Format("2006-01-02")
|
|
if w.file == nil || w.date != date {
|
|
if w.file != nil {
|
|
_ = w.file.Close()
|
|
}
|
|
directory := filepath.Join(w.root, date)
|
|
if err := os.MkdirAll(directory, 0o755); err != nil {
|
|
return 0, err
|
|
}
|
|
file, err := os.OpenFile(filepath.Join(directory, w.name), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0o644)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
w.file, w.date = file, date
|
|
}
|
|
return w.file.Write(value)
|
|
}
|
|
|
|
func (w *DailyWriter) Close() error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
if w.file == nil {
|
|
return nil
|
|
}
|
|
err := w.file.Close()
|
|
w.file = nil
|
|
return err
|
|
}
|