Directory + File Watchers
Watch directories and react to file changes in real time
BoxLang ships with a runtime WatcherService that lets you watch directories and respond to filesystem activity with BoxLang listeners.
Watchers are useful for:
hot reload workflows
build and asset pipelines
ingest/ETL drop folders
automation on create, modify, and delete events
Event Model
Watchers emit events for:
created
New file or directory exists
Fired when a new entry is added under a watched path.
modified
Existing entry changes
Fired when content or metadata updates are detected.
deleted
Existing entry is removed
Fired when an entry that previously existed is deleted.
overflow
Watch queue overflows
Indicates dropped events; re-sync state if needed.
A listener receives an event payload that includes event kind, watched root, path details, and timestamp.
Watcher Event Payload
Listeners receive a struct with these keys:
kind
string
Event kind: created, modified, deleted, or overflow.
path
string
Absolute path for the affected entry; blank for overflow.
relativePath
string
Path relative to watchRoot; blank for overflow.
watchRoot
string
Registered watcher root; blank for overflow.
timestamp
string
ISO-8601 timestamp for when the event was emitted.
How Watchers Work
Watchers monitor filesystem changes and route events to your listener code. Here's how the flow works:
Event Processing Pipeline
Quick Start (Programmatic)
Watcher Listeners
BoxLang watchers support four listener patterns: closures, struct of closures, class name strings, and class instances. Choose the pattern that fits your complexity and reusability needs.
🔹 Closure Listener
The simplest approach—pass a closure that receives the event struct:
Best for: Quick prototypes, simple logging, and single-action handlers.
🔹 Struct of Closures
Map event kinds to specific handler closures for cleaner separation:
Best for: Event-specific logic without creating a full class, medium complexity handlers.
🔹 Class Listener
For production systems for full control and error handling. A class can maintain internal state, helper methods, and complex logic. It is mandatory to have a onEvent() method that receives the event struct.
The available methods are:
onEvent()
Yes
Main event handler receiving all events. Must accept an event struct.
onCreate()
No
Called for created events. Receives event struct.
onModify()
No
Called for modified events. Receives event struct.
onDelete()
No
Called for deleted events. Receives event struct.
onOverflow()
No
Called for overflow events. Receives event struct.
onError()
No
Called when listener execution throws an exception.
When specific event methods (onCreate(), onModify(), onDelete(), onOverflow()) are defined, they are called in addition to onEvent(). Use onEvent() for centralized routing or implement specific methods for targeted handling.
Usage with class instance:
Usage with class name string:
Best for: Production systems, shared listener logic, complex error handling, testable code.
Global Startup Watchers
You can register auto-start watchers in boxlang.json:
See Watcher configuration for all options.
Application Watchers
If you want watchers to be app-scoped and auto-started from your Application.bx, define them in this.watchers.
See Application.bx Custom Watchers for full syntax, supported listener forms, and definition keys.
Watcher BIFs
watcherNew()
Create and register a watcher
watcherStart()
Start a watcher
watcherStop()
Stop a watcher
watcherRestart()
Restart a watcher
watcherGet()
Retrieve one watcher
watcherGetAll()
Retrieve all watchers
watcherList()
List watcher names
watcherExists()
Check if watcher exists
watcherShutdown()
Stop and unregister one watcher
watcherStopAll()
Stop all watchers without unregistering
watcherShutdownAll()
Stop and unregister all watchers
WatcherInstance API
watcherNew() and watcherGet() return a WatcherInstance object you can inspect and control directly.
start()→WatcherInstance: Start this watcher.stop( force )→WatcherInstance: Stop this watcher (forcedefaults tofalse).restart()→WatcherInstance: Stop and start this watcher.isRunning()→boolean: True when watcher state isRUNNING.isStopped()→boolean: True when watcher state isSTOPPED.getState()→State: Raw enum state (CREATED,RUNNING,STOPPED).getStateAsString()→string: String state value.getName()→Key: Watcher identifier.getWatchPaths()→array: Configured watch roots.getListener()→IWatcherListener: Listener instance.getStats()→struct: Snapshot of watcher configuration and runtime status.
getStats() Properties
The getStats() method returns a struct with the following keys:
name
string
Watcher identifier
state
string
Current watcher state (CREATED, RUNNING, STOPPED)
paths
array
Array of watch root paths
recursive
boolean
Whether subdirectories are monitored
debounce
numeric
Debounce delay in milliseconds (0 = disabled)
throttle
numeric
Throttle limit in milliseconds (0 = disabled)
atomicWrites
boolean
Whether atomic write detection is enabled
errorThreshold
numeric
Maximum consecutive errors before watcher stops
consecutiveErrors
numeric
Current count of consecutive errors since last success
Example:
Best Practices
Use
debounceto avoid duplicate triggers from editor save behavior.Use
throttlefor noisy directories with heavy write bursts.Keep listener logic fast; hand off heavy work to async executors.
Set
errorThresholdto prevent infinite noisy failures.Prefer class listeners for production systems and shared logic.
Watcher lifecycle and errors are logged through the runtime watcher logger. See logging configuration for logger tuning.
Last updated
Was this helpful?
