Skip to content

Commit

Permalink
Add colorized output to status
Browse files Browse the repository at this point in the history
Enable it by adding to config file:

    [supervisorctl]
    colorize_status = auto
  • Loading branch information
msabramo committed Mar 8, 2015
1 parent 20e47d8 commit 880bbfb
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
31 changes: 31 additions & 0 deletions supervisor/colors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
ESC = '\033['


class ansicolors:
class fg:
black = ESC + '30m'
red = ESC + '31m'
green = ESC + '32m'
yellow = ESC + '33m'
blue = ESC + '34m'
magenta = ESC + '35m'
cyan = ESC + '36m'
white = ESC + '37m'
reset = ESC + '39m'

class bg:
black = ESC + '40m'
red = ESC + '41m'
green = ESC + '42m'
yellow = ESC + '43m'
blue = ESC + '44m'
magenta = ESC + '45m'
cyan = ESC + '46m'
white = ESC + '47m'
reset = ESC + '49m'

class style:
bright = ESC + '1m'
dim = ESC + '2m'
normal = ESC + '22m'
reset_all = ESC + '0m'
5 changes: 5 additions & 0 deletions supervisor/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1557,6 +1557,7 @@ class ClientOptions(Options):
username = None
password = None
history_file = None
colorize_status = False

def __init__(self):
Options.__init__(self, require_configfile=False)
Expand All @@ -1568,6 +1569,7 @@ def __init__(self):
self.configroot.supervisorctl.username = None
self.configroot.supervisorctl.password = None
self.configroot.supervisorctl.history_file = None
self.configroot.supervisorctl.colorize_status = False

from supervisor.supervisorctl import DefaultControllerPlugin
default_factory = ('default', DefaultControllerPlugin, {})
Expand All @@ -1578,6 +1580,8 @@ def __init__(self):
self.add("interactive", "supervisorctl.interactive", "i",
"interactive", flag=1, default=0)
self.add("prompt", "supervisorctl.prompt", default="supervisor")
self.add("colorize_status", "supervisorctl.colorize_status",
default=False)
self.add("serverurl", "supervisorctl.serverurl", "s:", "serverurl=",
url, default="http://localhost:9001")
self.add("username", "supervisorctl.username", "u:", "username=")
Expand Down Expand Up @@ -1627,6 +1631,7 @@ def read_config(self, fp):
# The defaults used below are really set in __init__ (since
# section==self.configroot.supervisorctl)
section.prompt = config.getdefault('prompt', section.prompt)
section.colorize_status = config.getdefault('colorize_status', section.colorize_status)
section.username = config.getdefault('username', section.username)
section.password = config.getdefault('password', section.password)
history_file = config.getdefault('history_file', section.history_file)
Expand Down
50 changes: 45 additions & 5 deletions supervisor/supervisorctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,14 @@
import errno
import threading

from supervisor.colors import ansicolors
from supervisor.compat import xmlrpclib
from supervisor.compat import urlparse
from supervisor.compat import unicode
from supervisor.compat import raw_input

from supervisor.datatypes import boolean

from supervisor.medusa import asyncore_25 as asyncore

from supervisor.options import ClientOptions
Expand Down Expand Up @@ -592,28 +595,65 @@ def help_quit(self):
def help_exit(self):
self.ctl.output("exit\tExit the supervisor shell.")

def _show_statuses(self, process_infos):
def _show_statuses(self, process_infos, colorize_output=True):
namespecs, maxlen = [], 30
for i, info in enumerate(process_infos):
namespecs.append(make_namespec(info['group'], info['name']))
if len(namespecs[i]) > maxlen:
maxlen = len(namespecs[i])

template = '%(namespec)-' + str(maxlen+3) + 's%(state)-10s%(desc)s'
template = self._get_status_template(colorize_output, maxlen)

for i, info in enumerate(process_infos):
state = info['statename']

if colorize_output:
state = self._colorize_state(state)

line = template % {'namespec': namespecs[i],
'state': info['statename'],
'state': state,
'desc': info['description']}
self.ctl.output(line)

def _get_status_template(self, colorize_output, maxlen):
state_col_width = 24 if colorize_output else 10
template = ('%(namespec)-' + str(maxlen+3) + 's' +
'%(state)-' + str(state_col_width) +
's%(desc)s')
return template

def _colorize_state(self, statename):
colors = {
'RUNNING': ansicolors.style.bright + ansicolors.fg.green,
'STOPPED': ansicolors.style.bright + ansicolors.fg.yellow,
'BACKOFF': ansicolors.style.bright + ansicolors.fg.red,
'FATAL': ansicolors.style.bright + ansicolors.fg.red,
}
color = colors.get(statename, '')
return '%s%s%s' % (color, statename, ansicolors.fg.reset)

def do_status(self, arg):
if not self.ctl.upcheck():
return

supervisor = self.ctl.get_supervisor()
all_infos = supervisor.getAllProcessInfo()
options = self.ctl.options
if options.colorize_status == 'auto':
colorize_output = sys.stdout.isatty()
else:
colorize_output = boolean(options.colorize_status)

names = arg.split()
args = arg.split()

options = [arg for arg in args if arg.startswith('--')]
for option in options:
if option.startswith('--color'):
colorize_output = True
elif option.startswith('--no-color'):
colorize_output = False

names = [arg for arg in args if not arg.startswith('--')]
if not names or "all" in names:
matching_infos = all_infos
else:
Expand All @@ -638,7 +678,7 @@ def do_status(self, arg):
else:
msg = "%s: ERROR (no such process)" % name
self.ctl.output(msg)
self._show_statuses(matching_infos)
self._show_statuses(matching_infos, colorize_output)

def help_status(self):
self.ctl.output("status <name>\t\tGet status for a single process")
Expand Down
1 change: 1 addition & 0 deletions supervisor/tests/test_supervisorctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -1832,6 +1832,7 @@ def do_help(self, arg):
class DummyClientOptions:
def __init__(self):
self.prompt = 'supervisor'
self.colorize_status = False
self.serverurl = 'http://localhost:65532'
self.username = 'chrism'
self.password = '123'
Expand Down

0 comments on commit 880bbfb

Please sign in to comment.