Source

bmutils / bmtop.py

Full commit
#!/usr/bin/env python

import curses
import logging
import multiprocessing
import sys
import time
try:
    from collections import defaultdict, Counter
except:
    from bmutils.collections27 import defaultdict, Counter

from bmutils import logWorker


__author__ = "Peter Nixon"
__copyright__ = "Copyright (C) 2012 Peter Nixon"
__license__ = "Public Domain"
__version__ = "1.2"

DEBUG = False

host_counter = Counter()
status_counter = Counter()
#big = defaultdict(lambda: defaultdict(int))
# Setup a dict containing a nested Counter()
status_dict = defaultdict(lambda: Counter())

def init_display(stdscr):
    stdscr.clear()
    stdscr.refresh()
    size = stdscr.getmaxyx()
    rootwin = stdscr.derwin(size[0],size[1], 0, 0)
    return rootwin

def _main(stdscr, files, options):
    logQueue = multiprocessing.Queue()
    
    for filepath in files:
        p = multiprocessing.Process(target=logWorker, args=(filepath, logQueue, options.code, options.sleep))
        p.daemon = True
        p.start()

    try:
        try:
            # Setup an ncurses display
            rootwin = init_display(stdscr)
            size = rootwin.getmaxyx()
            data_len = int(size[0]) - 5
            height = size[0]
            width = int(size[1]) - 2
            refreshinterval = options.refresh
            nextrefresh = int(time.time()) + 2  # Collect data for 2 seconds before first screen refresh
            total_requests = 0
            bypassed_requests = 0
            malformed_lines = 0
            while (logQueue):
                log = logQueue.get()
                total_requests += 1
                stime = time.ctime(log.utime)
                if log.status == 0:
                    bypassed_requests += 1
                else:
                    host_counter[log.domainname] += 1
                    status_counter[log.status] +=1
                    status_dict[log.domainname][log.status] +=1
                if int(time.time()) > nextrefresh:
                    bypass_percent = float(bypassed_requests) / float(total_requests) * 100
                    percent_503 = float(status_counter[503]) / float(total_requests) * 100
                    percent_504 = float(status_counter[504]) / float(total_requests) * 100
                    rootwin.border(0)
                    rootwin.addstr(1, 1, "#  Hits      Site                          HTTP 503  HTTP 504")
                    rootwin.addstr(height - 2, 1, "Sites: %d  Req: %d  Bypass: %d (%.3f%%) Err 503: %d (%.3f%%) Err 504: %d (%.3f%%)" % (len(host_counter), total_requests, bypassed_requests, bypass_percent, status_counter[503], percent_503, status_counter[504], percent_504))
                    rootwin.hline(2, 1, curses.ACS_HLINE, width)
                    n = 1
                    y = 3
                    for (key, num) in host_counter.most_common(data_len):
                        rootwin.addstr(y, 1, str(n))
                        rootwin.addstr(y, 4, str(num))
                        rootwin.addstr(y, 14, str(key))
                        #for (key2, num2) in status_dict[key].most_common(1):
                        #    rootwin.addstr(y, 40, str(key2))
                        host_percent_503 = float(status_dict[key][503]) / float(total_requests) * 100
                        host_percent_504 = float(status_dict[key][504]) / float(total_requests) * 100
                        rootwin.addstr(y, 44, "%.3f%%" % (host_percent_503))
                        rootwin.addstr(y, 54, "%.3f%%" % (host_percent_504))
                        n += 1
                        y += 1
                    nextrefresh = int(time.time()) + refreshinterval
                    rootwin.refresh()
                    rootwin.clear()

        except KeyboardInterrupt:
            # Escape silently
            pass
    finally:
        pass
        
def main(stdscr):
    from optparse import OptionParser

    parser = OptionParser(usage='usage: %prog [options] filename')

    parser.add_option("-c", "--code", dest="code", default=None, type="int",
                      help='Filter output on HTTP Status code (default: None)')
                      
    parser.add_option('-r', '--refresh', dest='refresh', default=5, type='int',
                      help='screen refresh interval')

    parser.add_option('-s', '--sleep-interval', dest='sleep', default=2.0, metavar='S', type='float',
                      help='with  -f,  sleep  for  approximately  S  seconds between file iterations')

    parser.add_option('', '--test', dest='test', default=False, action='store_true',
                      help='Run some basic tests')

    (options, args) = parser.parse_args()

    if options.test:
        _test()
    elif not len(args) > 0:
        parser.print_help()
        sys.exit(1)
    else:
        _main(stdscr, args, options)
        
if __name__ == '__main__':
    if DEBUG: multiprocessing.log_to_stderr(logging.DEBUG)
    curses.wrapper(main)