[core] Add debugging capabilities

* If an exception is thrown, catch it and show a (somewhat) nice error
  message in the i3bar instead of the normal content
* Add a flag "-d" for debugging into a debug log. Currently, this only
  logs commandline calls as they occur and their return values, as well
  as exceptions.

fixes #58
This commit is contained in:
Tobias Witek 2017-04-02 08:31:23 +02:00
parent 11235d6883
commit 251f8d2e9f
5 changed files with 80 additions and 9 deletions

View file

@ -1,6 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
import os
import sys import sys
import logging
import bumblebee.theme import bumblebee.theme
import bumblebee.engine import bumblebee.engine
import bumblebee.config import bumblebee.config
@ -8,17 +10,42 @@ import bumblebee.output
import bumblebee.input import bumblebee.input
def main(): def main():
logging.basicConfig(
level=logging.DEBUG,
format="[%(asctime)s] %(levelname)-8s %(message)s",
filename="{}/debug.log".format(os.path.dirname(os.path.realpath(__file__)))
)
config = bumblebee.config.Config(sys.argv[1:]) config = bumblebee.config.Config(sys.argv[1:])
theme = bumblebee.theme.Theme(config.theme()) theme = bumblebee.theme.Theme(config.theme())
output = bumblebee.output.I3BarOutput(theme=theme) output = bumblebee.output.I3BarOutput(theme=theme)
inp = bumblebee.input.I3BarInput() inp = bumblebee.input.I3BarInput()
engine = None
try:
engine = bumblebee.engine.Engine( engine = bumblebee.engine.Engine(
config=config, config=config,
output=output, output=output,
inp=inp, inp=inp,
) )
engine.run() engine.run()
except BaseException as e:
logging.exception(e)
if output.started():
output.flush()
output.end()
else:
output.start()
import time
while True:
output.begin()
error = bumblebee.modules.error.Module(engine, config)
error.set("exception occurred: {}".format(e))
widget = error.widgets()[0]
widget.link_module(error)
output.draw(widget, error)
output.flush()
output.end()
time.sleep(1)
# try: # try:
# except KeyboardInterrupt as error: # except KeyboardInterrupt as error:
# inp.stop() # inp.stop()

View file

@ -4,7 +4,9 @@ This module provides configuration information (loaded modules,
module parameters, etc.) to all other components module parameters, etc.) to all other components
""" """
import os
import sys import sys
import logging
import argparse import argparse
import textwrap import textwrap
import importlib import importlib
@ -14,6 +16,7 @@ MODULE_HELP = "Specify a space-separated list of modules to load. The order of t
THEME_HELP = "Specify the theme to use for drawing modules" THEME_HELP = "Specify the theme to use for drawing modules"
PARAMETER_HELP = "Provide configuration parameters in the form of <module>.<key>=<value>" PARAMETER_HELP = "Provide configuration parameters in the form of <module>.<key>=<value>"
LIST_HELP = "Display a list of either available themes or available modules along with their parameters." LIST_HELP = "Display a list of either available themes or available modules along with their parameters."
DEBUG_HELP = "Enable debug log ('debug.log' located in the same directory as the bumblebee-status executable)"
class print_usage(argparse.Action): class print_usage(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs): def __init__(self, option_strings, dest, nargs=None, **kwargs):
@ -51,6 +54,8 @@ def create_parser():
help=PARAMETER_HELP) help=PARAMETER_HELP)
parser.add_argument("-l", "--list", choices=["modules", "themes"], action=print_usage, parser.add_argument("-l", "--list", choices=["modules", "themes"], action=print_usage,
help=LIST_HELP) help=LIST_HELP)
parser.add_argument("-d", "--debug", action="store_true",
help=DEBUG_HELP)
return parser return parser
class Config(bumblebee.store.Store): class Config(bumblebee.store.Store):
@ -64,6 +69,9 @@ class Config(bumblebee.store.Store):
parser = create_parser() parser = create_parser()
self._args = parser.parse_args(args if args else []) self._args = parser.parse_args(args if args else [])
if not self._args.debug:
logger = logging.getLogger().disabled = True
for param in self._args.parameters: for param in self._args.parameters:
key, value = param.split("=") key, value = param.split("=")
self.set(key, value) self.set(key, value)

View file

@ -0,0 +1,26 @@
# pylint: disable=C0111,R0903
"""Draws an error widget.
"""
import bumblebee.input
import bumblebee.output
import bumblebee.engine
class Module(bumblebee.engine.Module):
def __init__(self, engine, config):
super(Module, self).__init__(engine, config,
bumblebee.output.Widget(full_text=self.text)
)
self._text = ""
def set(self, text):
self._text = text
def text(self, widget):
return self._text
def state(self, widget):
return ["critical"]
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -50,9 +50,14 @@ class I3BarOutput(object):
def __init__(self, theme): def __init__(self, theme):
self._theme = theme self._theme = theme
self._widgets = [] self._widgets = []
self._started = False
def started(self):
return self._started
def start(self): def start(self):
"""Print start preamble for i3bar protocol""" """Print start preamble for i3bar protocol"""
self._started = True
sys.stdout.write(json.dumps({"version": 1, "click_events": True}) + "[\n") sys.stdout.write(json.dumps({"version": 1, "click_events": True}) + "[\n")
def stop(self): def stop(self):

View file

@ -1,4 +1,5 @@
import shlex import shlex
import logging
import subprocess import subprocess
try: try:
@ -8,17 +9,21 @@ except ImportError:
pass pass
def execute(cmd, wait=True): def execute(cmd, wait=True):
logging.info("executing command '{}'".format(cmd))
args = shlex.split(cmd) args = shlex.split(cmd)
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
rv = None
if wait: if wait:
out, _ = proc.communicate() out, _ = proc.communicate()
if proc.returncode != 0: if proc.returncode != 0:
raise RuntimeError("{} exited with {}".format(cmd, proc.returncode)) raise RuntimeError("{} exited with {}".format(cmd, proc.returncode))
if type(out) == str: if type(out) == str:
return out rv = out
return out.decode("utf-8") else:
return None rv = out.decode("utf-8")
logging.info("command returned '{}'".format("" if not rv else rv))
return rv
def bytefmt(num): def bytefmt(num):
for unit in [ "", "Ki", "Mi", "Gi" ]: for unit in [ "", "Ki", "Mi", "Gi" ]: