diff --git a/outage_detector/main.py b/outage_detector/main.py index 19243bf..a0f05ae 100644 --- a/outage_detector/main.py +++ b/outage_detector/main.py @@ -13,55 +13,73 @@ class LogEntry: self.host = log_entry.split('[')[2].split(']')[0] self.failed = 'FAIL' in log_entry + def __repr__(self: 'LogEntry') -> str: + return f"[{self.date}][{self.host}][{'OK' if not self.failed else 'FAIL'}]" + def printOutages(filepath: str, time: int) -> None: ''' Print outages from a log file - + `filepath` Path to a file with connection logs. `time` Minimum duration for a connection loss to be considered an outage. ''' + # Get a sorted list of log entries. with open(filepath, 'r') as file: - log = list(map(lambda x: LogEntry(x), file.readlines())) + log = sorted( + map(lambda x: LogEntry(x), file.readlines()), + key=lambda x: x.date + ) - # cluster log entries by host + # Cluster log entries by host. log_by_host = {} + for entry in log: if entry.host not in log_by_host: log_by_host[entry.host] = [entry] else: log_by_host[entry.host].append(entry) - # cluster fails to outages - # we consider the time of subsequent failed connection attempts to the same host an outage + # Cluster fails to outages. + # We consider the time of subsequent failed connection attempts to the same host an outage. outages: list = [] last_failed = False for entries in log_by_host.values(): for entry in entries: + + # Continue an outage. if entry.failed and last_failed: outages[-1].append(entry) - if entry.failed and not last_failed: + + # Start a new outage. + elif entry.failed and not last_failed: outages.append([entry]) last_failed = True - if not entry.failed and last_failed: + + # End an outage. + elif not entry.failed and last_failed: last_failed = False - # print the outages by date of first fail - for outage in sorted(outages, key=lambda x: x[0].date): + # Print the outages. + for outage in outages: - # get duration of outage in hours and minutes + # Get duration of outage in hours and minutes. outage_duration = round( - (outage[-1].date - outage[0].date).seconds / 60) - # continue if outage is shorter than the minimum duration + (outage[-1].date - outage[0].date).seconds / 60 + ) + + # Skip printing if outage is shorter than the minimum duration. if outage_duration < time: continue + + # Get hours and minutes for printing. hours = outage_duration // 60 minutes = outage_duration - (hours * 60) host = outage[0].host - # Outputs outages in the form of "Outage at: 2023-19-01 06:29:01 lasting for 2 Hours and 39 Minutes" + # Outputs outages in the form of "Outage at: 2023-19-01 06:29:01 lasting for 2 Hours and 39 Minutes". print(f"[{outage[0].date}][{host}] lasting for {'{} Hours and '.format(hours) if hours >= 1 else ''}{minutes} Minutes") @@ -114,7 +132,7 @@ def parseArgs(args: list[str]) -> argparse.Namespace: subparsers = parser.add_subparsers(dest='command') - # Arguments for the log command + # Arguments for the log command. parser_log = subparsers.add_parser( 'log', help='Log the connection status.' ) @@ -131,7 +149,7 @@ def parseArgs(args: list[str]) -> argparse.Namespace: help='Set the timeout in seconds to use for the connection test. (default: %(default)s)' ) - # Arguments for the outages command + # Arguments for the outages command. parser_outages = subparsers.add_parser( 'outages', help='Print/log outages' )