Source code for apache_manager.interactive
# Monitor and control Apache web server workers from Python.
#
# Author: Peter Odding <peter@peterodding.com>
# Last Change: September 26, 2015
# URL: https://apache-manager.readthedocs.io
"""
A ``top`` like interactive viewer for Apache web server metrics.
The :mod:`~apache_manager.interactive` module implements a ``top`` like
interactive viewer for Apache web server metrics using curses_. It can be
invoked from the command line using ``apache-manager --watch``.
Please note that the functions in this module are not included in the test
suite and are excluded from coverage calculations because:
1. For now this module is just an interesting experiment. It might disappear
completely or I might change it significantly, it all depends on time and
interest. For example it would be cool to have a tool like mytop_ or
innotop_ for Apache workers, but it's going to take time to build something
like that and I have 40+ open source projects and limited spare time, so I'm
not going to commit to anything :-).
2. This is my first time working with Python's :mod:`curses` module (and
curses_ interfaces in general) and it's not yet clear to me how feasible it
is to test an interactive command line interface that's not line based.
.. _curses: https://en.wikipedia.org/wiki/Curses_(programming_library)
.. _innotop: https://github.com/innotop/innotop
.. _mytop: http://jeremy.zawodny.com/mysql/mytop/
"""
# Standard library modules.
import curses
import logging
import time
# External dependencies.
import coloredlogs
[docs]def watch_metrics(manager):
"""Watch Apache web server metrics in a ``top`` like interface."""
try:
curses.wrapper(redraw_loop, manager)
except KeyboardInterrupt:
pass
[docs]def redraw_loop(screen, manager):
"""The main loop that continuously redraws Apache web server metrics."""
# Ugly workaround to avoid circular import errors due to interdependencies
# between the apache_manager.cli and apache_manager.interactive modules.
from apache_manager.cli import report_metrics, line_is_heading
# Hide warnings (they'll mess up the curses layout).
coloredlogs.set_level(logging.ERROR)
# Hide the text cursor.
cursor_mode = curses.curs_set(0)
# Make Control-C behave normally.
curses.noraw()
# Enable non-blocking getch().
screen.nodelay(True)
try:
# Repeat until the user aborts.
while True:
lnum = 0
for line in report_metrics(manager):
attributes = 0
if line_is_heading(line):
attributes |= curses.A_BOLD
screen.addstr(lnum, 0, line, attributes)
lnum += 1
# Redraw screen.
screen.refresh()
# Wait a while before refreshing the screen, but enable the user to
# quit in the mean time.
for i in range(10):
if screen.getch() == ord('q'):
return
# Don't burn through CPU like crazy :-).
time.sleep(0.1)
# Update metrics in next iteration.
manager.refresh()
# Clear screen for next iteration.
screen.erase()
finally:
# Restore cursor mode.
curses.curs_set(cursor_mode)
# Clean up the screen after we're done.
screen.erase()