渚  直樹

渚 直樹

1633972020

Pythonでウォッチドッグを作成する方法

ソフトウェア開発では、アプリケーションのログ記録が重要な役割を果たします。ソフトウェアを完璧にしたいと思う限り、常に問題が発生するため、避けられない混乱を制御および管理するために、堅牢な監視とロギングを実施することが重要です。

今日、アプリケーションサポートエンジニアは、アプリケーションとインフラストラクチャが生成している膨大な量のログデータに簡単にアクセスして分析できる必要があります。問題が発生した場合、クエリが結果を返すまで1〜2分待つ余裕はありません。収集およびクエリするデータの量に関係なく、速度が必要です。

このチュートリアルでは、Pythonでウォッチドッグを作成する方法を学習します。特定のディレクトリの変更を検出する方法を説明します(アプリケーションのログをホストしているディレクトリを想定しましょう)。変更が発生するたびに、事前定義されたタイプの変更または新しく作成されたファイルがタイムリーに処理され、指定されたパターンを満たす行が取得されます。

一方、指定されたパターンに一致しないこれらのファイル内のすべての行は、外れ値と見なされ、分析では破棄されます。

発生する変更を検出するためにウォッチドッグライブラリとピグテールライブラリを使用 します。同じ目的でGUIWebアプリが作成されるFlask、Redis、SocketIOバージョンもありますここでいつでも参照できます

工程流れ図

process-flowchart-of-watchdog-tutorial-in-python開始するには、要件をインストールしましょう:

$ pip3 install Pygtail==0.11.1 watchdog==2.1.1

まず、アプリケーションの構成パラメーターを次のように定義しましょうconfig.py

# Application configuration File
################################
# Directory To Watch, If not specified, the following value will be considered explicitly.
WATCH_DIRECTORY = "C:\\SCRIPTS"
# Delay Between Watch Cycles In Seconds
WATCH_DELAY = 1
# Check The WATCH_DIRECTORY and its children
WATCH_RECURSIVELY = False
# whether to watch for directory events
DO_WATCH_DIRECTORIES = True
# Patterns of the files to watch
WATCH_PATTERN = '.txt,.trc,.log'
LOG_FILES_EXTENSIONS = ('.txt', '.log', '.trc')
# Patterns for observations
EXCEPTION_PATTERN = ['EXCEPTION', 'FATAL', 'ERROR']
  

のパラメータconfig.pyはデフォルトのパラメータになります。後でスクリプトで、必要に応じてパラメータを上書きできます。

次に、チェックメカニズムを定義しましょう。このメカニズムは、モジュールpygtailとreを活用して、EXCEPTION_PATTERN先ほど定義したパラメーターに基づいて観測値を特定しconfig.pyます。

import datetime
from pygtail import Pygtail

# Loading the package called re from the RegEx Module in order to work with Regular Expressions
import re

class FileChecker:
    def __init__(self, exceptionPattern):
        self.exceptionPattern = exceptionPattern

    def checkForException(self, event, path):
        # Get current date and time according to the specified format.
        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
        # Read the lines of the file (specified in the path) that have not been read yet
        # Meaning by that it will start from the point where it was last stopped.
        for num, line in enumerate(Pygtail(path), 1):
            # Remove leading and trailing whitespaces including newlines.
            line = line.strip()
            # Return all non-overlapping matches of the values specified in the Exception Pattern.
            # The line is scanned from left to right and matches are returned in the oder found.
            if line and any(re.findall('|'.join(self.exceptionPattern), line, flags=re.I | re.X)):
                # Observation Detected
                type = 'observation'
                msg = f"{now} -- {event.event_type} -- File = {path} -- Observation: {line}"
                yield type, msg
            elif line:
                # No Observation Detected
                type = 'msg'
                msg = f"{now} -- {event.event_type} -- File = {path}"
                yield type, msg

checkForException()上記のコードで定義されたメソッドは、ウォッチドッグモジュールのオブザーバークラスによってディスパッチされたイベントを取り込みます(後で説明します)。

これらのイベントは、指定されたディレクトリ内のファイルが変更されるとトリガーされます。イベントオブジェクトには次の3つの属性があります。

  • event_type:文字列としてのイベントのタイプ(変更、作成、移動、または削除)。
  • is_directory:イベントがディレクトリに対して発行されたかどうかを示すブール値。
  • src_path:イベントをトリガーしたファイルシステムオブジェクトのソースパス。

それではcontroller.py、まず、ライブラリをインポートして定義しましょう。

# The Observer watches for any file change and then dispatches the respective events to an event handler.
from watchdog.observers import Observer
# The event handler will be notified when an event occurs.
from watchdog.events import FileSystemEventHandler
import time
import config
import os
from checker import FileChecker
import datetime
from colorama import Fore, init

init()

GREEN = Fore.GREEN
BLUE = Fore.BLUE
RESET = Fore.RESET
RED = Fore.RED
YELLOW = Fore.YELLOW

event2color = {
    "created": GREEN,
    "modified": BLUE,
    "deleted": RED,
    "moved": YELLOW,
}

def print_with_color(s, color=Fore.WHITE, brightness=Style.NORMAL, **kwargs):
    """Utility function wrapping the regular `print()` function 
    but with colors and brightness"""
    print(f"{brightness}{color}{s}{Style.RESET_ALL}", **kwargs)

このLogHandlerクラスFileSystemEventHandler は、ウォッチドッグライブラリの名前が付けられたクラスを継承し、主にそのon_any_event() メソッドを上書きします 。

このクラスの場合、以下はいくつかの便利なメソッドです。

  • on_any_event():イベントのために呼び出されます。
  • on_created():ファイルまたはディレクトリが作成されたときに呼び出されます。
  • on_modified():ファイルが変更されたとき、またはディレクトリの名前が変更されたときに呼び出されます。
  • on_deleted():ファイルまたはディレクトリが削除されたときに呼び出されます。
  • on_moved():ファイルまたはディレクトリが移動されたときに呼び出されます。

on_any_event()メソッドに割り当てられたコードは次のようになります。

  • ファイルとディレクトリを観察します。
  • イベントの対象となるファイルの拡張子が、WATCH_PATTERN 内の変数で事前定義されているものの中にあることを確認し ます config.py
  • イベントまたは検出された場合の観察を説明するメッセージを生成します。

それでは、LogWatcherクラスを作成しましょう。

class LogWatcher:
    # Initialize the observer
    observer = None
    # Initialize the stop signal variable
    stop_signal = 0
    # The observer is the class that watches for any file system change and then dispatches the event to the event handler.
    def __init__(self, watchDirectory, watchDelay, watchRecursively, watchPattern, doWatchDirectories, exceptionPattern, sessionid, namespace):
        # Initialize variables in relation
        self.watchDirectory = watchDirectory
        self.watchDelay = watchDelay
        self.watchRecursively = watchRecursively
        self.watchPattern = watchPattern
        self.doWatchDirectories = doWatchDirectories
        self.exceptionPattern = exceptionPattern
        self.namespace = namespace
        self.sessionid = sessionid

        # Create an instance of watchdog.observer
        self.observer = Observer()
        # The event handler is an object that will be notified when something happens to the file system.
        self.event_handler = LogHandler(watchPattern, exceptionPattern, self.doWatchDirectories)

    def schedule(self):
        print("Observer Scheduled:", self.observer.name)
        # Call the schedule function via the Observer instance attaching the event
        self.observer.schedule(
            self.event_handler, self.watchDirectory, recursive=self.watchRecursively)

    def start(self):
        print("Observer Started:", self.observer.name)
        self.schedule()
        # Start the observer thread and wait for it to generate events
        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
        msg = f"Observer: {self.observer.name} - Started On: {now} - Related To Session: {self.sessionid}"
        print(msg)

        msg = (
            f"Watching {'Recursively' if self.watchRecursively else 'Non-Recursively'}: {self.watchPattern}"
            f" -- Folder: {self.watchDirectory} -- Every: {self.watchDelay}(sec) -- For Patterns: {self.exceptionPattern}"
        )
        print(msg)
        self.observer.start()

    def run(self):
        print("Observer is running:", self.observer.name)
        self.start()
        try:
            while True:
                time.sleep(self.watchDelay)

                if self.stop_signal == 1:
                    print(
                        f"Observer stopped: {self.observer.name}  stop signal:{self.stop_signal}")
                    self.stop()
                    break
        except:
            self.stop()
        self.observer.join()

    def stop(self):
        print("Observer Stopped:", self.observer.name)

        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
        msg = f"Observer: {self.observer.name} - Stopped On: {now} - Related To Session: {self.sessionid}"
        print(msg)
        self.observer.stop()
        self.observer.join()

    def info(self):
        info = {
            'observerName': self.observer.name,
            'watchDirectory': self.watchDirectory,
            'watchDelay': self.watchDelay,
            'watchRecursively': self.watchRecursively,
            'watchPattern': self.watchPattern,
        }
        return info

これが私たちがLogWatcherクラスでしたことです:

  • watchdog.observerスレッドクラスのインスタンスを作成すると、オブザーバーはファイルシステムの変更を監視し、対応するイベントをイベントハンドラーにディスパッチします。
  • LogHandlerを継承するイベントハンドラのインスタンスを作成しますFileSystemEventHandler。変更が発生すると、イベントハンドラーに通知されます。
  • オブザーバーにスケジュールを割り当て、監視するディレクトリ、監視モードなどの他の入力パラメータを定義します。recursiveパラメータをに設定するときTrueは、サブフォルダに対する十分なアクセス権があることを確認する必要があることに注意してください。

最後に、argparse:を使用してコードの周りにコマンドライン引数を作成しましょう。

def is_dir_path(path):
    """Utility function to check whether a path is an actual directory"""
    if os.path.isdir(path):
        return path
    else:
        raise NotADirectoryError(path)

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(
        description="Watchdog script for watching for files & directories' changes")
    parser.add_argument("path",
                        default=config.WATCH_DIRECTORY,
                        type=is_dir_path,
                        )
    parser.add_argument("-d", "--watch-delay",
                        help=f"Watch delay, default is {config.WATCH_DELAY}",
                        default=config.WATCH_DELAY,
                        type=int,
                        )
    parser.add_argument("-r", "--recursive",
                        action="store_true",
                        help=f"Whether to recursively watch for the path's children, default is {config.WATCH_RECURSIVELY}",
                        default=config.WATCH_RECURSIVELY,
                        )
    parser.add_argument("-p", "--pattern",
                        help=f"Pattern of files to watch, default is {config.WATCH_PATTERN}",
                        default=config.WATCH_PATTERN,
                        )
    parser.add_argument("--watch-directories",
                        action="store_true",
                        help=f"Whether to watch directories, default is {config.DO_WATCH_DIRECTORIES}",
                        default=config.DO_WATCH_DIRECTORIES,
                        )
    # parse the arguments
    args = parser.parse_args()
    # define & launch the log watcher
    log_watcher = LogWatcher(
        watchDirectory=args.path,
        watchDelay=args.watch_delay,
        watchRecursively=args.recursive,
        watchPattern=tuple(args.pattern.split(",")),
        doWatchDirectories=args.watch_directories,
        exceptionPattern=config.EXCEPTION_PATTERN,
    )
    log_watcher.run()

is_dir_path()入力したパスが有効なディレクトリであることを確認するために定義しました。スクリプトを使用してみましょう:

Pythonでウォッチドッグスクリプトを実行するサブフォルダを含むディレクトリで--recursiveすべてが行われていることを確認するために合格しました。また、テキストファイルと画像ファイルを監視するためのE:\watchdogパターンを指定しました.txt,.log,.jpg,.png

次に、フォルダを作成してテキストファイルへの書き込みを開始し、画像を移動して削除しました。ウォッチドッグがすべてをキャッチしています。

ここでパラメータを上書きするかconfig.py、パラメータを渡すかを選択できることに注意してください。

結論

ウォッチドッグライブラリとピグテールライブラリの利用可能な機能を詳しく調べた後、この記事がお役に立てば幸いです。

説明されている機能を拡張することにより、ログファイルの1つで致命的なエラーが発生したときに、アラートメカニズムを関連付けたり、サウンドを再生したりできることに注意してください。そうすることで、監視が特定されると、構成されたワークフローまたはアラートが自動的にトリガーされます。

ここで完全なコードを確認してください。

リンク: https://www.thepythoncode.com/article/create-a-watchdog-in-python

#python 

What is GEEK

Buddha Community

Pythonでウォッチドッグを作成する方法
渚  直樹

渚 直樹

1633972020

Pythonでウォッチドッグを作成する方法

ソフトウェア開発では、アプリケーションのログ記録が重要な役割を果たします。ソフトウェアを完璧にしたいと思う限り、常に問題が発生するため、避けられない混乱を制御および管理するために、堅牢な監視とロギングを実施することが重要です。

今日、アプリケーションサポートエンジニアは、アプリケーションとインフラストラクチャが生成している膨大な量のログデータに簡単にアクセスして分析できる必要があります。問題が発生した場合、クエリが結果を返すまで1〜2分待つ余裕はありません。収集およびクエリするデータの量に関係なく、速度が必要です。

このチュートリアルでは、Pythonでウォッチドッグを作成する方法を学習します。特定のディレクトリの変更を検出する方法を説明します(アプリケーションのログをホストしているディレクトリを想定しましょう)。変更が発生するたびに、事前定義されたタイプの変更または新しく作成されたファイルがタイムリーに処理され、指定されたパターンを満たす行が取得されます。

一方、指定されたパターンに一致しないこれらのファイル内のすべての行は、外れ値と見なされ、分析では破棄されます。

発生する変更を検出するためにウォッチドッグライブラリとピグテールライブラリを使用 します。同じ目的でGUIWebアプリが作成されるFlask、Redis、SocketIOバージョンもありますここでいつでも参照できます

工程流れ図

process-flowchart-of-watchdog-tutorial-in-python開始するには、要件をインストールしましょう:

$ pip3 install Pygtail==0.11.1 watchdog==2.1.1

まず、アプリケーションの構成パラメーターを次のように定義しましょうconfig.py

# Application configuration File
################################
# Directory To Watch, If not specified, the following value will be considered explicitly.
WATCH_DIRECTORY = "C:\\SCRIPTS"
# Delay Between Watch Cycles In Seconds
WATCH_DELAY = 1
# Check The WATCH_DIRECTORY and its children
WATCH_RECURSIVELY = False
# whether to watch for directory events
DO_WATCH_DIRECTORIES = True
# Patterns of the files to watch
WATCH_PATTERN = '.txt,.trc,.log'
LOG_FILES_EXTENSIONS = ('.txt', '.log', '.trc')
# Patterns for observations
EXCEPTION_PATTERN = ['EXCEPTION', 'FATAL', 'ERROR']
  

のパラメータconfig.pyはデフォルトのパラメータになります。後でスクリプトで、必要に応じてパラメータを上書きできます。

次に、チェックメカニズムを定義しましょう。このメカニズムは、モジュールpygtailとreを活用して、EXCEPTION_PATTERN先ほど定義したパラメーターに基づいて観測値を特定しconfig.pyます。

import datetime
from pygtail import Pygtail

# Loading the package called re from the RegEx Module in order to work with Regular Expressions
import re

class FileChecker:
    def __init__(self, exceptionPattern):
        self.exceptionPattern = exceptionPattern

    def checkForException(self, event, path):
        # Get current date and time according to the specified format.
        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
        # Read the lines of the file (specified in the path) that have not been read yet
        # Meaning by that it will start from the point where it was last stopped.
        for num, line in enumerate(Pygtail(path), 1):
            # Remove leading and trailing whitespaces including newlines.
            line = line.strip()
            # Return all non-overlapping matches of the values specified in the Exception Pattern.
            # The line is scanned from left to right and matches are returned in the oder found.
            if line and any(re.findall('|'.join(self.exceptionPattern), line, flags=re.I | re.X)):
                # Observation Detected
                type = 'observation'
                msg = f"{now} -- {event.event_type} -- File = {path} -- Observation: {line}"
                yield type, msg
            elif line:
                # No Observation Detected
                type = 'msg'
                msg = f"{now} -- {event.event_type} -- File = {path}"
                yield type, msg

checkForException()上記のコードで定義されたメソッドは、ウォッチドッグモジュールのオブザーバークラスによってディスパッチされたイベントを取り込みます(後で説明します)。

これらのイベントは、指定されたディレクトリ内のファイルが変更されるとトリガーされます。イベントオブジェクトには次の3つの属性があります。

  • event_type:文字列としてのイベントのタイプ(変更、作成、移動、または削除)。
  • is_directory:イベントがディレクトリに対して発行されたかどうかを示すブール値。
  • src_path:イベントをトリガーしたファイルシステムオブジェクトのソースパス。

それではcontroller.py、まず、ライブラリをインポートして定義しましょう。

# The Observer watches for any file change and then dispatches the respective events to an event handler.
from watchdog.observers import Observer
# The event handler will be notified when an event occurs.
from watchdog.events import FileSystemEventHandler
import time
import config
import os
from checker import FileChecker
import datetime
from colorama import Fore, init

init()

GREEN = Fore.GREEN
BLUE = Fore.BLUE
RESET = Fore.RESET
RED = Fore.RED
YELLOW = Fore.YELLOW

event2color = {
    "created": GREEN,
    "modified": BLUE,
    "deleted": RED,
    "moved": YELLOW,
}

def print_with_color(s, color=Fore.WHITE, brightness=Style.NORMAL, **kwargs):
    """Utility function wrapping the regular `print()` function 
    but with colors and brightness"""
    print(f"{brightness}{color}{s}{Style.RESET_ALL}", **kwargs)

このLogHandlerクラスFileSystemEventHandler は、ウォッチドッグライブラリの名前が付けられたクラスを継承し、主にそのon_any_event() メソッドを上書きします 。

このクラスの場合、以下はいくつかの便利なメソッドです。

  • on_any_event():イベントのために呼び出されます。
  • on_created():ファイルまたはディレクトリが作成されたときに呼び出されます。
  • on_modified():ファイルが変更されたとき、またはディレクトリの名前が変更されたときに呼び出されます。
  • on_deleted():ファイルまたはディレクトリが削除されたときに呼び出されます。
  • on_moved():ファイルまたはディレクトリが移動されたときに呼び出されます。

on_any_event()メソッドに割り当てられたコードは次のようになります。

  • ファイルとディレクトリを観察します。
  • イベントの対象となるファイルの拡張子が、WATCH_PATTERN 内の変数で事前定義されているものの中にあることを確認し ます config.py
  • イベントまたは検出された場合の観察を説明するメッセージを生成します。

それでは、LogWatcherクラスを作成しましょう。

class LogWatcher:
    # Initialize the observer
    observer = None
    # Initialize the stop signal variable
    stop_signal = 0
    # The observer is the class that watches for any file system change and then dispatches the event to the event handler.
    def __init__(self, watchDirectory, watchDelay, watchRecursively, watchPattern, doWatchDirectories, exceptionPattern, sessionid, namespace):
        # Initialize variables in relation
        self.watchDirectory = watchDirectory
        self.watchDelay = watchDelay
        self.watchRecursively = watchRecursively
        self.watchPattern = watchPattern
        self.doWatchDirectories = doWatchDirectories
        self.exceptionPattern = exceptionPattern
        self.namespace = namespace
        self.sessionid = sessionid

        # Create an instance of watchdog.observer
        self.observer = Observer()
        # The event handler is an object that will be notified when something happens to the file system.
        self.event_handler = LogHandler(watchPattern, exceptionPattern, self.doWatchDirectories)

    def schedule(self):
        print("Observer Scheduled:", self.observer.name)
        # Call the schedule function via the Observer instance attaching the event
        self.observer.schedule(
            self.event_handler, self.watchDirectory, recursive=self.watchRecursively)

    def start(self):
        print("Observer Started:", self.observer.name)
        self.schedule()
        # Start the observer thread and wait for it to generate events
        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
        msg = f"Observer: {self.observer.name} - Started On: {now} - Related To Session: {self.sessionid}"
        print(msg)

        msg = (
            f"Watching {'Recursively' if self.watchRecursively else 'Non-Recursively'}: {self.watchPattern}"
            f" -- Folder: {self.watchDirectory} -- Every: {self.watchDelay}(sec) -- For Patterns: {self.exceptionPattern}"
        )
        print(msg)
        self.observer.start()

    def run(self):
        print("Observer is running:", self.observer.name)
        self.start()
        try:
            while True:
                time.sleep(self.watchDelay)

                if self.stop_signal == 1:
                    print(
                        f"Observer stopped: {self.observer.name}  stop signal:{self.stop_signal}")
                    self.stop()
                    break
        except:
            self.stop()
        self.observer.join()

    def stop(self):
        print("Observer Stopped:", self.observer.name)

        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")
        msg = f"Observer: {self.observer.name} - Stopped On: {now} - Related To Session: {self.sessionid}"
        print(msg)
        self.observer.stop()
        self.observer.join()

    def info(self):
        info = {
            'observerName': self.observer.name,
            'watchDirectory': self.watchDirectory,
            'watchDelay': self.watchDelay,
            'watchRecursively': self.watchRecursively,
            'watchPattern': self.watchPattern,
        }
        return info

これが私たちがLogWatcherクラスでしたことです:

  • watchdog.observerスレッドクラスのインスタンスを作成すると、オブザーバーはファイルシステムの変更を監視し、対応するイベントをイベントハンドラーにディスパッチします。
  • LogHandlerを継承するイベントハンドラのインスタンスを作成しますFileSystemEventHandler。変更が発生すると、イベントハンドラーに通知されます。
  • オブザーバーにスケジュールを割り当て、監視するディレクトリ、監視モードなどの他の入力パラメータを定義します。recursiveパラメータをに設定するときTrueは、サブフォルダに対する十分なアクセス権があることを確認する必要があることに注意してください。

最後に、argparse:を使用してコードの周りにコマンドライン引数を作成しましょう。

def is_dir_path(path):
    """Utility function to check whether a path is an actual directory"""
    if os.path.isdir(path):
        return path
    else:
        raise NotADirectoryError(path)

if __name__ == "__main__":
    import argparse
    parser = argparse.ArgumentParser(
        description="Watchdog script for watching for files & directories' changes")
    parser.add_argument("path",
                        default=config.WATCH_DIRECTORY,
                        type=is_dir_path,
                        )
    parser.add_argument("-d", "--watch-delay",
                        help=f"Watch delay, default is {config.WATCH_DELAY}",
                        default=config.WATCH_DELAY,
                        type=int,
                        )
    parser.add_argument("-r", "--recursive",
                        action="store_true",
                        help=f"Whether to recursively watch for the path's children, default is {config.WATCH_RECURSIVELY}",
                        default=config.WATCH_RECURSIVELY,
                        )
    parser.add_argument("-p", "--pattern",
                        help=f"Pattern of files to watch, default is {config.WATCH_PATTERN}",
                        default=config.WATCH_PATTERN,
                        )
    parser.add_argument("--watch-directories",
                        action="store_true",
                        help=f"Whether to watch directories, default is {config.DO_WATCH_DIRECTORIES}",
                        default=config.DO_WATCH_DIRECTORIES,
                        )
    # parse the arguments
    args = parser.parse_args()
    # define & launch the log watcher
    log_watcher = LogWatcher(
        watchDirectory=args.path,
        watchDelay=args.watch_delay,
        watchRecursively=args.recursive,
        watchPattern=tuple(args.pattern.split(",")),
        doWatchDirectories=args.watch_directories,
        exceptionPattern=config.EXCEPTION_PATTERN,
    )
    log_watcher.run()

is_dir_path()入力したパスが有効なディレクトリであることを確認するために定義しました。スクリプトを使用してみましょう:

Pythonでウォッチドッグスクリプトを実行するサブフォルダを含むディレクトリで--recursiveすべてが行われていることを確認するために合格しました。また、テキストファイルと画像ファイルを監視するためのE:\watchdogパターンを指定しました.txt,.log,.jpg,.png

次に、フォルダを作成してテキストファイルへの書き込みを開始し、画像を移動して削除しました。ウォッチドッグがすべてをキャッチしています。

ここでパラメータを上書きするかconfig.py、パラメータを渡すかを選択できることに注意してください。

結論

ウォッチドッグライブラリとピグテールライブラリの利用可能な機能を詳しく調べた後、この記事がお役に立てば幸いです。

説明されている機能を拡張することにより、ログファイルの1つで致命的なエラーが発生したときに、アラートメカニズムを関連付けたり、サウンドを再生したりできることに注意してください。そうすることで、監視が特定されると、構成されたワークフローまたはアラートが自動的にトリガーされます。

ここで完全なコードを確認してください。

リンク: https://www.thepythoncode.com/article/create-a-watchdog-in-python

#python