| 
 | 1 | +# The Observer watches for any file change and then dispatches the respective events to an event handler.  | 
 | 2 | +from watchdog.observers import Observer  | 
 | 3 | +# The event handler will be notified when an event occurs.  | 
 | 4 | +from watchdog.events import FileSystemEventHandler  | 
 | 5 | +import time  | 
 | 6 | +import config  | 
 | 7 | +import os  | 
 | 8 | +from checker import FileChecker  | 
 | 9 | +import datetime  | 
 | 10 | +from colorama import Fore, Style, init  | 
 | 11 | + | 
 | 12 | +init()  | 
 | 13 | + | 
 | 14 | +GREEN = Fore.GREEN  | 
 | 15 | +BLUE = Fore.BLUE  | 
 | 16 | +RED = Fore.RED  | 
 | 17 | +YELLOW = Fore.YELLOW  | 
 | 18 | + | 
 | 19 | +event2color = {  | 
 | 20 | +    "created": GREEN,  | 
 | 21 | +    "modified": BLUE,  | 
 | 22 | +    "deleted": RED,  | 
 | 23 | +    "moved": YELLOW,  | 
 | 24 | +}  | 
 | 25 | + | 
 | 26 | + | 
 | 27 | +def print_with_color(s, color=Fore.WHITE, brightness=Style.NORMAL, **kwargs):  | 
 | 28 | +    """Utility function wrapping the regular `print()` function   | 
 | 29 | +    but with colors and brightness"""  | 
 | 30 | +    print(f"{brightness}{color}{s}{Style.RESET_ALL}", **kwargs)  | 
 | 31 | + | 
 | 32 | + | 
 | 33 | +# Class that inherits from FileSystemEventHandler for handling the events sent by the Observer  | 
 | 34 | +class LogHandler(FileSystemEventHandler):  | 
 | 35 | + | 
 | 36 | +    def __init__(self, watchPattern, exceptionPattern, doWatchDirectories):  | 
 | 37 | +        self.watchPattern = watchPattern  | 
 | 38 | +        self.exceptionPattern = exceptionPattern  | 
 | 39 | +        self.doWatchDirectories = doWatchDirectories  | 
 | 40 | +        # Instantiate the checker  | 
 | 41 | +        self.fc = FileChecker(self.exceptionPattern)  | 
 | 42 | + | 
 | 43 | +    def on_any_event(self, event):  | 
 | 44 | +        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")  | 
 | 45 | +        # print("event happened:", event)  | 
 | 46 | +        # To Observe files only not directories  | 
 | 47 | +        if not event.is_directory:  | 
 | 48 | +            # To cater for the on_move event  | 
 | 49 | +            path = event.src_path  | 
 | 50 | +            if hasattr(event, 'dest_path'):  | 
 | 51 | +                path = event.dest_path  | 
 | 52 | +            # Ensure that the file extension is among the pre-defined ones.  | 
 | 53 | +            if path.endswith(self.watchPattern):  | 
 | 54 | +                msg = f"{now} -- {event.event_type} -- File: {path}"  | 
 | 55 | +                if event.event_type in ('modified', 'created', 'moved'):  | 
 | 56 | +                    # check for exceptions in log files  | 
 | 57 | +                    if path.endswith(config.LOG_FILES_EXTENSIONS):  | 
 | 58 | +                        for type, msg in self.fc.checkForException(event=event, path=path):  | 
 | 59 | +                            print_with_color(  | 
 | 60 | +                                msg, color=event2color[event.event_type], brightness=Style.BRIGHT)  | 
 | 61 | +                    else:  | 
 | 62 | +                        print_with_color(  | 
 | 63 | +                            msg, color=event2color[event.event_type])  | 
 | 64 | +                else:  | 
 | 65 | +                    print_with_color(msg, color=event2color[event.event_type])  | 
 | 66 | +        elif self.doWatchDirectories:  | 
 | 67 | +            msg = f"{now} -- {event.event_type} -- Folder: {event.src_path}"  | 
 | 68 | +            print_with_color(msg, color=event2color[event.event_type])  | 
 | 69 | + | 
 | 70 | +    def on_modified(self, event):  | 
 | 71 | +        pass  | 
 | 72 | + | 
 | 73 | +    def on_deleted(self, event):  | 
 | 74 | +        pass  | 
 | 75 | + | 
 | 76 | +    def on_created(self, event):  | 
 | 77 | +        pass  | 
 | 78 | + | 
 | 79 | +    def on_moved(self, event):  | 
 | 80 | +        pass  | 
 | 81 | + | 
 | 82 | + | 
 | 83 | +class LogWatcher:  | 
 | 84 | +    # Initialize the observer  | 
 | 85 | +    observer = None  | 
 | 86 | +    # Initialize the stop signal variable  | 
 | 87 | +    stop_signal = 0  | 
 | 88 | + | 
 | 89 | +    # The observer is the class that watches for any file system change and then dispatches the event to the event handler.  | 
 | 90 | +    def __init__(self, watchDirectory, watchDelay, watchRecursively, watchPattern, doWatchDirectories, exceptionPattern):  | 
 | 91 | +        # Initialize variables in relation  | 
 | 92 | +        self.watchDirectory = watchDirectory  | 
 | 93 | +        self.watchDelay = watchDelay  | 
 | 94 | +        self.watchRecursively = watchRecursively  | 
 | 95 | +        self.watchPattern = watchPattern  | 
 | 96 | +        self.doWatchDirectories = doWatchDirectories  | 
 | 97 | +        self.exceptionPattern = exceptionPattern  | 
 | 98 | + | 
 | 99 | +        # Create an instance of watchdog.observer  | 
 | 100 | +        self.observer = Observer()  | 
 | 101 | +        # The event handler is an object that will be notified when something happens to the file system.  | 
 | 102 | +        self.event_handler = LogHandler(  | 
 | 103 | +            watchPattern, exceptionPattern, self.doWatchDirectories)  | 
 | 104 | + | 
 | 105 | +    def schedule(self):  | 
 | 106 | +        print("Observer Scheduled:", self.observer.name)  | 
 | 107 | +        # Call the schedule function via the Observer instance attaching the event  | 
 | 108 | +        self.observer.schedule(  | 
 | 109 | +            self.event_handler, self.watchDirectory, recursive=self.watchRecursively)  | 
 | 110 | + | 
 | 111 | +    def start(self):  | 
 | 112 | +        print("Observer Started:", self.observer.name)  | 
 | 113 | +        self.schedule()  | 
 | 114 | +        # Start the observer thread and wait for it to generate events  | 
 | 115 | +        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")  | 
 | 116 | +        msg = f"Observer: {self.observer.name} - Started On: {now}"  | 
 | 117 | +        print(msg)  | 
 | 118 | + | 
 | 119 | +        msg = (  | 
 | 120 | +            f"Watching {'Recursively' if self.watchRecursively else 'Non-Recursively'}: {self.watchPattern}"  | 
 | 121 | +            f" -- Folder: {self.watchDirectory} -- Every: {self.watchDelay}(sec) -- For Patterns: {self.exceptionPattern}"  | 
 | 122 | +        )  | 
 | 123 | +        print(msg)  | 
 | 124 | +        self.observer.start()  | 
 | 125 | + | 
 | 126 | +    def run(self):  | 
 | 127 | +        print("Observer is running:", self.observer.name)  | 
 | 128 | +        self.start()  | 
 | 129 | +        try:  | 
 | 130 | +            while True:  | 
 | 131 | +                time.sleep(self.watchDelay)  | 
 | 132 | + | 
 | 133 | +                if self.stop_signal == 1:  | 
 | 134 | +                    print(  | 
 | 135 | +                        f"Observer stopped: {self.observer.name}  stop signal:{self.stop_signal}")  | 
 | 136 | +                    self.stop()  | 
 | 137 | +                    break  | 
 | 138 | +        except:  | 
 | 139 | +            self.stop()  | 
 | 140 | +        self.observer.join()  | 
 | 141 | + | 
 | 142 | +    def stop(self):  | 
 | 143 | +        print("Observer Stopped:", self.observer.name)  | 
 | 144 | + | 
 | 145 | +        now = (datetime.datetime.now()).strftime("%Y-%m-%d %H:%M:%S")  | 
 | 146 | +        msg = f"Observer: {self.observer.name} - Stopped On: {now}"  | 
 | 147 | +        print(msg)  | 
 | 148 | +        self.observer.stop()  | 
 | 149 | +        self.observer.join()  | 
 | 150 | + | 
 | 151 | +    def info(self):  | 
 | 152 | +        info = {  | 
 | 153 | +            'observerName': self.observer.name,  | 
 | 154 | +            'watchDirectory': self.watchDirectory,  | 
 | 155 | +            'watchDelay': self.watchDelay,  | 
 | 156 | +            'watchRecursively': self.watchRecursively,  | 
 | 157 | +            'watchPattern': self.watchPattern,  | 
 | 158 | +        }  | 
 | 159 | +        return info  | 
 | 160 | + | 
 | 161 | + | 
 | 162 | +def is_dir_path(path):  | 
 | 163 | +    """Utility function to check whether a path is an actual directory"""  | 
 | 164 | +    if os.path.isdir(path):  | 
 | 165 | +        return path  | 
 | 166 | +    else:  | 
 | 167 | +        raise NotADirectoryError(path)  | 
 | 168 | + | 
 | 169 | + | 
 | 170 | +if __name__ == "__main__":  | 
 | 171 | +    import argparse  | 
 | 172 | +    parser = argparse.ArgumentParser(  | 
 | 173 | +        description="Watchdog script for watching for files & directories' changes")  | 
 | 174 | +    parser.add_argument("path",  | 
 | 175 | +                        default=config.WATCH_DIRECTORY,  | 
 | 176 | +                        type=is_dir_path,  | 
 | 177 | +                        )  | 
 | 178 | +    parser.add_argument("-d", "--watch-delay",  | 
 | 179 | +                        help=f"Watch delay, default is {config.WATCH_DELAY}",  | 
 | 180 | +                        default=config.WATCH_DELAY,  | 
 | 181 | +                        type=int,  | 
 | 182 | +                        )  | 
 | 183 | +    parser.add_argument("-r", "--recursive",  | 
 | 184 | +                        action="store_true",  | 
 | 185 | +                        help=f"Whether to recursively watch for the path's children, default is {config.WATCH_RECURSIVELY}",  | 
 | 186 | +                        default=config.WATCH_RECURSIVELY,  | 
 | 187 | +                        )  | 
 | 188 | +    parser.add_argument("-p", "--pattern",  | 
 | 189 | +                        help=f"Pattern of files to watch, default is {config.WATCH_PATTERN}",  | 
 | 190 | +                        default=config.WATCH_PATTERN,  | 
 | 191 | +                        )  | 
 | 192 | +    parser.add_argument("--watch-directories",  | 
 | 193 | +                        action="store_true",  | 
 | 194 | +                        help=f"Whether to watch directories, default is {config.DO_WATCH_DIRECTORIES}",  | 
 | 195 | +                        default=config.DO_WATCH_DIRECTORIES,  | 
 | 196 | +                        )  | 
 | 197 | +    # parse the arguments  | 
 | 198 | +    args = parser.parse_args()  | 
 | 199 | +    # define & launch the log watcher  | 
 | 200 | +    log_watcher = LogWatcher(  | 
 | 201 | +        watchDirectory=args.path,  | 
 | 202 | +        watchDelay=args.watch_delay,  | 
 | 203 | +        watchRecursively=args.recursive,  | 
 | 204 | +        watchPattern=tuple(args.pattern.split(",")),  | 
 | 205 | +        doWatchDirectories=args.watch_directories,  | 
 | 206 | +        exceptionPattern=config.EXCEPTION_PATTERN,  | 
 | 207 | +    )  | 
 | 208 | +    log_watcher.run()  | 
0 commit comments