|
| 1 | +# The Observer watches for any file change and then dispatches the respective events to an event handler. |
| 2 | +fromwatchdog.observersimportObserver |
| 3 | +# The event handler will be notified when an event occurs. |
| 4 | +fromwatchdog.eventsimportFileSystemEventHandler |
| 5 | +importtime |
| 6 | +importconfig |
| 7 | +importos |
| 8 | +fromcheckerimportFileChecker |
| 9 | +importdatetime |
| 10 | +fromcoloramaimportFore,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 | +defprint_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 | +classLogHandler(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 | +defon_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 | +ifnotevent.is_directory: |
| 48 | +# To cater for the on_move event |
| 49 | +path=event.src_path |
| 50 | +ifhasattr(event,'dest_path'): |
| 51 | +path=event.dest_path |
| 52 | +# Ensure that the file extension is among the pre-defined ones. |
| 53 | +ifpath.endswith(self.watchPattern): |
| 54 | +msg=f"{now} --{event.event_type} -- File:{path}" |
| 55 | +ifevent.event_typein ('modified','created','moved'): |
| 56 | +# check for exceptions in log files |
| 57 | +ifpath.endswith(config.LOG_FILES_EXTENSIONS): |
| 58 | +fortype,msginself.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 | +elifself.doWatchDirectories: |
| 67 | +msg=f"{now} --{event.event_type} -- Folder:{event.src_path}" |
| 68 | +print_with_color(msg,color=event2color[event.event_type]) |
| 69 | + |
| 70 | +defon_modified(self,event): |
| 71 | +pass |
| 72 | + |
| 73 | +defon_deleted(self,event): |
| 74 | +pass |
| 75 | + |
| 76 | +defon_created(self,event): |
| 77 | +pass |
| 78 | + |
| 79 | +defon_moved(self,event): |
| 80 | +pass |
| 81 | + |
| 82 | + |
| 83 | +classLogWatcher: |
| 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 | +defschedule(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 | +defstart(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'ifself.watchRecursivelyelse'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 | +defrun(self): |
| 127 | +print("Observer is running:",self.observer.name) |
| 128 | +self.start() |
| 129 | +try: |
| 130 | +whileTrue: |
| 131 | +time.sleep(self.watchDelay) |
| 132 | + |
| 133 | +ifself.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 | +defstop(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 | +definfo(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 | +returninfo |
| 160 | + |
| 161 | + |
| 162 | +defis_dir_path(path): |
| 163 | +"""Utility function to check whether a path is an actual directory""" |
| 164 | +ifos.path.isdir(path): |
| 165 | +returnpath |
| 166 | +else: |
| 167 | +raiseNotADirectoryError(path) |
| 168 | + |
| 169 | + |
| 170 | +if__name__=="__main__": |
| 171 | +importargparse |
| 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() |