Jusene's Blog

watchdog 监控文件系统事件

字数统计: 1.5k阅读时长: 6 min
2018/05/23 Share

inotify是linux系统监控文件事件的实现,但是这也就局限于linux系统,不同的操作系统都有自己的实现方式:

  • inotify: linux 2.6.13+
  • fsevents: mac os
  • kqueue: mac os and bsd
  • winapi: ms windows
  • polling: any

这里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()
CATALOG
  1. 1. 属性和方法
  2. 2. 子类
  • 事件处理类
    1. 1. 类实例方法
  • 自定义事件处理类
    1. 1. 实例的属性及方法