Skip to content

Commit

Permalink
enh: support maxAge and rotateSize
Browse files Browse the repository at this point in the history
  • Loading branch information
huskar-t committed Sep 14, 2024
1 parent 4d2fc56 commit 2e3c182
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 35 deletions.
57 changes: 23 additions & 34 deletions rotatelogs.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func New(p string, options ...Option) (*RotateLogs, error) {
}
var clock Clock = Local
rotationTime := 24 * time.Hour
var rotationSize int64
var rotationSize int64 = 1 * 1024 * 1024 * 1024 // 1GB
var rotationCount uint
var linkName string
var maxAge time.Duration
Expand Down Expand Up @@ -96,15 +96,6 @@ func New(p string, options ...Option) (*RotateLogs, error) {
}
}

if maxAge > 0 && rotationCount > 0 {
return nil, errors.New("options MaxAge and RotationCount cannot be both set")
}

if maxAge == 0 && rotationCount == 0 {
// if both are 0, give maxAge a sane default
maxAge = 7 * 24 * time.Hour
}

rl := &RotateLogs{
clock: clock,
eventHandler: handler,
Expand Down Expand Up @@ -453,9 +444,6 @@ func (rl *RotateLogs) rotateNolock(filename string) error {
}
}

if rl.maxAge <= 0 && rl.rotationCount <= 0 {
return errors.New("panic: maxAge and rotationCount are both set")
}
select {
case rl.rotateCleanChan <- struct{}{}:
default:
Expand Down Expand Up @@ -490,9 +478,10 @@ func (rl *RotateLogs) rotateClean() error {
return err
}

cutoff := rl.clock.Now().Add(-1 * rl.maxAge)
cutoff := time.Now().Add(-1 * rl.maxAge)
var toCompress []string
toCleanFl := make([]os.FileInfo, 0, len(matches))
totalFiles := make([]os.FileInfo, 0, len(matches))
outDateFiles := make([]os.FileInfo, 0, len(matches))
m := make(map[os.FileInfo]string)
for _, p := range matches {
// Ignore lock files and tmp files
Expand All @@ -517,33 +506,33 @@ func (rl *RotateLogs) rotateClean() error {
continue
}
toCompress = append(toCompress, p)
if rl.maxAge > 0 && fi.ModTime().After(cutoff) {
continue
if rl.maxAge > 0 && fi.ModTime().Compare(cutoff) <= 0 {
m[fl] = p
outDateFiles = append(outDateFiles, fl)
}

if rl.rotationCount > 0 && fl.Mode()&os.ModeSymlink == os.ModeSymlink {
continue
if rl.rotationCount > 0 && fl.Mode()&os.ModeSymlink != os.ModeSymlink {
m[fl] = p
totalFiles = append(totalFiles, fl)
}
m[fl] = p
toCleanFl = append(toCleanFl, fl)
}
// sort by mod time
sort.Slice(toCleanFl, func(i, j int) bool {
return toCleanFl[i].ModTime().Before(toCleanFl[j].ModTime())
sort.Slice(totalFiles, func(i, j int) bool {
return totalFiles[i].ModTime().Before(totalFiles[j].ModTime())
})
toClean := make([]string, 0, len(toCleanFl))
for _, info := range toCleanFl {
toClean = append(toClean, m[info])
overRotationSizeFiles := make([]os.FileInfo, 0, len(totalFiles))
if rl.rotationCount > 0 && uint(len(totalFiles)) > rl.rotationCount {
overRotationSizeFiles = totalFiles[:len(totalFiles)-int(rl.rotationCount)]
}

var toRemove []string
if rl.rotationCount > 0 && uint(len(toClean)) > rl.rotationCount {
// Only delete if we have more than rotationCount
toRemove = toClean[:len(toClean)-int(rl.rotationCount)]
}

if rl.maxAge > 0 {
toRemove = toClean
if len(overRotationSizeFiles) > len(outDateFiles) {
for i := 0; i < len(overRotationSizeFiles); i++ {
toRemove = append(toRemove, m[overRotationSizeFiles[i]])
}
} else {
for i := 0; i < len(outDateFiles); i++ {
toRemove = append(toRemove, m[outDateFiles[i]])
}
}

for _, path := range toRemove {
Expand Down
161 changes: 160 additions & 1 deletion rotatelogs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func TestLogRotationCount(t *testing.T) {
rotatelogs.WithMaxAge(1),
rotatelogs.WithRotationCount(1),
)
if !assert.Error(t, err, `Both of maxAge and rotationCount is enabled`) {
if !assert.NoError(t, err) {
return
}
if rl != nil {
Expand Down Expand Up @@ -650,3 +650,162 @@ func TestReservedDiskSize(t *testing.T) {
}
rl.Close()
}

func TestWithMaxAge(t *testing.T) {
dir, err := ioutil.TempDir("", "file-rotatelogs-maxage")
if !assert.NoError(t, err, `creating temporary directory should succeed`) {
return
}
defer os.RemoveAll(dir)
dummyTime := time.Now().Add(-24 * time.Hour)
clock := clockwork.NewFakeClockAt(dummyTime)
rl, err := rotatelogs.New(
filepath.Join(dir, "rotationTime_%Y%m%d%H%M%S.log"),
rotatelogs.WithClock(clock),
rotatelogs.WithRotationTime(24*time.Hour),
rotatelogs.WithMaxAge(time.Second*2),
rotatelogs.WithRotationCount(5),
)
if !assert.NoError(t, err, "rotatelogs.New should succeed") {
return
}
_, err = rl.Write([]byte("Hello, World!"))
if !assert.NoError(t, err) {
return
}
files, err := filepath.Glob(filepath.Join(dir, "rotationTime_*.log"))
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, 1, len(files)) {
return
}
time.Sleep(time.Second * 2)
lastFile := files[0]
for i := 0; i < 5; i++ {
clock.Advance(24 * time.Hour)
_, err = rl.Write([]byte("Hello, World!"))
if !assert.NoError(t, err) {
return
}
time.Sleep(time.Second * 2)
files, err = filepath.Glob(filepath.Join(dir, "rotationTime_*.log*"))
if !assert.NoError(t, err) {
return
}
var filteredFiles []string
for _, file := range files {
if !strings.HasSuffix(file, "_lock") {
filteredFiles = append(filteredFiles, file)
}
}
t.Log(len(filteredFiles), files)
if !assert.Equal(t, 1, len(filteredFiles)) {
return
}
if !assert.NotEqual(t, lastFile, filteredFiles[0]) {
return
}
lastFile = filteredFiles[0]
}
}

func TestWithRotateSize(t *testing.T) {
dir, err := ioutil.TempDir("", "file-rotatelogs-rotatesize")
if !assert.NoError(t, err, `creating temporary directory should succeed`) {
return
}
defer os.RemoveAll(dir)
dummyTime := time.Now().Add(-24 * time.Hour)
clock := clockwork.NewFakeClockAt(dummyTime)
rl, err := rotatelogs.New(
filepath.Join(dir, "rotationTime_%Y%m%d%H%M%S.log"),
rotatelogs.WithClock(clock),
rotatelogs.WithRotationTime(24*time.Hour),
rotatelogs.WithMaxAge(time.Second*6),
rotatelogs.WithRotationCount(2),
)
if !assert.NoError(t, err, "rotatelogs.New should succeed") {
return
}
_, err = rl.Write([]byte("Hello, World!"))
if !assert.NoError(t, err) {
return
}
files, err := filepath.Glob(filepath.Join(dir, "rotationTime_*.log"))
if !assert.NoError(t, err) {
return
}
if !assert.Equal(t, 1, len(files)) {
return
}
time.Sleep(time.Second * 2)
lastFiles := make([]string, 2)
for i := 0; i < 5; i++ {
clock.Advance(24 * time.Hour)
_, err = rl.Write([]byte("Hello, World!"))
if !assert.NoError(t, err) {
return
}
time.Sleep(time.Second * 2)
files, err = filepath.Glob(filepath.Join(dir, "rotationTime_*.log*"))
if !assert.NoError(t, err) {
return
}
var filteredFiles []string
for _, file := range files {
if !strings.HasSuffix(file, "_lock") {
filteredFiles = append(filteredFiles, file)
}
}
t.Log(len(filteredFiles), files)
if !assert.Equal(t, 2, len(filteredFiles)) {
return
}
if i != 0 {
if !assert.NotEqual(t, lastFiles, filteredFiles) {
return
}
}
lastFiles = filteredFiles
}
}

func TestForceNewFile2(t *testing.T) {
dir, err := ioutil.TempDir("", "file-rotatelogs-forcenewfile")
if !assert.NoError(t, err, `creating temporary directory should succeed`) {
return
}
defer os.RemoveAll(dir)
f, err := os.Create(filepath.Join(dir, "logfile.log"))
if !assert.NoError(t, err) {
return
}
err = f.Close()
if !assert.NoError(t, err) {
return
}
rl, err := rotatelogs.New(
filepath.Join(dir, "logfile.log"),
rotatelogs.ForceNewFile(),
)
if !assert.NoError(t, err, "rotatelogs.New should succeed") {
return
}
_, err = rl.Write([]byte("Hello, World!"))
if !assert.NoError(t, err) {
return
}
files, err := filepath.Glob(filepath.Join(dir, "logfile.log*"))
if !assert.NoError(t, err) {
return
}
var filteredFiles []string
for _, file := range files {
if !strings.HasSuffix(file, "_lock") {
filteredFiles = append(filteredFiles, file)
}
}
t.Log(len(filteredFiles), files)
assert.Equal(t, 2, len(filteredFiles))
}

0 comments on commit 2e3c182

Please sign in to comment.