From fd16ea13cb8287b827d8600da19d05dd266f4932 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Mon, 25 Oct 2021 19:56:20 -0400 Subject: [PATCH 1/4] prevent active log from being overwritten when agent starts --- command/agent/log_file.go | 17 +++++++++++------ command/agent/log_file_bsd.go | 16 ++++++++++++++++ command/agent/log_file_linux.go | 16 ++++++++++++++++ command/agent/log_file_test.go | 22 +++++++++++++++++++++- command/agent/log_file_windows.go | 14 ++++++++++++++ 5 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 command/agent/log_file_bsd.go create mode 100644 command/agent/log_file_linux.go create mode 100644 command/agent/log_file_windows.go 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_bsd.go b/command/agent/log_file_bsd.go new file mode 100644 index 000000000000..ae6ec23e149b --- /dev/null +++ b/command/agent/log_file_bsd.go @@ -0,0 +1,16 @@ +//go:build darwin || freebsd || netbsd || openbsd +// +build darwin freebsd netbsd openbsd + +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_linux.go b/command/agent/log_file_linux.go new file mode 100644 index 000000000000..5b5895042ba4 --- /dev/null +++ b/command/agent/log_file_linux.go @@ -0,0 +1,16 @@ +//go:build dragonfly || linux || solaris +// +build dragonfly linux 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.Ctim + return time.Unix(createTime.Sec, createTime.Nsec) +} 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_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() +} From 468bf53d72164d27ea8872fb5dee9d57e9cca763 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Mon, 25 Oct 2021 20:32:15 -0400 Subject: [PATCH 2/4] changelog: add entry for #11386 --- .changelog/11386.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/11386.txt diff --git a/.changelog/11386.txt b/.changelog/11386.txt new file mode 100644 index 000000000000..285623860d16 --- /dev/null +++ b/.changelog/11386.txt @@ -0,0 +1,3 @@ +```release-note:bug +agent: Fixed an issue that could cause previous log lines to be overwritten +``` From b3490d58becbcf30a24c1e1339a06dd559bc5d7d Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Tue, 26 Oct 2021 15:49:06 -0400 Subject: [PATCH 3/4] fix input type when retrieving file create time in 32-bit arch --- command/agent/log_file_linux.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/agent/log_file_linux.go b/command/agent/log_file_linux.go index 5b5895042ba4..3e655bf3c59c 100644 --- a/command/agent/log_file_linux.go +++ b/command/agent/log_file_linux.go @@ -12,5 +12,6 @@ import ( func (l *logFile) createTime(stat os.FileInfo) time.Time { stat_t := stat.Sys().(*syscall.Stat_t) createTime := stat_t.Ctim - return time.Unix(createTime.Sec, createTime.Nsec) + // Sec and Nsec are int32 in 32-bit architectures. + return time.Unix(int64(createTime.Sec), int64(createTime.Nsec)) } From d2c730d032b761cea7c455d6132e1475cf9e9ee7 Mon Sep 17 00:00:00 2001 From: Luiz Aoqui Date: Tue, 26 Oct 2021 17:34:37 -0400 Subject: [PATCH 4/4] add nolint to arch-specific int64 conversion --- command/agent/log_file_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/agent/log_file_linux.go b/command/agent/log_file_linux.go index 3e655bf3c59c..2f873b90f881 100644 --- a/command/agent/log_file_linux.go +++ b/command/agent/log_file_linux.go @@ -13,5 +13,5 @@ func (l *logFile) createTime(stat os.FileInfo) time.Time { stat_t := stat.Sys().(*syscall.Stat_t) createTime := stat_t.Ctim // Sec and Nsec are int32 in 32-bit architectures. - return time.Unix(int64(createTime.Sec), int64(createTime.Nsec)) + return time.Unix(int64(createTime.Sec), int64(createTime.Nsec)) //nolint:unconvert }