v0.37.1 match bytes to follow a file tail, preventing reset on edit in most cases
This commit is contained in:
1 parent
24c3eb1d6e
commit
1d90070ad1
4 files changed
+567
-192
No files matched your search
@@ -10,7 +10,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/lixenwraith/logwisp/internal/config"
|
||||
@@ -276,6 +275,7 @@ func (fs *FileSource) ensureWatcher(path string) {
|
||||
}
|
||||
|
||||
w := newFileWatcher(path, fs.config.Raw, fs.config.From == "start", fs.publish, fs.logger)
|
||||
w.others = fs.othersOf(w)
|
||||
// A rotation renames the file out from under its watcher, so the same inode
|
||||
// reappears here under the archive name. Resume where it was left: from the
|
||||
// start would re-emit every record the file has already delivered.
|
||||
@@ -304,22 +304,44 @@ func (fs *FileSource) ensureWatcher(path string) {
|
||||
// readPosition reports how far a running watcher has read the file now at path.
|
||||
// Callers hold fs.mu.
|
||||
func (fs *FileSource) readPosition(path string) (int64, bool) {
|
||||
info, err := os.Stat(path)
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
defer file.Close()
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return fs.readFile(file, info, nil)
|
||||
}
|
||||
|
||||
// readFile reports how far a running watcher other than but read the file, by
|
||||
// its inode, if it still holds what that one read last before there: an inode
|
||||
// can be reused, and more than one watcher can claim it. Callers hold fs.mu.
|
||||
func (fs *FileSource) readFile(file *os.File, info os.FileInfo, but *fileWatcher) (int64, bool) {
|
||||
for _, w := range fs.watchers {
|
||||
if position, ok := w.readTo(stat.Ino); ok {
|
||||
if w == but {
|
||||
continue
|
||||
}
|
||||
if position, tail, ok := w.readTo(inodeOf(info)); ok && leads(file, position, info.Size(), tail) {
|
||||
return position, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// othersOf asks the source's other watchers, from w's goroutine, how far they
|
||||
// read a file: w holds no lock of its own then, so the order is fs.mu, then a
|
||||
// watcher's mu, as everywhere.
|
||||
func (fs *FileSource) othersOf(w *fileWatcher) func(*os.File, os.FileInfo) (int64, bool) {
|
||||
return func(file *os.File, info os.FileInfo) (int64, bool) {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
return fs.readFile(file, info, w)
|
||||
}
|
||||
}
|
||||
|
||||
// removeWatcher removes only the watcher that finished. A deleted file can be
|
||||
// recreated before its old watcher observes stop; in that case ensureWatcher
|
||||
// has already installed a replacement under the same path, which must survive.
|
||||
|
||||
Reference in new issue
Block a user