11 ALERT_INTERVAL = 86400
16 fd = os.open(path, os.O_RDONLY)
17 current_ino = os.fstat(fd).st_ino
18 with os.fdopen(fd, "r") as f:
19 logging.info("Re-attached to log file.")
24 if os.stat(path).st_ino != current_ino or \
25 os.stat(path).st_size < f.tell():
31 yield line.rstrip("\n")
32 except FileNotFoundError:
36 def feed_handler(data):
38 handler = subprocess.Popen(shlex.split(args.handler),
39 stdin=subprocess.PIPE,
40 stdout=subprocess.PIPE,
41 stderr=subprocess.PIPE,
43 out_data, err_data = handler.communicate("%s\n" % data)
44 if handler.returncode != 0:
45 logging.warning("Handler exited with non-zero return code %d! (%s)" %
46 (handler.returncode, err_data))
47 except Exception as e:
48 logging.error("Error feeding handler: %s" % str(e))
50 def create_msg(title, icon, logfile, text, lines):
51 msg = "<b>%s</b> <i>%s</i> %s" % (title, logfile, icon)
52 msg += "<br>%s" % text
54 for line in lines: msg += line + "\n"
58 logging.basicConfig(format='[%(asctime)s] %(levelname)s: %(message)s',
60 datefmt='%m/%d/%Y %H:%M:%S')
62 parser = argparse.ArgumentParser(description='Alert on excessive number of error log lines.')
63 parser.add_argument('logfile', type=str, help='logfile to be watched')
64 parser.add_argument('handler', type=str,
65 help='alert will be delivered to standard input of handler')
66 parser.add_argument('-s', '--interval-size', type=int, default=600, dest='interval_size',
67 help='sample interval size in seconds (default: 600)')
68 parser.add_argument('-n', '--num-intervals', type=int, default=6, dest='num_intervals',
69 help='number of intervals to keep in history (default: 6)')
71 args = parser.parse_args()
79 for line in follow(args.logfile):
80 time_now = time.time()
81 slot_now = int(time_now) // args.interval_size
84 if not last_slot_time or slot_now > last_slot_time:
85 kept_times.append(slot_now)
86 last_slot_time = slot_now
88 if len(lines) > MAX_LINES:
91 while len(kept_times) > 0 and \
92 kept_times[0] <= slot_now - (args.num_intervals + 1):
95 intervals = [False] * (args.num_intervals + 1)
96 for kept_time in kept_times:
97 intervals[slot_now - kept_time] = True
99 logging.debug(intervals)
101 if not False in intervals[1:]:
102 if not error_state or time_now - last_alert_time > ALERT_INTERVAL:
103 last_alert_time = time_now
104 feed_handler( create_msg("Log Alert",
107 "Number of errors exceeded!",
110 logging.warning("Entering error state!")
115 logging.info("Leaving error state.")