runtime: FileSystemWatcher not getting rename events

I’ve got a worker service in a .NET Core 3.1 app that uses a FileSystemWatcher to monitor PostgreSQL logfiles. However, it never receives events for the logfiles themselves. If I set the watcher to include subdirectories, it receives events for temporary files being updated, renamed, and deleted, but never for the log files (*.log and *.csv). I know from running ls -l over and over that the logfiles are in fact being updated as they constantly increase in size. I also used procmon to see that the .csv file is being written to about twice per second using WriteFile (in Windows).

Here’s how I’m using my watcher:

type LogFileWatcherOptions() =
    member val DataDir = "" with get,set
    member val LogFormat = "" with get,set

type LogFileWatcher(logger:ILogger<LogFileWatcher>, optionsMonitor:IOptionsMonitor<LogFileWatcherOptions>) =
    inherit BackgroundService()

    let handleFileChanged (fsea:FileSystemEventArgs) =
        sprintf "%s changed: %O" fsea.FullPath fsea.ChangeType
        |> logger.LogTrace

    let handlePathCreated (fsea:FileSystemEventArgs) =
        sprintf "%s created: %O" fsea.FullPath fsea.ChangeType
        |> logger.LogTrace

    let handlePathRenamed (rea:RenamedEventArgs) =
        sprintf "%s renamed to %s (%O)" rea.OldFullPath rea.FullPath rea.ChangeType
        |> logger.LogTrace

    let handlePathDeleted (fsea:FileSystemEventArgs) =
        sprintf "%s deleted (%O)" fsea.FullPath fsea.ChangeType
        |> logger.LogTrace

    let configureWatcher path format =
        sprintf "configuring watcher with path: '%s' and format: '%s'" path format
        |> logger.LogTrace

        if not (Directory.Exists(path)) then
            failwithf "data directory does not exist: %s" path

        let watcher = new FileSystemWatcher()
        watcher.Path <- path
        watcher.IncludeSubdirectories <- true
        watcher.Filter <- "*.csv"
        watcher.NotifyFilter <- watcher.NotifyFilter ||| NotifyFilters.LastAccess
        watcher.NotifyFilter <- watcher.NotifyFilter ||| NotifyFilters.Size

        watcher.Changed.Add(handleFileChanged)
        watcher.Created.Add(handlePathCreated)
        watcher.Renamed.Add(handlePathRenamed)
        watcher.Deleted.Add(handlePathDeleted)
        watcher

    let _options = optionsMonitor.CurrentValue
    let _watcher = configureWatcher _options.DataDir _options.LogFormat

    override __.Dispose() =
        _watcher.Dispose()
        base.Dispose()

    override __.ExecuteAsync(stopToken) =
        async {
            logger.LogTrace "enabling filewatcher events..."
            _watcher.EnableRaisingEvents <- true
            while not stopToken.IsCancellationRequested do
                do! Async.Sleep 500
            logger.LogTrace "cancelling..."
            _watcher.EnableRaisingEvents <- false
        }
        |> Async.StartAsTask
        :> Task

Is it possible that PostgreSQL is writing to these logs in a way that the underlying implementation of FileSystemWatcher is unable to detect?

Update: I tested this on Windows, too, and the same thing holds true there.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Comments: 22 (12 by maintainers)

Most upvoted comments

If multiple files are being changed at once (or quicky) you may be running into https://github.com/dotnet/runtime/issues/30846. Actual events are cached and distributed by separate OS daemon as well as the logic to pair events was broken at .Net layer. There is at least one more outstanding bug where available OS API does not allow to disambiguate events properly in all cases.

It may be work or putting together standalone repro @aggieben where you watch as well as manipulate watched files. And perhaps give it try with 5.0 preview.

I also tried adding an error handler as you suggested, but it doesn’t receive any events.