Monitoring file system activity with file-watchers
(require file-watchers) | package: file-watchers |
Use file-watchers to audit and react to file activity in a system.
1 Quick Start
For command-line use, use the file-watchers raco command.
$ raco file-watchers -h # For help |
$ raco file-watchers dir # Watch given directory |
|
# Watch files and directories with a given method (methods documented below). |
$ raco file-watchers -m apathetic dir fileA fileB |
$ raco file-watchers -m robust dir fileA fileB |
$ raco file-watchers -m intensive dir fileA fileB |
For programmatic use, you can apply watch to a list of targets.
(require file-watchers) (define watcher (watch '("/path/to/dir" "config.json")))
By default, lists describing file activity from the watched directory will appear via displayln.
procedure
(watch [ paths on-activity on-status thread-maker]) → thread? paths : (listof path-on-disk?) = (list (current-directory)) on-activity : (-> list? any) = displayln on-status : (-> list? any) = displayln
thread-maker : (-> path? thread?) = (suggest-approach #:apathetic #f)
The thread returned from watch will wait for all subordinate threads to terminate before it itself terminates. Breaking is enabled.
thread-maker should either be one of apathetic-watch, intensive-watch, or robust-watch, or a procedure that returns a thread created using one of those procedures.
procedure
(watch-directories [ directories on-activity on-status thread-maker]) → thread?
directories : (listof directory-exists?) = (list (current-directory)) on-activity : (-> list? any) = displayln on-status : (-> list? any) = displayln
thread-maker : (-> path? thread?) = (suggest-approach #:apathetic #f)
NOTE: This procedure is deprecated; use watch, instead. watch-directories will be removed after January 1, 2020.
procedure
(suggest-approach #:apathetic apathetic) → procedure?
apathetic : boolean?
If file change events are not supported on the operating system or if file-level monitoring is unavailable, then robust-watch is returned.
procedure
(path-on-disk? path) → boolean?
path : path?
2 Synchronization
All file monitoring occurs in at least one thread. Activity and status information are each conveyed on a dedicated asynchronous channel. For more, see Buffered Asynchronous Channels.
Each channel message is a list that starts with a symbol for the associated file monitoring method, followed by a symbol indicating the kind of activity or status reported. For example, an apathetic-watch will convey that it is watching a directory and a change was detected somewhere inside it.
'(apathetic watching /path/to/dir)
A 'watching status comes from file-watcher-status-channel, while detected file activity comes from file-activity-channel.
value
file-activity-channel : (parameter/c async-channel?)
value
file-watcher-status-channel : (parameter/c async-channel?)
procedure
(file-watcher-channel-try-get) → (or/c boolean? list?)
procedure
(file-watcher-channel-get) → (or/c boolean? list?)
3 Detecting changes without concern for root cause
file-watcher
(apathetic-watch path) → thread?
path : path-on-disk?
If path is a directory, apathetic-watch will monitor all files recursively, but all changes within the directory are reported as changes to path.
An apathetic watch:
...reports only (list 'apathetic 'watching path) on file-watcher-status-channel each time it starts waiting for a change.
...reports only (list 'apathetic 'change path) on file-activity-channel when any change is detected.
The below example starts an apathetic watch thread, waits for the thread to report that it is watching "dir", then deletes "dir". The apathetic watcher thread will report that the change occurred on file-activity-channel before terminating, since "dir" was the root path for the watching thread.
(define apathetic-watcher (apathetic-watch "dir")) (sync/enable-break (file-watcher-status-channel)) (delete-directory "dir") (displayln (sync/enable-break (file-activity-channel))) (thread-wait apathetic-watcher) (displayln (thread-dead? apathetic-watcher))
4 Poll-based file monitoring
file-watcher
(robust-watch path) → thread?
path : path-on-disk?
Furthermore, robust-watch will only compare file permissions and access times, not contents.
robust-watch only reports 'add, 'change, and 'remove events on file-activity-channel. It does not report status information on file-watcher-status-channel.
value
robust-poll-milliseconds : (parameter/c exact-positive-integer?)
5 Verbose file-level monitoring
file-watcher
(intensive-watch path) → thread?
path : path-on-disk?
Due to the resource-hungry nature of the model, an intensive watch may warrant a dedicated custodian.
If a link file is accessed in a way that impacts the link’s target, both the link file and the target file will be marked as changed.
Status information appears on file-watcher-status-channel under the following rules:
(list 'intensive 'new-thread detected-path) appears when a new thread is created to monitor a created file.
(list 'intensive 'thread-done path) appears when a thread dies, meaning it is no longer monitoring the given path.
Activity information appears on file-activity-channel under the following rules:
(list 'intensive 'add detected-path) appears when a new file is detected.
(list 'intensive 'remove path) appears when a file or directory is found to no longer exist.
(list 'intensive 'change path) appears when a file or directory at the given path triggers a filesystem-change-evt.