v0.18.1 file watcher retirement diagnostics update

This commit is contained in:
2026-09-13 18:53:18 -04:00
parent 5934a2e35f
commit 7b782a79ef
10 changed files with 169 additions and 28 deletions
+12 -4
View File
@@ -292,12 +292,21 @@ func (fs *FileSource) ensureWatcher(path string) {
}
}
fs.mu.Lock()
delete(fs.watchers, path)
fs.mu.Unlock()
fs.removeWatcher(path, 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.
func (fs *FileSource) removeWatcher(path string, watcher *fileWatcher) {
fs.mu.Lock()
if fs.watchers[path] == watcher {
delete(fs.watchers, path)
}
fs.mu.Unlock()
}
// cleanupWatchers stops and removes watchers for files that no longer exist.
func (fs *FileSource) cleanupWatchers() {
fs.mu.Lock()
@@ -369,4 +378,3 @@ func globToRegex(glob string) string {
regex = strings.ReplaceAll(regex, `\?`, `.`)
return "^" + regex + "$"
}
+46
View File
@@ -0,0 +1,46 @@
package file
import (
"context"
"os"
"path/filepath"
"testing"
"time"
"logwisp/internal/core"
)
func TestStoppedWatcherReturnsNormally(t *testing.T) {
path := filepath.Join(t.TempDir(), "session.jsonl")
if err := os.WriteFile(path, nil, 0o600); err != nil {
t.Fatal(err)
}
watcher := newFileWatcher(path, true, true, func(_ core.LogEntry) {}, nil)
watcher.stop()
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := watcher.watch(ctx); err != nil {
t.Fatalf("stopped watcher returned error: %v", err)
}
}
func TestRemoveWatcherPreservesReplacement(t *testing.T) {
oldWatcher := &fileWatcher{}
replacement := &fileWatcher{}
source := &FileSource{
watchers: map[string]*fileWatcher{
"session.jsonl": replacement,
},
}
source.removeWatcher("session.jsonl", oldWatcher)
if got := source.watchers["session.jsonl"]; got != replacement {
t.Fatalf("replacement watcher = %p, want %p", got, replacement)
}
source.removeWatcher("session.jsonl", replacement)
if _, exists := source.watchers["session.jsonl"]; exists {
t.Fatal("finished watcher was not removed")
}
}
+1 -1
View File
@@ -80,7 +80,7 @@ func (w *fileWatcher) watch(ctx context.Context) error {
return ctx.Err()
case <-ticker.C:
if w.isStopped() {
return fmt.Errorf("watcher stopped")
return nil
}
if err := w.checkFile(); err != nil {
// Log error but continue watching