Skip to content

Commit

Permalink
feat: keep reserved space for backup on device (natefinch#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
sejust committed Mar 21, 2024
1 parent 4cb27fc commit 466800a
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 5 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ type Logger struct {
// deleted.)
MaxBackups int `json:"maxbackups" yaml:"maxbackups"`

// ReservedSize is the minimum left space in megabytes of the store device.
// Do not to check the available space of the device.
ReservedSize int `json:"reservedsize" yaml:"reservedsize"`

// LocalTime determines if the time used for formatting the timestamps in
// backup files is the computer's local time. The default is to use UTC
// time.
Expand Down Expand Up @@ -107,8 +111,9 @@ number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
with an encoded timestamp older than MaxAge days are deleted, regardless of
MaxBackups. Note that the time encoded in the timestamp is the rotation
time, which may differ from the last time that file was written to.
The older files will be deleted until there's enough ReservedSize space left.

If MaxBackups and MaxAge are both 0, no old log files will be deleted.
If MaxBackups and MaxAge and ReservedSize are all 0, no old log files will be deleted.



Expand Down
37 changes: 33 additions & 4 deletions lumberjack.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
// thusly:
//
// import "gopkg.in/natefinch/lumberjack.v2"
// import "gopkg.in/natefinch/lumberjack.v2"
//
// The package name remains simply lumberjack, and the code resides at
// https://github.com/natefinch/lumberjack under the v2.0 branch.
Expand Down Expand Up @@ -32,6 +32,7 @@ import (
"sort"
"strings"
"sync"
"syscall"
"time"
)

Expand Down Expand Up @@ -66,16 +67,18 @@ var _ io.WriteCloser = (*Logger)(nil)
// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would
// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log`
//
// Cleaning Up Old Log Files
// # Cleaning Up Old Log Files
//
// Whenever a new logfile gets created, old log files may be deleted. The most
// recent files according to the encoded timestamp will be retained, up to a
// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
// with an encoded timestamp older than MaxAge days are deleted, regardless of
// MaxBackups. Note that the time encoded in the timestamp is the rotation
// time, which may differ from the last time that file was written to.
// The older files will be deleted until there's enough ReservedSize space left.
//
// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
// If MaxBackups and MaxAge and ReservedSize are all 0, no old log files will be deleted.
type Logger struct {
// Filename is the file to write logs to. Backup log files will be retained
// in the same directory. It uses <processname>-lumberjack.log in
Expand All @@ -98,6 +101,10 @@ type Logger struct {
// deleted.)
MaxBackups int `json:"maxbackups" yaml:"maxbackups"`

// ReservedSize is the minimum left space in megabytes of the store device.
// Do not to check the available space of the device.
ReservedSize int `json:"reservedsize" yaml:"reservedsize"`

// LocalTime determines if the time used for formatting the timestamps in
// backup files is the computer's local time. The default is to use UTC
// time.
Expand Down Expand Up @@ -300,9 +307,9 @@ func (l *Logger) filename() string {
// millRunOnce performs compression and removal of stale log files.
// Log files are compressed if enabled via configuration and old log
// files are removed, keeping at most l.MaxBackups files, as long as
// none of them are older than MaxAge.
// none of them are older than MaxAge, or keep reserved space in device.
func (l *Logger) millRunOnce() error {
if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress {
if l.MaxBackups == 0 && l.MaxAge == 0 && l.ReservedSize == 0 && !l.Compress {
return nil
}

Expand Down Expand Up @@ -347,6 +354,28 @@ func (l *Logger) millRunOnce() error {
}
files = remaining
}
if reserved := l.ReservedSize * megabyte; reserved > 0 && len(files) > 0 {
var fs syscall.Statfs_t
errStat := syscall.Statfs(l.Filename, &fs)
if errStat != nil {
if err == nil {
err = errStat
}
} else {
avail := int(fs.Bavail) * int(fs.Bsize)
var remaining []logInfo
for idx := len(files) - 1; idx >= 0; idx-- {
f := files[idx]
if avail < reserved {
avail += int(f.Size())
remove = append(remove, f)
} else {
remaining = append(remaining, f)
}
}
files = remaining
}
}

if l.Compress {
for _, f := range files {
Expand Down
24 changes: 24 additions & 0 deletions lumberjack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,6 +587,30 @@ func TestRotate(t *testing.T) {
existsWithContent(filename, b2, t)
}

func TestReservedSize(t *testing.T) {
currentTime = fakeTime
megabyte = 1 << 20
dir := makeTempDir("TestReservedSize", t)
defer os.RemoveAll(dir)

l := &Logger{
Filename: logFile(dir),
ReservedSize: 1 << 30, // large enough reserved size
LocalTime: true,
}
defer l.Close()
b := []byte("boo!")
n, err := l.Write(b)
isNil(err, t)
equals(len(b), n, t)

err = l.Rotate()
isNil(err, t)

<-time.After(20 * time.Millisecond)
notExist(backupFileLocal(dir), t)
}

func TestCompressOnRotate(t *testing.T) {
currentTime = fakeTime
megabyte = 1
Expand Down

0 comments on commit 466800a

Please sign in to comment.