cmd/syncthing: Do not truncate/rotate logs at start (#6359)

This commit is contained in:
Simon Frei 2020-02-26 13:49:03 +01:00 committed by GitHub
parent 4e4b9a872a
commit 299a80d328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 69 additions and 52 deletions

View File

@ -55,28 +55,32 @@ func monitorMain(runtimeOptions RuntimeOptions) {
logFile = expanded logFile = expanded
} }
var fileDst io.Writer var fileDst io.Writer
var err error
open := func(name string) (io.WriteCloser, error) {
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime)
}
if runtimeOptions.logMaxSize > 0 { if runtimeOptions.logMaxSize > 0 {
open := func(name string) (io.WriteCloser, error) { fileDst, err = newRotatedFile(logFile, open, int64(runtimeOptions.logMaxSize), runtimeOptions.logMaxFiles)
return newAutoclosedFile(name, logFileAutoCloseDelay, logFileMaxOpenTime), nil
}
fileDst = newRotatedFile(logFile, open, int64(runtimeOptions.logMaxSize), runtimeOptions.logMaxFiles)
} else { } else {
fileDst = newAutoclosedFile(logFile, logFileAutoCloseDelay, logFileMaxOpenTime) fileDst, err = open(logFile)
} }
if err != nil {
if runtime.GOOS == "windows" { l.Warnln("Failed to setup logging to file, proceeding with logging to stdout only:", err)
// Translate line breaks to Windows standard } else {
fileDst = osutil.ReplacingWriter{ if runtime.GOOS == "windows" {
Writer: fileDst, // Translate line breaks to Windows standard
From: '\n', fileDst = osutil.ReplacingWriter{
To: []byte{'\r', '\n'}, Writer: fileDst,
From: '\n',
To: []byte{'\r', '\n'},
}
} }
// Log to both stdout and file.
dst = io.MultiWriter(dst, fileDst)
l.Infof(`Log output saved to file "%s"`, logFile)
} }
// Log to both stdout and file.
dst = io.MultiWriter(dst, fileDst)
l.Infof(`Log output saved to file "%s"`, logFile)
} }
args := os.Args args := os.Args
@ -353,16 +357,30 @@ type rotatedFile struct {
currentSize int64 currentSize int64
} }
// the createFn should act equivalently to os.Create
type createFn func(name string) (io.WriteCloser, error) type createFn func(name string) (io.WriteCloser, error)
func newRotatedFile(name string, create createFn, maxSize int64, maxFiles int) *rotatedFile { func newRotatedFile(name string, create createFn, maxSize int64, maxFiles int) (*rotatedFile, error) {
return &rotatedFile{ var size int64
name: name, if info, err := os.Lstat(name); err != nil {
create: create, if !os.IsNotExist(err) {
maxSize: maxSize, return nil, err
maxFiles: maxFiles, }
size = 0
} else {
size = info.Size()
} }
writer, err := create(name)
if err != nil {
return nil, err
}
return &rotatedFile{
name: name,
create: create,
maxSize: maxSize,
maxFiles: maxFiles,
currentFile: writer,
currentSize: size,
}, nil
} }
func (r *rotatedFile) Write(bs []byte) (int, error) { func (r *rotatedFile) Write(bs []byte) (int, error) {
@ -370,19 +388,13 @@ func (r *rotatedFile) Write(bs []byte) (int, error) {
// file so we'll start on a new one. // file so we'll start on a new one.
if r.currentSize+int64(len(bs)) > r.maxSize { if r.currentSize+int64(len(bs)) > r.maxSize {
r.currentFile.Close() r.currentFile.Close()
r.currentFile = nil
r.currentSize = 0 r.currentSize = 0
}
// If we have no current log, rotate old files out of the way and create
// a new one.
if r.currentFile == nil {
r.rotate() r.rotate()
fd, err := r.create(r.name) f, err := r.create(r.name)
if err != nil { if err != nil {
return 0, err return 0, err
} }
r.currentFile = fd r.currentFile = f
} }
n, err := r.currentFile.Write(bs) n, err := r.currentFile.Write(bs)
@ -435,7 +447,7 @@ type autoclosedFile struct {
mut sync.Mutex mut sync.Mutex
} }
func newAutoclosedFile(name string, closeDelay, maxOpenTime time.Duration) *autoclosedFile { func newAutoclosedFile(name string, closeDelay, maxOpenTime time.Duration) (*autoclosedFile, error) {
f := &autoclosedFile{ f := &autoclosedFile{
name: name, name: name,
closeDelay: closeDelay, closeDelay: closeDelay,
@ -444,8 +456,13 @@ func newAutoclosedFile(name string, closeDelay, maxOpenTime time.Duration) *auto
closed: make(chan struct{}), closed: make(chan struct{}),
closeTimer: time.NewTimer(time.Minute), closeTimer: time.NewTimer(time.Minute),
} }
f.mut.Lock()
defer f.mut.Unlock()
if err := f.ensureOpenLocked(); err != nil {
return nil, err
}
go f.closerLoop() go f.closerLoop()
return f return f, nil
} }
func (f *autoclosedFile) Write(bs []byte) (int, error) { func (f *autoclosedFile) Write(bs []byte) (int, error) {
@ -453,7 +470,7 @@ func (f *autoclosedFile) Write(bs []byte) (int, error) {
defer f.mut.Unlock() defer f.mut.Unlock()
// Make sure the file is open for appending // Make sure the file is open for appending
if err := f.ensureOpen(); err != nil { if err := f.ensureOpenLocked(); err != nil {
return 0, err return 0, err
} }
@ -483,22 +500,14 @@ func (f *autoclosedFile) Close() error {
} }
// Must be called with f.mut held! // Must be called with f.mut held!
func (f *autoclosedFile) ensureOpen() error { func (f *autoclosedFile) ensureOpenLocked() error {
if f.fd != nil { if f.fd != nil {
// File is already open // File is already open
return nil return nil
} }
// We open the file for write only, and create it if it doesn't exist. // We open the file for write only, and create it if it doesn't exist.
flags := os.O_WRONLY | os.O_CREATE flags := os.O_WRONLY | os.O_CREATE | os.O_APPEND
if f.opened.IsZero() {
// This is the first time we are opening the file. We should truncate
// it to better emulate an os.Create() call.
flags |= os.O_TRUNC
} else {
// The file was already opened once, so we should append to it.
flags |= os.O_APPEND
}
fd, err := os.OpenFile(f.name, flags, 0644) fd, err := os.OpenFile(f.name, flags, 0644)
if err != nil { if err != nil {

View File

@ -33,7 +33,10 @@ func TestRotatedFile(t *testing.T) {
maxSize := int64(len(testData) + len(testData)/2) maxSize := int64(len(testData) + len(testData)/2)
// We allow the log file plus two rotated copies. // We allow the log file plus two rotated copies.
rf := newRotatedFile(logName, open, maxSize, 2) rf, err := newRotatedFile(logName, open, maxSize, 2)
if err != nil {
t.Fatal(err)
}
// Write some bytes. // Write some bytes.
if _, err := rf.Write(testData); err != nil { if _, err := rf.Write(testData); err != nil {
@ -140,7 +143,10 @@ func TestAutoClosedFile(t *testing.T) {
data := []byte("hello, world\n") data := []byte("hello, world\n")
// An autoclosed file that closes very quickly // An autoclosed file that closes very quickly
ac := newAutoclosedFile(file, time.Millisecond, time.Millisecond) ac, err := newAutoclosedFile(file, time.Millisecond, time.Millisecond)
if err != nil {
t.Fatal(err)
}
// Write some data. // Write some data.
if _, err := ac.Write(data); err != nil { if _, err := ac.Write(data); err != nil {
@ -182,21 +188,23 @@ func TestAutoClosedFile(t *testing.T) {
} }
// Open the file again. // Open the file again.
ac = newAutoclosedFile(file, time.Second, time.Second) ac, err = newAutoclosedFile(file, time.Second, time.Second)
if err != nil {
t.Fatal(err)
}
// Write something // Write something
if _, err := ac.Write(data); err != nil { if _, err := ac.Write(data); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// It should now contain only one write, because the first open // It should now contain three writes, as the file is always opened for appending
// should be a truncate.
bs, err = ioutil.ReadFile(file) bs, err = ioutil.ReadFile(file)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if len(bs) != len(data) { if len(bs) != 3*len(data) {
t.Fatalf("Write failed, expected %d bytes, not %d", len(data), len(bs)) t.Fatalf("Write failed, expected %d bytes, not %d", 3*len(data), len(bs))
} }
// Close. // Close.