bldr/util/logfile/prune.go
Rama master · réplica de lectura del repositorio original.
package logfile
import (
"os"
"path/filepath"
"slices"
"strings"
"time"
)
// DefaultKeepLogs is the number of newest prior logs kept regardless of age.
// Each process start writes a new log, so frequent restarts would otherwise
// accumulate logs for the whole retention window.
const DefaultKeepLogs = 8
// PruneOldLogs deletes regular .log files under dir whose mtime is older
// than maxAge relative to now, and all but the keep newest of the rest.
// A non-positive keep disables the count limit. Logs held open by a running
// writer (see AttachLogFiles) are never removed. Returns the number of files
// removed and the first error encountered (other failures are still
// attempted).
//
// Non-existent dir is treated as success with zero removed files.
// Subdirectories and non-.log entries are skipped. Symlinks are not
// followed; their own metadata is consulted via os.Lstat semantics from
// os.ReadDir's DirEntry.
func PruneOldLogs(dir string, maxAge time.Duration, keep int, now time.Time) (int, error) {
// Read the log directory, treating a missing directory as empty.
entries, err := os.ReadDir(dir)
if err != nil {
if os.IsNotExist(err) {
return 0, nil
}
return 0, err
}
// Collect regular log files; a failed stat is reported but skipped.
var logs []os.FileInfo
var firstErr error
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".log") {
continue
}
info, err := entry.Info()
if err != nil {
if firstErr == nil {
firstErr = err
}
continue
}
if info.Mode().IsRegular() {
logs = append(logs, info)
}
}
// Newest first: keep the first keep logs younger than the cutoff.
slices.SortFunc(logs, func(a, b os.FileInfo) int {
return b.ModTime().Compare(a.ModTime())
})
cutoff := now.Add(-maxAge)
var removed int
for i, info := range logs {
if !info.ModTime().Before(cutoff) && (keep <= 0 || i < keep) {
continue
}
// Skip logs still held open by a running writer (e.g. a daemon).
path := filepath.Join(dir, info.Name())
if logInUse(path) {
continue
}
if err := os.Remove(path); err != nil {
if firstErr == nil {
firstErr = err
}
continue
}
removed++
}
return removed, firstErr
}