v0.3.1 background mode fixed, bug fixes and refactoring

This commit is contained in:
2025-07-12 20:30:47 -04:00
parent 58d33d7872
commit e31591ac8d
9 changed files with 193 additions and 353 deletions

View File

@ -79,11 +79,10 @@ func (w *fileWatcher) watch(ctx context.Context) error {
}
}
// FILE: src/internal/source/file_watcher.go
func (w *fileWatcher) seekToEnd() error {
file, err := os.Open(w.path)
if err != nil {
// For non-existent files, initialize position to 0
// This allows watching files that don't exist yet
if os.IsNotExist(err) {
w.mu.Lock()
w.position = 0
@ -103,13 +102,13 @@ func (w *fileWatcher) seekToEnd() error {
}
w.mu.Lock()
// Only seek to end if position was never set (-1)
// This preserves position = 0 for new files while allowing
// directory-discovered files to start reading from current position
defer w.mu.Unlock()
// Keep existing position (including 0)
// First time initialization seeks to the end of the file
if w.position == -1 {
pos, err := file.Seek(0, io.SeekEnd)
if err != nil {
w.mu.Unlock()
return err
}
w.position = pos
@ -120,7 +119,6 @@ func (w *fileWatcher) seekToEnd() error {
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
w.inode = stat.Ino
}
w.mu.Unlock()
return nil
}
@ -171,35 +169,57 @@ func (w *fileWatcher) checkFile() error {
w.inode = currentInode
w.size = currentSize
w.modTime = currentModTime
// Keep position at 0 to read from beginning if this is a new file
// or seek to end if we want to skip existing content
if oldSize == 0 && w.position == 0 {
// First time seeing this file, seek to end to skip existing content
w.position = currentSize
}
// Position stays at 0 for new files
w.mu.Unlock()
return nil
// Don't return here - continue to read content
}
// Check for rotation
rotated := false
rotationReason := ""
startPos := oldPos
if oldInode != 0 && currentInode != 0 && currentInode != oldInode {
rotated = true
rotationReason = "inode change"
} else if currentSize < oldSize {
// Rotation detection
if currentSize < oldSize {
// File was truncated
rotated = true
rotationReason = "size decrease"
} else if currentModTime.Before(oldModTime) && currentSize <= oldSize {
// Modification time went backwards (logrotate behavior)
rotated = true
rotationReason = "modification time reset"
} else if oldPos > currentSize+1024 {
// Our position is way beyond file size
rotated = true
rotationReason = "position beyond file size"
} else if oldInode != 0 && currentInode != 0 && currentInode != oldInode {
// Inode changed - distinguish between rotation and atomic save
if currentSize == 0 {
// Empty file with new inode = likely rotation
rotated = true
rotationReason = "inode change with empty file"
} else if currentSize < oldPos {
// New file is smaller than our position = rotation
rotated = true
rotationReason = "inode change with size less than position"
} else {
// Inode changed but file has content and size >= position
// This is likely an atomic save by an editor
// Update inode but keep position
w.mu.Lock()
w.inode = currentInode
w.mu.Unlock()
w.logger.Debug("msg", "Atomic file update detected",
"component", "file_watcher",
"path", w.path,
"old_inode", oldInode,
"new_inode", currentInode,
"position", oldPos,
"size", currentSize)
}
}
startPos := oldPos
if rotated {
startPos = 0
w.mu.Lock()