# include " wtr/watcher.hpp "
# include < iostream >
# include < string >
using namespace std ;
using namespace wtr ;
// The event type, and every field within it, has
// string conversions and stream operators. All
// kinds of strings -- Narrow, wide and weird ones.
// If we don't want particular formatting, we can
// json-serialize and show the event like this:
// some_stream << event
// Here, we'll apply our own formatting.
auto show (event e) {
cout << to<string>(e. effect_type ) + ' '
+ to<string>(e. path_type ) + ' '
+ to<string>(e. path_name )
+ (e. associated ? " -> " + to<string>(e. associated -> path_name ) : " " )
<< endl;
}
auto main () -> int {
// Watch the current directory asynchronously,
// calling the provided function on each event.
auto watcher = watch ( " . " , show);
// Do some work. (We'll just wait for a newline.)
getchar ();
// The watcher would close itself around here,
// though we can check and close it ourselves.
return watcher. close () ? 0 : 1 ;
} # Sigh
PLATFORM_EXTRAS= $( test " $( uname ) " = Darwin && echo ' -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -framework CoreFoundation -framework CoreServices ' )
# Build
eval c++ -std=c++17 -Iinclude src/wtr/tiny_watcher/main.cpp -o watcher $PLATFORM_EXTRAS
# Run
./watcher #include "wtr/watcher-c.h"
#include <stdio.h>
void callback ( struct wtr_watcher_event event , void * _ctx ) {
printf (
"path name: %s, effect type: %d path type: %d, effect time: %lld, associated path name: %sn" ,
event . path_name ,
event . effect_type ,
event . path_type ,
event . effect_time ,
event . associated_path_name ? event . associated_path_name : ""
);
}
int main () {
void * watcher = wtr_watcher_open ( "." , callback , NULL );
getchar ();
return ! wtr_watcher_close ( watcher );
}pip install wtr-watcher from watcher import Watch
with Watch ( "." , print ):
input ()cargo add wtr-watcher tokio futures use futures :: StreamExt ;
use wtr_watcher :: Watch ;
# [ tokio :: main ( flavor = "current_thread" ) ]
async fn main ( ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
let show = |e| async move { println ! ( "{e:?}" ) } ;
let events = Watch :: try_new ( "." ) ? ;
events . for_each ( show ) . await ;
Ok ( ( ) )
} import * as watcher from 'watcher' ;
var w = watcher . watch ( '.' , ( event ) => {
console . log ( event ) ;
} ) ;
process . stdin . on ( 'data' , ( ) => {
w . close ( ) ;
process . exit ( ) ;
} ) ;上面的每个输出将是这样的,具体取决于格式:
modify file /home/e-dant/dev/watcher/.git/refs/heads/next.lock
rename file /home/e-dant/dev/watcher/.git/refs/heads/next.lock -> /home/e-dant/dev/watcher/.git/refs/heads/next
create file /home/e-dant/dev/watcher/.git/HEAD.lock
享受!
文件系统事件观察者是
我尝试保留构成观察者相对简单的1579行,且API实用:
auto w = watch(path, [](event ev) { cout << ev; });wtr.watcher ~观察者可以用作库,程序或两者兼而有之。如果您不想用图书馆创建某些东西,那就不用担心了。只需使用我们的即可,您就可以拥有一个文件系统观察器,该观察者将文件系统事件打印为JSON。整洁的。以下是:
# The main branch is the (latest) release branch.
git clone https://github.com/e-dant/watcher.git && cd watcher
# Via Nix
nix run | grep -oE ' cmake-is-tough '
# With the build script
tool/build --no-build-test --no-run && cd out/this/Release # Build the release version for the host platform.
./wtr.watcher | grep -oE ' needle-in-a-haystack/.+" ' # Use it, pipe it, whatever. (This is an .exe on Windows.)您可以使用此项目观看整个文件系统。在几乎所有情况下,我们都使用接近零的资源并有效地使用缓存。我们定期测试检测事件并将事件发送给用户的开销比测量的文件系统操作要小的数量级。
我们通过对所有可用的消毒剂进行单位测试来运行该项目。此代码试图成为线程,内存,界限,类型和资源安全。我们缺乏语言,我们试图通过测试来弥补。对于安全的一些实际定义,该项目可能很合适。
观察者取决于C ++标准库。为了提高效率,我们在Linux,Darwin和Windows上可能利用操作系统。为了进行测试和调试,我们使用小记录和消毒剂。
观察者几乎可以在任何地方运行。唯一的要求是文件系统。
重要的部分是(仅标题)库和(可选的)CLI程序。
include/wtr/watcher.hpp 。将其包括在您的C ++项目中。将其复制到您的项目中,并将其包括为#include "wtr/watcher.hpp" (或类似)足以以这种语言启动并运行。在活动中可以找到一些额外的文档和高级图书馆内部内容和观看标题。watcher-c构建以其他语言的FFI使用Watcher的构建。src/wtr/watcher/main.cpp 。从命令行中构建以使用观察器。输出是详尽的JSON流。src/wtr/tiny_watcher/main.cpp 。一个非常最小,更可读的CLI程序。这样的来源几乎与C ++的示例用法相同。目录树在下面的注释中。
这里的两个基本构建块是:
watch功能或类(取决于语言)event对象(或类似命名,再次取决于语言) watch走一条路径,这是一件类似弦乐的东西,而回调是一种函数式的东西。例如,通过watch角色阵列和闭合在C ++中可以很好地工作。
可以在快速启动中找到各种语言的示例。 API在各种语言之间相对一致。
观看者将愉快地继续观看,直到您停止它,否则遇到了无法恢复的错误。
event对象用于将有关文件系统事件的信息传递给给定的回调(由您) watch 。
event对象将包含:
path_name ,这是事件的绝对路径。path_type ,路径的类型。之一:dirfilehard_linksym_linkwatcherothereffect_type ,“发生了什么”。之一:renamemodifycreatedestroyownerothereffect_time ,自时期以来纳秒中事件的时间。associated (事件,C ++)或associated_path_name (所有其他实现,单个路径名):(请注意,对于JavaScript,我们使用骆驼盒与该语言的生态系统保持一致。)
watcher类型很特别。
此类型的事件将包括来自观察者的消息。您可以收到错误消息或重要状态更新。
选择这种格式以以通用,便携式格式支持观察者的异步消息。
最重要的“观察者”事件中有两个是初始“现场”事件和最后的“模具”事件。
该消息出现在手表的基本路径上。
例如,在/a/path打开观察者后,您可以从Watcher那里收到这些消息:
s/self/live@/a/pathe/self/die@/a/path消息始终以s开头,表明成功的操作, w ,指示非致命警告或e ,表示致命错误。
重要的是,如果关闭观察者将始终产生错误
self/live信息;或者,换句话说,如果观察者尚未完全启动。在这种情况下,观察者完全打开后将立即关闭,并报告所有关闭呼叫的错误。最后一个事件将永远是观看者的destroy活动。您可以像这样解析它,对于某些事件ev :
ev.path_type == path_type::watcher && ev.effect_type == effect_type::destroy;快乐的黑客。
该项目试图使您可以轻松地处理文件系统事件。我认为好的工具易于使用。如果此项目不是符合人体工程学的,请提出问题。
这是在编写本段之前准备此提交时所采取的输出的快照。
{
"1666393024210001000" : {
"path_name" : " /home/edant/dev/watcher/.git/logs/HEAD " ,
"effect_type" : " modify " ,
"path_type" : " file "
},
"1666393024210026000" : {
"path_name" : " /home/edant/dev/watcher/.git/logs/refs/heads/next " ,
"effect_type" : " modify " ,
"path_type" : " file "
},
"1666393024210032000" : {
"path_name" : " /home/edant/dev/watcher/.git/refs/heads/next.lock " ,
"effect_type" : " create " ,
"path_type" : " other "
}
}这很酷。
有能力的程序在这里。
该项目可以通过:
tool/build :包括C ++标头/库,CLI,测试和基准目标tool/cross :包括watcher-c共享库和标题,用于许多平台的交叉编译watcher-c库(静态和共享),CLI,测试和基准目标在此处查看包。
nix build # To just build
nix run # Build the default target, then run without arguments
nix run . -- / | jq # Build and run, watch the root directory, pipe it to jq
nix develop # Enter an isolated development shell with everything needed to explore this projectbazel build cli # Build, but don't run, the cli
bazel build hdr # Ditto, for the single-header
bazel run cli # Run the cli program without argumentstool/build
cd out/this/Release
# watches the current directory forever
./wtr.watcher
# watches some path for 10 seconds
./wtr.watcher ' your/favorite/path ' -s 10这将照顾一些平台特定的,建立发行版,调试和消毒器变体,并进行一些测试。
cmake -S . -B out
cmake --build out --config Release
cd out
# watches the current directory forever
./wtr.watcher
# watches some path for 10 seconds
./wtr.watcher ' your/favorite/path ' -s 10所有平台上的观察者故意忽略了仅更改文件或目录上的ACESS时间的修改事件。
这些事件的实用性值得怀疑。
它似乎比好有害。其他观察者,例如Microsoft的C#Watcher,默认情况下会忽略它们。一些用户应用程序依靠修改事件来知道何时重新加载文件。
更好的是,存在更完整的解决方案,这些默认设置可能会再次改变。
提供一种忽略过程ID的事件,“此”过程中的速记的方法,以及一种指定我们感兴趣的事件来源的方法是更完整的解决方案的好候选人。
当我第一次写这篇文章时,我对C ++很满意。后来,我将这个项目重写为Rust的实验。生锈有好处和缺点。有些事情有点安全,而其他事情绝对不是。例如,在核心上进行一些可变尺寸的不透明类型进行指针数学的必要性并不安全。其他事情更安全,但是这个项目并不能从中受益匪浅。
Rust确实闪耀着可用性和表达。这可能足以使用它。除其他事项外,我们可以使用异步性状和代数类型来实现良好的好处。
我不确定是否有一种语言可以从定义上“使该项目中的大部分代码”安全。
这个项目的胆量,适配器,与内核对话。他们必然会使用不安全,不易于警告的系统级界面。
公共API仅大约100行,经过良好的经验,经过良好的测试且可以验证。那里没有太多发生。
通过用C ABI曝光适配器来创建FFI可能值得。大多数语言都应该能够吸引这一点。
平台适配器的安全性一定取决于每个平台的界面文档。与所有系统级接口一样,只要我们确保正确的前和条件,这些条件是明确的,我们应该没事。
在特定于平台的实现中, FSEvents API在Darwin上使用,并且在Windows上使用了ReadDirectoryChanges API。我们要做一些额外的工作来选择Linux上的最佳适配器。当内核版本大于5.9时,使用fanotify适配器,包含过程具有根特性,则允许使用必要的系统调用。尽管有必要的特权和内核版本,但在集装箱或cgroup内部内部可能会不允许与fanotify相关的系统调用。否则将使用inotify适配器。您可以在此处找到Linux的选择代码。
适配器的名称空间是内联的。当调用(内部) detail::...::watch()函数时,它将解析为一个(且仅一个)平台 - 规格实现了watch()函数。一个符号是许多平台,其中平台是内联名称空间。
当我们带出与平台无关的适配器warthog时,效率受到了打击。该适配器用于缺乏更好替代方案的平台,例如(不是达尔文)BSD和Solaris(因为warthog击败了kqueue )。
当观察者没有比warthog更好的选择时,它仍然相对有效。作为拇指规则,扫描超过1000千的路径可能会warthog 。
我会睁开眼睛,以在BSD上获得更好的内核API。
当观察者准备将事件发送到回调时,没有可靠的方式进行交流。
对于几千条路径,这可能需要几毫秒。对于成千上万的路径,请考虑等待几秒钟。
特定于平台的实现都没有提供有关从中更改哪些属性的信息。这使得支持这些事件取决于自己存储此信息。将路径的图表存储到stat结构,在属性变化上差异是无关紧要的内存承诺。
所有者和属性事件不支持,因为我不确定如何支持这些事件。
不包括/proc和/sys在内的特殊文件系统无法通过inotify , fanotify或warthog观看。未来的工作可能涉及派遣EBPF程序供内核使用。这将使我们能够在其中一些特殊文件系统上监视modify事件。
使用inotify时,观察文件的数量受到限制。
对于仅限标题库和Tiny-Watcher,C ++ 17及以上应该很好。
有一天,我们可能会使用C ++ 20 Coroutines。
$ tool/gen-event/dir &
$ tool/gen-event/file &
$ valgrind --tool=cachegrind wtr.watcher ~ -s 30I refs: 797,368,564
I1 misses: 6,807
LLi misses: 2,799
I1 miss rate: 0.00%
LLi miss rate: 0.00%
D refs: 338,544,669 (224,680,988 rd + 113,863,681 wr)
D1 misses: 35,331 ( 24,823 rd + 10,508 wr)
LLd misses: 11,884 ( 8,121 rd + 3,763 wr)
D1 miss rate: 0.0% ( 0.0% + 0.0% )
LLd miss rate: 0.0% ( 0.0% + 0.0% )
LL refs: 42,138 ( 31,630 rd + 10,508 wr)
LL misses: 14,683 ( 10,920 rd + 3,763 wr)
LL miss rate: 0.0% ( 0.0% + 0.0% )名称空间和符号紧密遵循devel/include文件夹中的目录。内联名称空间与-词缀为目录。
例如, wtr::watch在文件内部devel/include/wtr/watcher-/watch.hpp中。 wtr::watcher::watch中的命名空间watcher在本惯例上是匿名的。
更深入:函数::detail::wtr::watcher::adapter::watch()在一个(文件中只有一个!)内定义devel/include/detail/wtr/watcher/adapter/*/watch.hpp , *在compile time(根据主机的操作系统)确定 *。
将devel/include中的所有标题合并到include/wtr/watcher.hpp中,并添加了guard guard。包含后卫不会随发行版而更改。将来,它可能。
watcher
├── src
│ └── wtr
│ ├── watcher
│ │ └── main.cpp
│ └── tiny_watcher
│ └── main.cpp
├── out
├── include
│ └── wtr
│ └── watcher.hpp
└── devel
├── src
│ └── wtr
└── include
├── wtr
│ ├── watcher.hpp
│ └── watcher-
│ ├── watch.hpp
│ └── event.hpp
└── detail
└── wtr
└── watcher
├── semabin.hpp
└── adapter
├── windows
│ └── watch.hpp
├── warthog
│ └── watch.hpp
├── linux
│ ├── watch.hpp
│ ├── sysres.hpp
│ ├── inotify
│ │ └── watch.hpp
│ └── fanotify
│ └── watch.hpp
└── darwin
└── watch.hpp
您可以运行
tool/tree以本地查看此树。
https://github.com/notify-rs/notify :
lines of code : 2799
lines of tests : 475
lines of docs : 1071
implementation languages : rust
interface languages : rust
supported platforms : linux, windows, darwin, bsd
kernel apis : inotify, readdirectorychanges, fsevents, kqueue
non-blocking : yes
dependencies : none
tests : yes
static analysis : yes (borrow checked, memory and concurrency safe language)
https://github.com/e-dant/watcher :
lines of code : 1579
lines of tests : 881
lines of docs : 1977
implementation languages : cpp
interface languages : cpp, shells
supported platforms : linux, darwin, windows, bsd
kernel apis : inotify, fanotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : yes
static analysis : yes
https://github.com/facebook/watchman.git :
lines of code : 37435
lines of tests : unknown
lines of docs : unknown
implementation languages : cpp, c
interface languages : cpp, js, java, python, ruby, rust, shells
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : inotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : yes (many)
static analysis : yes (all available)
https://github.com/p-ranav/fswatch :
lines of code : 245
lines of tests : 19
lines of docs : 114
implementation languages : cpp
interface languages : cpp, shells
supported platforms : linux, darwin, windows, bsd
kernel apis : inotify
non-blocking : maybe
dependencies : none
tests : some
static analysis : none
https://github.com/tywkeene/go-fsevents :
lines of code : 413
lines of tests : 401
lines of docs : 384
implementation languages : go
interface languages : go
supported platforms : linux
kernel apis : inotify
non-blocking : yes
dependencies : yes
tests : yes
static analysis : none (gc language)
https://github.com/radovskyb/watcher :
lines of code : 552
lines of tests : 767
lines of docs : 399
implementation languages : go
interface languages : go
supported platforms : linux, darwin, windows
kernel apis : none
non-blocking : no
dependencies : none
tests : yes
static analysis : none
https://github.com/parcel-bundler/watcher :
lines of code : 2862
lines of tests : 474
lines of docs : 791
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : fsevents, inotify, readdirectorychanges
non-blocking : yes
dependencies : none
tests : some (js bindings)
static analysis : none (interpreted language)
https://github.com/atom/watcher :
lines of code : 7789
lines of tests : 1864
lines of docs : 1334
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : inotify, fsevents, readdirectorychanges
non-blocking : yes
dependencies : none
tests : some (js bindings)
static analysis : none
https://github.com/paulmillr/chokidar :
lines of code : 1544
lines of tests : 1823
lines of docs : 1377
implementation languages : js
interface languages : js
supported platforms : linux, darwin, windows, bsd
kernel apis : fsevents
non-blocking : maybe
dependencies : yes
tests : yes (many)
static analysis : none (interpreted language)
https://github.com/Axosoft/nsfw :
lines of code : 2536
lines of tests : 1085
lines of docs : 148
implementation languages : cpp
interface languages : js
supported platforms : linux, darwin, windows, maybe bsd
kernel apis : fsevents
non-blocking : maybe
dependencies : yes (many)
tests : yes (js bindings)
static analysis : none
https://github.com/canton7/SyncTrayzor :
lines of code : 17102
lines of tests : 0
lines of docs : 2303
implementation languages : c #
interface languages : c #
supported platforms : windows
kernel apis : unknown
non-blocking : yes
dependencies : unknown
tests : none
static analysis : none (managed language)
https://github.com/g0t4/Rx-FileSystemWatcher :
lines of code : 360
lines of tests : 0
lines of docs : 46
implementation languages : c #
interface languages : c #
supported platforms : windows
kernel apis : unknown
non-blocking : yes
dependencies : unknown
tests : yes
static analysis : none (managed language)