Add a Python context that logs exceptions
The Python context named `ExceptionLogger` can be used to route exceptions to a specified logging service. Change-Id: I85d74339ac8857e7d577302d29c5951b76a1d127
This commit is contained in:
parent
216f443906
commit
f24a904dd3
@ -98,14 +98,10 @@ class FlavorsGenerator(base.Context):
|
|||||||
"""Delete created flavors."""
|
"""Delete created flavors."""
|
||||||
clients = osclients.Clients(self.context["admin"]["endpoint"])
|
clients = osclients.Clients(self.context["admin"]["endpoint"])
|
||||||
for flavor in self.context["flavors"].values():
|
for flavor in self.context["flavors"].values():
|
||||||
try:
|
with logging.ExceptionLogger(
|
||||||
|
LOG, _("Can't delete flavor %s") % flavor["id"]):
|
||||||
rutils.retry(3, clients.nova().flavors.delete, flavor["id"])
|
rutils.retry(3, clients.nova().flavors.delete, flavor["id"])
|
||||||
LOG.debug("Flavor is deleted %s" % flavor["id"])
|
LOG.debug("Flavor is deleted %s" % flavor["id"])
|
||||||
except Exception as e:
|
|
||||||
LOG.error(
|
|
||||||
"Can't delete flavor %s: %s" % (flavor["id"], e.message))
|
|
||||||
if logging.is_debug():
|
|
||||||
LOG.exception(e)
|
|
||||||
|
|
||||||
|
|
||||||
class FlavorConfig(dict):
|
class FlavorConfig(dict):
|
||||||
|
@ -71,8 +71,8 @@ class Network(base.Context):
|
|||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
for tenant_id, tenant_ctx in six.iteritems(self.context["tenants"]):
|
for tenant_id, tenant_ctx in six.iteritems(self.context["tenants"]):
|
||||||
for network in tenant_ctx.get("networks", []):
|
for network in tenant_ctx.get("networks", []):
|
||||||
try:
|
with logging.ExceptionLogger(
|
||||||
|
LOG,
|
||||||
|
_("Failed to delete network for tenant %s")
|
||||||
|
% tenant_id):
|
||||||
self.net_wrapper.delete_network(network)
|
self.net_wrapper.delete_network(network)
|
||||||
except Exception as e:
|
|
||||||
LOG.error("Failed to delete network for tenant %s\n"
|
|
||||||
" reason: %s" % (tenant_id, e))
|
|
||||||
|
@ -74,13 +74,10 @@ class RoleGenerator(base.Context):
|
|||||||
client = osclients.Clients(admin_endpoint).keystone()
|
client = osclients.Clients(admin_endpoint).keystone()
|
||||||
|
|
||||||
for user in self.context["users"]:
|
for user in self.context["users"]:
|
||||||
try:
|
with logging.ExceptionLogger(
|
||||||
|
LOG, _("Failed to remove role: %s") % role["id"]):
|
||||||
client.roles.remove_user_role(
|
client.roles.remove_user_role(
|
||||||
user["id"], role["id"], tenant=user["tenant_id"])
|
user["id"], role["id"], tenant=user["tenant_id"])
|
||||||
except Exception as ex:
|
|
||||||
LOG.warning("Failed to remove role: %(role_id)s. "
|
|
||||||
"Exception: %(ex)s" %
|
|
||||||
{"role_id": role["id"], "ex": ex})
|
|
||||||
|
|
||||||
@rutils.log_task_wrapper(LOG.info, _("Enter context: `roles`"))
|
@rutils.log_task_wrapper(LOG.info, _("Enter context: `roles`"))
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
@ -108,9 +108,6 @@ class AllowSSH(base.Context):
|
|||||||
@utils.log_task_wrapper(LOG.info, _("Exit context: `allow_ssh`"))
|
@utils.log_task_wrapper(LOG.info, _("Exit context: `allow_ssh`"))
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
for secgroup in self.secgroup:
|
for secgroup in self.secgroup:
|
||||||
try:
|
with logging.ExceptionLogger(
|
||||||
|
LOG, _("Unable to delete secgroup: %s.") % secgroup.id):
|
||||||
secgroup.delete()
|
secgroup.delete()
|
||||||
except Exception as ex:
|
|
||||||
LOG.warning("Unable to delete secgroup: %(group_id)s. "
|
|
||||||
"Exception: %(ex)s" %
|
|
||||||
{"group_id": secgroup.id, "ex": ex})
|
|
||||||
|
@ -67,5 +67,40 @@ class RallyContextAdapter(oslogging.ContextAdapter):
|
|||||||
self.log(logging.RDEBUG, msg, *args, **kwargs)
|
self.log(logging.RDEBUG, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionLogger(object):
|
||||||
|
"""Context that intercepts and logs exceptions.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
...
|
||||||
|
|
||||||
|
def foobar():
|
||||||
|
with ExceptionLogger(LOG, "foobar warning") as e:
|
||||||
|
return house_of_raising_exception()
|
||||||
|
|
||||||
|
if e.exception:
|
||||||
|
raise e.exception # remove if not required
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, logger, warn=None):
|
||||||
|
self.logger = logger
|
||||||
|
self.warn = warn
|
||||||
|
self.exception = None
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, type_, value, traceback):
|
||||||
|
if value:
|
||||||
|
self.exception = value
|
||||||
|
|
||||||
|
if self.warn:
|
||||||
|
self.logger.warning(self.warn)
|
||||||
|
self.logger.debug(value)
|
||||||
|
if is_debug():
|
||||||
|
self.logger.exception(value)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def is_debug():
|
def is_debug():
|
||||||
return CONF.debug or CONF.rally_debug
|
return CONF.debug or CONF.rally_debug
|
||||||
|
@ -76,3 +76,27 @@ class LogRallyContaxtAdapter(test.TestCase):
|
|||||||
|
|
||||||
radapter.log.assert_called_once_with(mock_logging.RDEBUG,
|
radapter.log.assert_called_once_with(mock_logging.RDEBUG,
|
||||||
fake_msg)
|
fake_msg)
|
||||||
|
|
||||||
|
|
||||||
|
class ExceptionLoggerTestCase(test.TestCase):
|
||||||
|
|
||||||
|
@mock.patch("rally.common.log.is_debug")
|
||||||
|
def test_context(self, mock_is_debug):
|
||||||
|
# Prepare
|
||||||
|
mock_is_debug.return_value = True
|
||||||
|
|
||||||
|
logger = mock.MagicMock()
|
||||||
|
exception = Exception()
|
||||||
|
|
||||||
|
# Run
|
||||||
|
with log.ExceptionLogger(logger, "foo") as e:
|
||||||
|
raise exception
|
||||||
|
|
||||||
|
# Assertions
|
||||||
|
logger.warning.assert_called_once_with("foo")
|
||||||
|
|
||||||
|
logger.exception.assert_called_once_with(exception)
|
||||||
|
|
||||||
|
logger.debug.assert_called_once_with(exception)
|
||||||
|
|
||||||
|
self.assertEqual(e.exception, exception)
|
||||||
|
Loading…
Reference in New Issue
Block a user