File System Monitoring
Vaibhav • September 10, 2025
As applications grow more dynamic and data-driven, they often need to respond to changes in the file system -
such as when a file is created, modified, deleted, or renamed. Instead of constantly polling the disk, C#
provides a powerful and efficient way to monitor these changes in real time using the FileSystemWatcher
class. In this article, we’ll explore how to use FileSystemWatcher
to track file system events, how to configure it properly, and
how to avoid common pitfalls when building reactive file-based workflows.
Why Monitor the File System?
File system monitoring is useful in many scenarios:
- Automatically reloading configuration files when they change
- Triggering processing when new files are added to a folder
- Logging or auditing file access
- Synchronizing files between systems
Instead of manually checking for changes, FileSystemWatcher
lets your
application subscribe to events and react immediately.
Introducing FileSystemWatcher
FileSystemWatcher
is part of the System.IO
namespace. It monitors a specified directory and raises events when changes occur. You can track changes to
files, subdirectories, or both.
using System.IO;
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = "C:\\Logs";
watcher.Filter = "*.txt";
watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
watcher.Created += (s, e) => Console.WriteLine($"Created: {e.Name}");
watcher.Changed += (s, e) => Console.WriteLine($"Changed: {e.Name}");
watcher.Deleted += (s, e) => Console.WriteLine($"Deleted: {e.Name}");
watcher.Renamed += (s, e) => Console.WriteLine($"Renamed: {e.OldName} → {e.Name}");
watcher.EnableRaisingEvents = true;
This sets up a watcher on the C:\Logs
folder, listening for changes to .txt
files. When a file is created, modified, deleted, or renamed, the
corresponding event handler is triggered.
EnableRaisingEvents
must be set to true
for the watcher to start listening. You can toggle it off to temporarily
pause monitoring.
Understanding NotifyFilters
NotifyFilter
controls what kinds of changes the watcher should detect. You can
combine multiple filters using bitwise OR:
FileName
- detects file creation, deletion, renamingDirectoryName
- detects folder changesLastWrite
- detects content changesSize
- detects file size changesAttributes
,CreationTime
, etc.
Choose filters based on what your application cares about. For example, a log processor might only care about
Created
and LastWrite
.
Filtering by File Type
Use the Filter
property to limit monitoring to specific file types:
watcher.Filter = "*.csv"; // Only watch CSV files
You can also leave Filter
empty to watch all files:
watcher.Filter = "*.*"; // Watch all files
This is useful when you don’t know the file types in advance or want to monitor everything.
Watching Subdirectories
By default, FileSystemWatcher
only watches the specified folder. To include
subdirectories, set IncludeSubdirectories
to true
:
watcher.IncludeSubdirectories = true;
This is useful for monitoring entire folder trees - for example, a shared project directory or a nested configuration folder.
Handling Events
Each event provides a FileSystemEventArgs
or RenamedEventArgs
object with details:
watcher.Created += (s, e) =>
{
Console.WriteLine($"New file: {e.FullPath}");
};
You can use e.FullPath
to get the full path, or e.Name
for just the file name. For renamed events, use e.OldName
and e.Name
.
Debouncing Rapid Events
Some operations (like saving a file) may trigger multiple events rapidly. To avoid duplicate processing, you can debounce events using a timer or a short delay:
watcher.Changed += (s, e) =>
{
Thread.Sleep(100); // Wait briefly to avoid duplicate triggers
Console.WriteLine($"Changed: {e.FullPath}");
};
This helps prevent race conditions or redundant processing when files are updated frequently.
Handling Exceptions
FileSystemWatcher
may throw exceptions if the path is invalid, inaccessible, or
too many watchers are active. Always wrap setup in a try-catch
block:
try
{
watcher.Path = "C:\\Logs";
watcher.EnableRaisingEvents = true;
}
catch (Exception ex)
{
Console.WriteLine($"Watcher error: {ex.Message}");
}
This ensures your app fails gracefully and provides useful diagnostics.
Disposing the Watcher
FileSystemWatcher
holds unmanaged resources. Always dispose it when done:
watcher.Dispose();
Or use a using
block:
using FileSystemWatcher watcher = new FileSystemWatcher();
// watcher is automatically disposed
Monitoring Multiple Folders
To monitor multiple folders, create separate watchers for each path:
string[] folders = { "C:\\Logs", "C:\\Reports" };
foreach (string folder in folders)
{
FileSystemWatcher w = new FileSystemWatcher(folder);
w.Created += (s, e) => Console.WriteLine($"New file in {folder}: {e.Name}");
w.EnableRaisingEvents = true;
}
This lets you track changes across multiple directories independently.
Limitations and Caveats
While FileSystemWatcher
is powerful, it has some limitations:
- It may miss events if too many changes happen quickly
- It relies on OS-level notifications - not all file systems support it
- It doesn’t detect changes inside files (only metadata)
- It may raise duplicate or delayed events
For mission-critical monitoring, consider combining FileSystemWatcher
with
periodic validation or checksums.
Real-World Use Case: Auto-Reload Config
Let’s build a simple example that reloads a configuration file when it changes:
string configPath = "config.json";
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = Path.GetDirectoryName(configPath);
watcher.Filter = Path.GetFileName(configPath);
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Changed += (s, e) =>
{
Console.WriteLine("Config changed. Reloading...");
string config = File.ReadAllText(configPath);
Console.WriteLine(config);
};
watcher.EnableRaisingEvents = true;
This watches a single file and reloads its contents when modified. You can extend this to parse JSON, update settings, or trigger reinitialization.
Summary
FileSystemWatcher
is a powerful tool for building reactive applications that
respond to file system changes. You’ve learned how to monitor folders and files, handle events like creation and
modification, filter by file type, and manage performance and reliability. These skills are essential for
building modern apps that adapt to changing data and environments. In the next article, we’ll explore
compression and archives - how to zip and unzip files, reduce storage, and manage bundled data efficiently.