@marcel91200 / caddy Mirror · read-only / commits / 31daa220
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 11, 08:30 UTC

log: support `d` (day) in `roll_interval` directive (#7900)

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>

Mohammed Al Sahaf committed Jul 22, 2026 at 01:39 UTC 31daa220fcba80c7ed7ea636e7f04c568c8cccf8
2 files changed +71 -4
modules/logging/filewriter.go
+4 -4
@@ -108,7 +108,7 @@ type FileWriter struct {
108 RollSizeMB int `json:"roll_size_mb,omitempty"`
109
110 // Roll log file after some time
111 - RollInterval time.Duration `json:"roll_interval,omitempty"`
111 + RollInterval caddy.Duration `json:"roll_interval,omitempty"`
112
113 // Roll log file at fix minutes
114 // For example []int{0, 30} will roll file at xx:00 and xx:30 each hour
@@ -287,7 +287,7 @@ func (fw FileWriter) OpenWriter() (io.WriteCloser, error) {
287 MaxBackups: fw.RollKeep,
288 LocalTime: fw.RollLocalTime,
289 Compression: compression,
290 - RotationInterval: fw.RollInterval,
290 + RotationInterval: time.Duration(fw.RollInterval),
291 RotateAtMinutes: fw.RollAtMinutes,
292 RotateAt: fw.RollAt,
293 BackupTimeFormat: fw.BackupTimeFormat,
@@ -489,11 +489,11 @@ func (fw *FileWriter) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
489 if !d.AllArgs(&durationStr) {
490 return d.ArgErr()
491 }
492 - duration, err := time.ParseDuration(durationStr)
492 + duration, err := caddy.ParseDuration(durationStr)
493 if err != nil {
494 return d.Errf("parsing roll_interval duration: %v", err)
495 }
496 - fw.RollInterval = duration
496 + fw.RollInterval = caddy.Duration(duration)
497
498 case "roll_minutes":
499 // Accept either a single comma-separated argument or
modules/logging/filewriter_test.go
+67
@@ -23,6 +23,7 @@ import (
23 "path/filepath"
24 "syscall"
25 "testing"
26 + "time"
27
28 "github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
29 )
@@ -648,3 +649,69 @@ file /var/log/app.log {
649 t.Fatal("expected error for invalid dir_mode")
650 }
651 }
652 +
653 +func TestCaddyfile_RollInterval(t *testing.T) {
654 + tests := []struct {
655 + name string
656 + input string
657 + wantSecs float64
658 + wantErr bool
659 + }{
660 + {
661 + name: "day unit",
662 + input: `file /var/log/app.log {
663 + roll_interval 1d
664 +}`,
665 + wantSecs: 86400,
666 + },
667 + {
668 + name: "hours unit",
669 + input: `file /var/log/app.log {
670 + roll_interval 24h
671 +}`,
672 + wantSecs: 86400,
673 + },
674 + {
675 + name: "minutes unit",
676 + input: `file /var/log/app.log {
677 + roll_interval 30m
678 +}`,
679 + wantSecs: 1800,
680 + },
681 + {
682 + name: "fractional days",
683 + input: `file /var/log/app.log {
684 + roll_interval 1.5d
685 +}`,
686 + wantSecs: 129600,
687 + },
688 + {
689 + name: "invalid duration",
690 + input: `file /var/log/app.log {
691 + roll_interval bogus
692 +}`,
693 + wantErr: true,
694 + },
695 + }
696 +
697 + for _, tt := range tests {
698 + t.Run(tt.name, func(t *testing.T) {
699 + d := caddyfile.NewTestDispenser(tt.input)
700 + var fw FileWriter
701 + err := fw.UnmarshalCaddyfile(d)
702 + if tt.wantErr {
703 + if err == nil {
704 + t.Fatal("expected error but got none")
705 + }
706 + return
707 + }
708 + if err != nil {
709 + t.Fatalf("unexpected error: %v", err)
710 + }
711 + gotSecs := time.Duration(fw.RollInterval).Seconds()
712 + if gotSecs != tt.wantSecs {
713 + t.Errorf("got %v seconds, want %v", gotSecs, tt.wantSecs)
714 + }
715 + })
716 + }
717 +}