From ba36bda4bde582e8fb05d166aa79c963052ba806 Mon Sep 17 00:00:00 2001 From: Nakul Chander Date: Fri, 27 Aug 2021 14:45:07 -0700 Subject: [PATCH 1/2] File watcher may not capture any changes that occured before the watcher was started --- watch/inotify.go | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/watch/inotify.go b/watch/inotify.go index cbd11ad..7d85d56 100644 --- a/watch/inotify.go +++ b/watch/inotify.go @@ -79,6 +79,13 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChange events := Events(fw.Filename) + // Check to see the file hasn't been modified already before the watcher started + fi, deleted := fw.checkAndNotifyIfModifiedInBetween(changes) + if deleted { + return + } + fw.Size = fi.Size() + for { prevSize := fw.Size @@ -134,3 +141,27 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChange return changes, nil } + +func (fw *InotifyFileWatcher) checkAndNotifyIfModifiedInBetween(changes *FileChanges) (os.FileInfo, bool) { + fi, err := os.Stat(fw.Filename) + if err != nil { + if os.IsNotExist(err) { + RemoveWatch(fw.Filename) + changes.NotifyDeleted() + return nil, true + } + util.Fatal("Failed to stat file %v: %v", fw.Filename, err) + } + + if fw.Size > 0 && fw.Size > fi.Size() { // old file size was larger than now => truncated + changes.NotifyTruncated() + } else if fw.Size != fi.Size() { + changes.NotifyModified() + } + // there is a corner case of file that was truncated and replaced with exact same amount of bytes, which would + // result in no notification. However any subsequent writes will capture that + // If the file isn't expected to be written to often and those events cannot be missed, recommend using Polling watcher + // instead of inotify + + return fi, false +} From a6a562443980db637c984089f8635ed7a71752a6 Mon Sep 17 00:00:00 2001 From: Nakul Chander Date: Tue, 31 Aug 2021 12:50:14 -0700 Subject: [PATCH 2/2] review comments & gofmt --- util/util.go | 9 +++++++-- watch/inotify.go | 15 ++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/util/util.go b/util/util.go index b64caa2..9c466c7 100644 --- a/util/util.go +++ b/util/util.go @@ -17,14 +17,19 @@ type Logger struct { var LOGGER = &Logger{log.New(os.Stderr, "", log.LstdFlags)} -// fatal is like panic except it displays only the current goroutine's stack. +// Fatal is like panic except it displays only the current goroutine's stack. func Fatal(format string, v ...interface{}) { // https://github.com/nxadm/log/blob/master/log.go#L45 LOGGER.Output(2, fmt.Sprintf("FATAL -- "+format, v...)+"\n"+string(debug.Stack())) os.Exit(1) } -// partitionString partitions the string into chunks of given size, +// Error logs an error message with the current goroutine stack and returns. It doesn't quit. +func Error(format string, v ...interface{}) { + LOGGER.Output(2, fmt.Sprintf("ERROR -- "+format, v...)+"\n"+string(debug.Stack())) +} + +// PartitionString partitions the string into chunks of given size, // with the last chunk of variable size. func PartitionString(s string, chunkSize int) []string { if chunkSize <= 0 { diff --git a/watch/inotify.go b/watch/inotify.go index 7d85d56..6ffab80 100644 --- a/watch/inotify.go +++ b/watch/inotify.go @@ -11,7 +11,7 @@ import ( "github.com/nxadm/tail/util" - "github.com/fsnotify/fsnotify" + "github.com/fsnotify/fsnotify" "gopkg.in/tomb.v1" ) @@ -145,15 +145,16 @@ func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, pos int64) (*FileChange func (fw *InotifyFileWatcher) checkAndNotifyIfModifiedInBetween(changes *FileChanges) (os.FileInfo, bool) { fi, err := os.Stat(fw.Filename) if err != nil { - if os.IsNotExist(err) { - RemoveWatch(fw.Filename) - changes.NotifyDeleted() - return nil, true + if !os.IsNotExist(err) { + util.Error("Failed to stat file %v: %v", fw.Filename, err) + // Treat it as a deleted file as we cannot read it anyway. } - util.Fatal("Failed to stat file %v: %v", fw.Filename, err) + _ = RemoveWatch(fw.Filename) + changes.NotifyDeleted() + return nil, true } - if fw.Size > 0 && fw.Size > fi.Size() { // old file size was larger than now => truncated + if fw.Size > fi.Size() { // old file size was larger than now => truncated changes.NotifyTruncated() } else if fw.Size != fi.Size() { changes.NotifyModified()