inotify是linux系统监控文件事件的实现,但是这也就局限于linux系统,不同的操作系统都有自己的实现方式:
这里watchdog为我们提供了一种跨平台能力,实现了对各个平台的封装,所以我们可以放心在不同的平台来实现类似inotify的机制,监控文件系统事件。
watchdog用来监控指定目录/文件的变化,如添加删除文件或目录、修改文件内容、重命名文件或目录等,每种变化都会产生一个事件,且有一个特定的事件类与之对应,然后再通过事件处理器来处理对应的事件,怎么处理完全可以自定义,只需要继承事件处理类的基类并重写对应实例方法即可:
官网例子:
1 | import sys |
2 | import time |
3 | import logging |
4 | from watchdog.observer import Observer |
5 | from watchdog.events import LoggingEventHandler |
6 | |
7 | if __name__ == "__main__": |
8 | logging.basicConfig(level=logging.INFO,format="%(asctime)s - %(message)s" |
9 | ,datefmt="%Y-%m-%d %H:%M:%S") #定义日志格式 |
10 | path = sys.argv[1] if len(sys.argv) > 1 else '.' #默认为当前目录 |
11 | event_handler = LoggingEventHandler() #输出日志handler,将指定目录发生任何变化都会打印到终端 |
12 | observer = Observer() |
13 | observer.schedule(event_handler,path,recursive=True) |
14 | observer.start() |
15 | try: |
16 | while True: |
17 | time.sleep() |
18 | except KeyboardInterrupt: |
19 | observer.stop() |
20 | observer.join() |
21 | ``` |
22 | |
23 | ## 事件类 |
24 | |
25 | ```python |
26 | watchdog.events.FileSystemEvent(event_type,src_path,is_directory=False) |
事件类的基类,所有具体事件类的父类,当一个目录或文件发生变化时,就会产生一个特定事件,也就是该类的子类。
属性和方法
- event.is_directory
改事件是否由一个目录触发 - event.src_path
触发该事件的文件或目录路径 - event.event_type
事件类型,为moved,deleted,created或modified - event.key
返回元组(event_type,src_path,is_directory)
子类
- watchdog.events.FileDeleteEvent() 文件被删除时触发该事件
- watchdog.events.DirDeleteEvent() 目录被删除时触发该事件
- watchdog.events.FileCreatedEvent() 文件被创建时触发该事件
- watchdog.events.DirCreatedEvent() 目录创建时触发该事件
- watchdog.events.FileModifiedEvent() 文件被修改时触发该事件(修改文件内容、修改文件inode信息如权限或访问时间)
- watchdog.events.DirModifiedEvent() 目录被修改时触发
- watchdog.events.FileMovedEvent() 文件被移动或重命名时触发该事件,因为涉及文件移动,所以除了event.src_path表示原路径,还有event.dest_path表示目的路径
- watchdog.events.DirMovedEvent() 目录被移动或重命名时触发该事件,因为涉及目录移动,所以除了event.src_path表示原路径,还有event.dest_path表示目的路径
事件处理类
1 | watchdog.event.FileSystemEventHandler() |
事件处理类的基类,用于处理事件,用户需继承该类,并在子类中重写对应方法。
类实例方法
- self.dispatch(event)
接受事件后,通过该方法来决定该event由下面哪个方法处理 - self.on_any_event(event)
任何事件发生都会首先执行该方法,该方法默认为空,dispatch()方法都会执行改方法,然后再把event分派给其他方法处理 - self.on_moved(event)
处理DirMovedEvent和FileMovedEvent事件,子类需重写该方法 - self.on_created(event)
处理DirCreatedEvent和FileCreatedEvent事件,子类需重写该方法 - self.on_deleted(event)
处理DirDeletedEvent和FileDeletedEvent事件,子类需重写该方法 - self.on_modified(event)
处理DirModifiedEvent和FileModifiedEvent事件,子类需重写该方法
watchdog默认提供的一些事件处理类:
1 | watchdog.events.PatternMatchingEventHandler(patterns=None, |
2 | ignore_patterns=None, |
3 | ignore_directories=False, |
4 | case_sensitive=False) |
该类会检查触发事件的src_path和dest_path,是否与pattern指定的模式匹配;ignore_patterns是需要排除不处理的模式,如果路径匹配该模式则不处理;还有ignore_directories为True则表示不处理由目录引起的事件;case_sensitive为True则表示路径不区分大小写。如果需要按模式匹配处理事件,则可以继承该类,不过需要自己实现on_moved(),on_created(),on_deleted(),on_modified()这四个方法。
1 | watchdog.events.RegexMatchingEventHandler(regexes=[r".*"], |
2 | ignore_regexes=[], |
3 | ignore_directories=False, |
4 | case_sensitive=False) |
基本等同于PatternMatchingEventHandler()类,除了是使用正则,而不是模式匹配。
1 | watchdog.events.LoggingEventHandler() |
使用logging模块记录所有事件信息。
自定义事件处理类
1 | import time |
2 | from watchdog.observer import Observer |
3 | from watchdog.events import FileSystemEventHandler |
4 | |
5 | class MyHandler(FileSystemEventHandler): |
6 | def __init__(self): |
7 | FileSystemEventHandler.__init__(self) |
8 | |
9 | def on_modified(self,event): |
10 | if event.src_path == "/home/jusene/logs/access.log": |
11 | print('log file {} changed!'.format(event.src_path)) |
12 | |
13 | if __name__ == "__main__": |
14 | event_handler = MyHandler() |
15 | observer = Observer() |
16 | observer.schedule(event_handler,path='.',True) |
17 | observer.start() |
18 | try: |
19 | while True: |
20 | time.sleep(1) |
21 | except KeyboardInterrupt: |
22 | observer.stop() |
23 | observer.join() |
实例的属性及方法
observer.schedule(event_handler, path, recursive=False)
监控指定路径path,该路径触发任何事件都会调用event_handler来处理,如果path是目录,则recursive=True则会递归监控该目录的所有变化。每一次调用schedule()对一个路径进行监控处理就叫做一个watch,schedule()方法会返回这个watch,接着可以对这个watch做其他操作,如为该watch增加多个event处理器等注:内部由一个字典handlers来保存所有watch,watch的值是一个集合,包含对应此watch的所有event handler:
- observer.add_handler_for_watch(event_handler,watch)
添加一个新的事件处理器到watch中,watch是ObserverdWatch()类或其子类的实例
observer.remove_handler_for_watch(event_handler,watch)
从watch中移除一个事件处理器
observer.unschedule(watch)
移除一个watch及这个watch上的所有事件处理器
observer.unschedule_all()
移除所有watch及关联的事件处理器
observer.on_thread_stop()
等同observer.unschedule_all()
observer.stop()
调用该方法来停止observer线程
1 | import time |
2 | import logging |
3 | from watchdog.observer import Observer |
4 | from watchdog.events import FileSystemEventHandler,LoggingEventHandler |
5 | from watchdog.observers.api import ObserverdWatch |
6 | |
7 | class MyHandler(FileSystemEventHandler): |
8 | def on_modified(self,event): |
9 | if event.src_path == "/home/jusene/logs/access.log": |
10 | print('log file %s changed!' % event.src_path) |
11 | |
12 | if __name__ == "__main__": |
13 | event_handler = MyHandler() |
14 | observer = Observer() |
15 | watch = observer.schedule(event_handler,path='.',recursive=True) |
16 | logging.basicConfig(level=logging.INFO, |
17 | format='%(asctime)s - %(message)s', |
18 | datefmt='%Y-%m-%d %H:%M:%S') |
19 | event_handler1 = LoggingEventHandler() |
20 | observer.add_handler_for_watch(event_handler1,watch) |
21 | observer.start() |
22 | try: |
23 | while True: |
24 | time.sleep(1) |
25 | except KeyboardInterrupt: |
26 | observer.stop() |
27 | observer.join() |