17             with open(filename, "r") as f:
 
  18                 logging.info("Re-attached to log file.")
 
  23                         if not os.path.exists(filename):
 
  29                         yield line.rstrip("\n")
 
  30         except FileNotFoundError:
 
  34 def feed_handler(data):
 
  36         handler = subprocess.Popen(shlex.split(args.handler),
 
  37                                    stdin=subprocess.PIPE,
 
  38                                    stdout=subprocess.PIPE,
 
  39                                    stderr=subprocess.PIPE,
 
  41         out_data, err_data = handler.communicate("%s\n" % data)
 
  42         if handler.returncode != 0:
 
  43             logging.warning("Handler exited with non-zero return code %d! (%s)" %
 
  44                             (handler.returncode, err_data))
 
  45     except Exception as e:
 
  46         logging.error("Error feeding handler: %s" % str(e))
 
  48 def create_msg(title, icon, logfile, text, lines):
 
  49     msg = "<b>%s</b> <i>%s</i> %s" % (title, logfile, icon)
 
  50     msg += "<br>%s" % text
 
  52     for line in lines: msg += line + "\n"
 
  56 logging.basicConfig(format='[%(asctime)s] %(levelname)s: %(message)s',
 
  58                     datefmt='%m/%d/%Y %H:%M:%S')
 
  60 parser = argparse.ArgumentParser(description='Alert on excessive number of error log lines.')
 
  61 parser.add_argument('logfile', type=str, help='logfile to be watched')
 
  62 parser.add_argument('handler', type=str,
 
  63                     help='alert will be delivered to standard input of handler')
 
  64 parser.add_argument('-s', '--interval-size', type=int, default=600, dest='interval_size',
 
  65                     help='sample interval size in seconds (default: 600)')
 
  66 parser.add_argument('-n', '--num-intervals', type=int, default=6, dest='num_intervals',
 
  67                     help='number of intervals to keep in history (default: 6)')
 
  69 args = parser.parse_args()
 
  76 for line in follow(args.logfile):
 
  77     now = int(time.time()) // args.interval_size
 
  80         if not last_time or now > last_time:
 
  81             kept_times.append(now)
 
  84         if len(lines) > MAX_LINES:
 
  87     while len(kept_times) > 0 and \
 
  88           kept_times[0] <= now - args.num_intervals:
 
  91     intervals = [False] * args.num_intervals
 
  92     for kept_time in kept_times:
 
  93         intervals[now - kept_time] = True
 
  95     logging.debug(intervals)
 
  97     if not False in intervals:
 
  99             logging.warning("Entering error state!")
 
 101             feed_handler( create_msg("Log Alert",
 
 104                                      "Number of errors exceeded!",
 
 108             logging.info("Leaving error state.")
 
 110             feed_handler( create_msg("Log Un-Alert",
 
 113                                      "Log is back to normal.",