From 9a74d973565a83d6d46268c9cab60891fe249ffa Mon Sep 17 00:00:00 2001 From: aeris Date: Fri, 15 Aug 2025 14:21:55 +0200 Subject: [PATCH 1/2] Move to timberjack and add rotate-at methods --- go.mod | 1 + go.sum | 6 +++ modules/logging/filewriter.go | 85 ++++++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index d51e9308f05..6c567ed9a53 100644 --- a/go.mod +++ b/go.mod @@ -53,6 +53,7 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.8.0 // indirect dario.cat/mergo v1.0.1 // indirect + github.com/DeRuina/timberjack v1.3.7 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/ccoveille/go-safecast v1.6.1 // indirect diff --git a/go.sum b/go.sum index 6a12e07a5bc..43f314fb2f9 100644 --- a/go.sum +++ b/go.sum @@ -32,6 +32,10 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOv github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/DeRuina/timberjack v1.3.5 h1:a8QrIyFCmnx6sjqmroFTIXcmNcen69dlx8DBRmN8+qU= +github.com/DeRuina/timberjack v1.3.5/go.mod h1:oir/YwhAK3URX7u4CDuMzdVQtLQqc1l3M/vJ6WBsjZs= +github.com/DeRuina/timberjack v1.3.7 h1:+eP0+cxzwOh8RMg5ByHHZ/abrtDiEk/F5B7JYwCvUJ0= +github.com/DeRuina/timberjack v1.3.7/go.mod h1:RLoeQrwrCGIEF8gO5nV5b/gMD0QIy7bzQhBUgpp1EqE= github.com/KimMachineGun/automemlimit v0.7.4 h1:UY7QYOIfrr3wjjOAqahFmC3IaQCLWvur9nmfIn6LnWk= github.com/KimMachineGun/automemlimit v0.7.4/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -149,6 +153,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= diff --git a/modules/logging/filewriter.go b/modules/logging/filewriter.go index 0999bbfb27f..e7759fd894e 100644 --- a/modules/logging/filewriter.go +++ b/modules/logging/filewriter.go @@ -22,9 +22,11 @@ import ( "os" "path/filepath" "strconv" + "strings" + "time" + "github.com/DeRuina/timberjack" "github.com/dustin/go-humanize" - "gopkg.in/natefinch/lumberjack.v2" "github.com/caddyserver/caddy/v2" "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile" @@ -96,6 +98,15 @@ type FileWriter struct { // it will be rotated. RollSizeMB int `json:"roll_size_mb,omitempty"` + // Roll log file after some time + RollInterval time.Duration `json:"roll_interval,omitempty` + + // Roll log file at fix minutes + RollAtMinutes []int `json:"roll_minutes,omitempty` + + // Roll log file at fix time + RollAt []string `json:"roll_at,omitempty` + // Whether to compress rolled files. Default: true RollCompress *bool `json:"roll_gzip,omitempty"` @@ -109,6 +120,9 @@ type FileWriter struct { // How many days to keep rolled log files. Default: 90 RollKeepDays int `json:"roll_keep_days,omitempty"` + + // Rotated file will have format --.log + BackupTimeFormat string `json:"backup_time_format,omitempty"` } // CaddyModule returns the Caddy module information. @@ -156,7 +170,7 @@ func (fw FileWriter) OpenWriter() (io.WriteCloser, error) { roll := fw.Roll == nil || *fw.Roll // create the file if it does not exist; create with the configured mode, or default - // to restrictive if not set. (lumberjack will reuse the file mode across log rotation) + // to restrictive if not set. (timberjack will reuse the file mode across log rotation) if err := os.MkdirAll(filepath.Dir(fw.Filename), 0o700); err != nil { return nil, err } @@ -166,7 +180,7 @@ func (fw FileWriter) OpenWriter() (io.WriteCloser, error) { } info, err := file.Stat() if roll { - file.Close() // lumberjack will reopen it on its own + file.Close() // timberjack will reopen it on its own } // Ensure already existing files have the right mode, since OpenFile will not set the mode in such case. @@ -201,13 +215,17 @@ func (fw FileWriter) OpenWriter() (io.WriteCloser, error) { if fw.RollKeepDays == 0 { fw.RollKeepDays = 90 } - return &lumberjack.Logger{ - Filename: fw.Filename, - MaxSize: fw.RollSizeMB, - MaxAge: fw.RollKeepDays, - MaxBackups: fw.RollKeep, - LocalTime: fw.RollLocalTime, - Compress: *fw.RollCompress, + return &timberjack.Logger{ + Filename: fw.Filename, + MaxSize: fw.RollSizeMB, + MaxAge: fw.RollKeepDays, + MaxBackups: fw.RollKeep, + LocalTime: fw.RollLocalTime, + Compress: *fw.RollCompress, + RotationInterval: fw.RollInterval, + RotateAtMinutes: fw.RollAtMinutes, + RotateAt: fw.RollAt, + BackupTimeFormat: fw.BackupTimeFormat, }, nil } @@ -314,6 +332,53 @@ func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { } fw.RollKeepDays = int(math.Ceil(keepFor.Hours() / 24)) + case "roll_interval": + var durationStr string + if !d.AllArgs(&durationStr) { + return d.ArgErr() + } + duration, err := time.ParseDuration(durationStr) + if err != nil { + return d.Errf("parsing roll_interval duration: %v", err) + } + fw.RollInterval = duration + + case "roll_minutes": + var minutesArrayStr string + if !d.AllArgs(&minutesArrayStr) { + return d.ArgErr() + } + minutesStr := strings.Split(minutesArrayStr, ",") + minutes := make([]int, len(minutesStr)) + for i := range minutesStr { + ms := strings.Trim(minutesStr[i], " ") + m, err := strconv.Atoi(ms) + if err != nil { + return d.Errf("parsing roll_minutes number: %v", err) + } + minutes[i] = m + } + fw.RollAtMinutes = minutes + + case "roll_at": + var timeArrayStr string + if !d.AllArgs(&timeArrayStr) { + return d.ArgErr() + } + timeStr := strings.Split(timeArrayStr, ",") + times := make([]string, len(timeStr)) + for i := range timeStr { + times[i] = strings.Trim(timeStr[i], " ") + } + fw.RollAt = times + + case "backup_time_format": + var format string + if !d.AllArgs(&format) { + return d.ArgErr() + } + fw.BackupTimeFormat = format + default: return d.Errf("unrecognized subdirective '%s'", d.Val()) } From 1a412344a82164f94abf873e3664ef156e7b7906 Mon Sep 17 00:00:00 2001 From: aeris Date: Tue, 30 Sep 2025 21:17:15 +0200 Subject: [PATCH 2/2] More docs --- modules/logging/filewriter.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/logging/filewriter.go b/modules/logging/filewriter.go index e7759fd894e..c3df562cbe5 100644 --- a/modules/logging/filewriter.go +++ b/modules/logging/filewriter.go @@ -99,13 +99,19 @@ type FileWriter struct { RollSizeMB int `json:"roll_size_mb,omitempty"` // Roll log file after some time - RollInterval time.Duration `json:"roll_interval,omitempty` + RollInterval time.Duration `json:"roll_interval,omitempty"` // Roll log file at fix minutes - RollAtMinutes []int `json:"roll_minutes,omitempty` + // For example []int{0, 30} will roll file at xx:00 and xx:30 each hour + // Invalid value are ignored with a warning on stderr + // See https://github.com/DeRuina/timberjack#%EF%B8%8F-rotation-notes--warnings for caveats + RollAtMinutes []int `json:"roll_minutes,omitempty"` // Roll log file at fix time - RollAt []string `json:"roll_at,omitempty` + // For example []string{"00:00", "12:00"} will roll file at 00:00 and 12:00 each day + // Invalid value are ignored with a warning on stderr + // See https://github.com/DeRuina/timberjack#%EF%B8%8F-rotation-notes--warnings for caveats + RollAt []string `json:"roll_at,omitempty"` // Whether to compress rolled files. Default: true RollCompress *bool `json:"roll_gzip,omitempty"` @@ -122,6 +128,8 @@ type FileWriter struct { RollKeepDays int `json:"roll_keep_days,omitempty"` // Rotated file will have format --.log + // Optional. If unset or invalid, defaults to 2006-01-02T15-04-05.000 (with fallback warning) + // must be a Go time compatible format, see https://pkg.go.dev/time#pkg-constants BackupTimeFormat string `json:"backup_time_format,omitempty"` }