X-Git-Url: http://git.treefish.org/~alex/logalert.git/blobdiff_plain/9bd4e7752ed07286a8e6b43f92c2820192539a5c..6b34e849bedd62deb8ef4964c399f3e6ebd87bb7:/src/logalert.py?ds=sidebyside
diff --git a/src/logalert.py b/src/logalert.py
index df9de07..73e5660 100755
--- a/src/logalert.py
+++ b/src/logalert.py
@@ -3,61 +3,24 @@
import argparse
import logging
import os
-import shlex
-import subprocess
+import signal
import time
-MAX_LINES = 10
+import misc
+
+LINES_TO_KEEP = 10
ALERT_INTERVAL = 86400
-def follow(path):
- while True:
- try:
- fd = os.open(path, os.O_RDONLY)
- current_ino = os.fstat(fd).st_ino
- with os.fdopen(fd, "r") as f:
- logging.info("Re-attached to log file.")
- for line in f: pass
- while True:
- line = f.readline()
- if not line:
- if os.stat(path).st_ino != current_ino or \
- os.stat(path).st_size < f.tell():
- break
- else:
- time.sleep(1.0)
- yield None
- else:
- yield line.rstrip("\n")
- except FileNotFoundError:
- time.sleep(1.0)
- yield None
-
-def feed_handler(data):
- try:
- handler = subprocess.Popen(shlex.split(args.handler),
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- encoding='UTF-8')
- out_data, err_data = handler.communicate("%s\n" % data)
- if handler.returncode != 0:
- logging.warning("Handler exited with non-zero return code %d! (%s)" %
- (handler.returncode, err_data))
- except Exception as e:
- logging.error("Error feeding handler: %s" % str(e))
-
-def create_msg(title, icon, logfile, text, lines):
- msg = "%s %s %s" % (title, logfile, icon)
- msg += "
%s" % text
- msg += "
" - for line in lines: msg += line + "\n" - msg += "" - return msg +def handleInterrupt(sig, frame): + global stop + if os.getpid() == mainPid: + logging.info( "Got stop signal." ) + stop = True -logging.basicConfig(format='[%(asctime)s] %(levelname)s: %(message)s', - level=logging.INFO, - datefmt='%m/%d/%Y %H:%M:%S') +mainPid = os.getpid() +signal.signal(signal.SIGINT, handleInterrupt) + +stop = False parser = argparse.ArgumentParser(description='Alert on excessive number of error log lines.') parser.add_argument('logfile', type=str, help='logfile to be watched') @@ -67,45 +30,56 @@ parser.add_argument('-s', '--interval-size', type=int, default=600, dest='interv help='sample interval size in seconds (default: 600)') parser.add_argument('-n', '--num-intervals', type=int, default=6, dest='num_intervals', help='number of intervals to keep in history (default: 6)') +parser.add_argument('-l', '--log-level', type=str, default='INFO', dest='log_lvl', + choices=['DEBUG', 'INFO', 'WARNING'], help='select log level') args = parser.parse_args() -kept_times = [] +logging.basicConfig(format='[%(asctime)s] %(levelname)s: %(message)s', + level=logging.getLevelName(args.log_lvl), + datefmt='%m/%d/%Y %H:%M:%S') + +num_intervals = (args.num_intervals + 1) +intervals = [False] * num_intervals lines = [] -last_slot_time = None +last_time_slot = 0 +interval_ptr = 0 error_state = False last_alert_time = 0 -for line in follow(args.logfile): +for line in misc.follow_file(args.logfile): time_now = time.time() - slot_now = int(time_now) // args.interval_size + time_slot = int(time_now) // args.interval_size + time_slot_diff = time_slot - last_time_slot + last_time_slot = time_slot + + interval_ptr = (interval_ptr + time_slot_diff) % num_intervals + for i in range(0, min(num_intervals, time_slot_diff)): + intervals[(interval_ptr + i ) % num_intervals] = False - if line != None: - if not last_slot_time or slot_now > last_slot_time: - kept_times.append(slot_now) - last_slot_time = slot_now + if line: + intervals[interval_ptr] = True lines.append(line) - if len(lines) > MAX_LINES: + if len(lines) > LINES_TO_KEEP: lines.pop(0) - while len(kept_times) > 0 and \ - kept_times[0] <= slot_now - (args.num_intervals + 1): - kept_times.pop(0) + logging.debug( misc.print_list(intervals, interval_ptr) ) - intervals = [False] * (args.num_intervals + 1) - for kept_time in kept_times: - intervals[slot_now - kept_time] = True + intervals_error_state = True + for i in range(1, num_intervals): + if not intervals[(interval_ptr + i) % num_intervals]: + intervals_error_state = False + break - logging.debug(intervals) - - if not False in intervals[1:]: + if intervals_error_state: if not error_state or time_now - last_alert_time > ALERT_INTERVAL: last_alert_time = time_now - feed_handler( create_msg("Log Alert", - "☠", - args.logfile, - "Number of errors exceeded!", - lines) ) + misc.feed_handler( args.handler, + misc.create_msg("Log Alert", + "☠", + args.logfile, + "Number of errors exceeded!", + lines) ) if not error_state: logging.warning("Entering error state!") error_state = True @@ -114,3 +88,6 @@ for line in follow(args.logfile): if error_state: logging.info("Leaving error state.") error_state = False + + if stop: + break