diff --git a/command/agent/log_file.go b/command/agent/log_file.go index da660c4eadda..7bba86a319c1 100644 --- a/command/agent/log_file.go +++ b/command/agent/log_file.go @@ -62,18 +62,23 @@ func (l *logFile) fileNamePattern() string { } func (l *logFile) openNew() error { - createTime := now() newfilePath := filepath.Join(l.logPath, l.fileName) - // Try creating a file. We truncate the file because we are the only authority to write the logs - filePointer, err := os.OpenFile(newfilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640) + + // Try creating or opening the active log file. Since the active log file + // always has the same name, append log entries to prevent overwriting + // previous log data. + filePointer, err := os.OpenFile(newfilePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0640) + if err != nil { + return err + } + stat, err := filePointer.Stat() if err != nil { return err } l.FileInfo = filePointer - // New file, new bytes tracker, new creation time :) - l.LastCreated = createTime - l.BytesWritten = 0 + l.BytesWritten = stat.Size() + l.LastCreated = l.createTime(stat) return nil } diff --git a/command/agent/log_file_test.go b/command/agent/log_file_test.go index 402560ac0a0d..8bcc52a00e8c 100644 --- a/command/agent/log_file_test.go +++ b/command/agent/log_file_test.go @@ -50,13 +50,33 @@ func TestLogFile_openNew(t *testing.T) { require.NoError(err) defer os.Remove(tempDir) - logFile := logFile{fileName: testFileName, logPath: tempDir, duration: testDuration} + filt := LevelFilter() + filt.MinLevel = logutils.LogLevel("INFO") + logFile := logFile{ + logFilter: filt, + fileName: testFileName, + logPath: tempDir, + MaxBytes: testBytes, + duration: 24 * time.Hour, + } require.NoError(logFile.openNew()) _, err = ioutil.ReadFile(logFile.FileInfo.Name()) require.NoError(err) require.Equal(logFile.FileInfo.Name(), filepath.Join(tempDir, testFileName)) + + // Check if create time and bytes written are kept when opening the active + // log file again. + bytesWritten, err := logFile.Write([]byte("test")) + require.NoError(err) + + time.Sleep(2 * time.Second) + require.NoError(logFile.openNew()) + + timeDelta := time.Now().Sub(logFile.LastCreated) + require.GreaterOrEqual(timeDelta, 2*time.Second) + require.Equal(logFile.BytesWritten, int64(bytesWritten)) } func TestLogFile_byteRotation(t *testing.T) { diff --git a/command/agent/log_file_unix.go b/command/agent/log_file_unix.go new file mode 100644 index 000000000000..69cd2f727530 --- /dev/null +++ b/command/agent/log_file_unix.go @@ -0,0 +1,16 @@ +//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +package agent + +import ( + "os" + "syscall" + "time" +) + +func (l *logFile) createTime(stat os.FileInfo) time.Time { + stat_t := stat.Sys().(*syscall.Stat_t) + createTime := stat_t.Ctimespec + return time.Unix(createTime.Sec, createTime.Nsec) +} diff --git a/command/agent/log_file_windows.go b/command/agent/log_file_windows.go new file mode 100644 index 000000000000..ea6a691f284e --- /dev/null +++ b/command/agent/log_file_windows.go @@ -0,0 +1,14 @@ +package agent + +import ( + "os" + "time" +) + +func (l *logFile) createTime(stat os.FileInfo) time.Time { + // Use `ModTime` as an approximation if the exact create time is not + // available. + // On Windows, the file create time is not updated after the active log + // rotates, so use `ModTime` as an approximation as well. + return stat.ModTime() +}