diff --git a/rotatelogs.go b/rotatelogs.go index 2003ae4..a4e9f8f 100644 --- a/rotatelogs.go +++ b/rotatelogs.go @@ -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 @@ -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, @@ -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: @@ -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 @@ -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 { diff --git a/rotatelogs_test.go b/rotatelogs_test.go index ae12b9b..30cb4f3 100644 --- a/rotatelogs_test.go +++ b/rotatelogs_test.go @@ -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 { @@ -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)) +}