From 674dab4012df69ce9966165b164e64689aede3e4 Mon Sep 17 00:00:00 2001 From: Daisuke Fujita Date: Fri, 28 Aug 2015 16:48:27 +0900 Subject: [PATCH] The user_identity format flexibility This patch-set add a new config string option for user_identity format. Default of "logging_context_format_string" format is followings, '%(asctime)s.%(msecs)03d %(process)d %(levelname)s ' '%(name)s [%(request_id)s %(user_identity)s] ' '%(instance)s%(message)s', which is defined in oslo_log/_option.py, or config file. And default of "%(user_identity)s" field format is followings, user_idt_format = '{user} {tenant} {domain} {user_domain} {p_domain}' which is defined in oslo_context/context.py So, adding new config string option for user_identity format, we are able to control the user_identity format. For example, add following line to config file. logging_user_identity_format = U:%(user)s T:%(tenant)s Change-Id: I249ef7a8d144a4d5a34c3f80113f96fa4730ce28 Implements: blueprint user-identity-format-flexibility --- oslo_log/_options.py | 5 ++++ oslo_log/formatters.py | 14 +++++++++++ oslo_log/tests/unit/test_log.py | 43 ++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/oslo_log/_options.py b/oslo_log/_options.py index 05338ba4..fd325830 100644 --- a/oslo_log/_options.py +++ b/oslo_log/_options.py @@ -132,6 +132,11 @@ log_opts = [ default='[instance: %(uuid)s] ', help='The format for an instance UUID that is passed with the ' 'log message.'), + cfg.StrOpt('logging_user_identity_format', + default='%(user)s %(tenant)s ' + '%(domain)s %(user_domain)s %(project_domain)s', + help='Format string for user_identity field of ' + 'the logging_context_format_string'), ] diff --git a/oslo_log/formatters.py b/oslo_log/formatters.py index 2ea4bf3b..40b424dc 100644 --- a/oslo_log/formatters.py +++ b/oslo_log/formatters.py @@ -61,6 +61,11 @@ def _update_record_with_context(record): return context +class _ReplaceFalseValue(dict): + def __getitem__(self, key): + return dict.get(self, key, None) or '-' + + class JSONFormatter(logging.Formatter): def __init__(self, fmt=None, datefmt=None): # NOTE(jkoelker) we ignore the fmt argument, but its still there @@ -216,6 +221,15 @@ class ContextFormatter(logging.Formatter): if key not in record.__dict__: record.__dict__[key] = '' + # Set the "user_identity" value of "logging_context_format_string" + # by using "logging_user_identity_format" and + # "to_dict()" of oslo.context. + if context: + record.user_identity = ( + self.conf.logging_user_identity_format % + _ReplaceFalseValue(context.__dict__) + ) + if record.__dict__.get('request_id'): fmt = self.conf.logging_context_format_string else: diff --git a/oslo_log/tests/unit/test_log.py b/oslo_log/tests/unit/test_log.py index 5159decd..fd9b5571 100644 --- a/oslo_log/tests/unit/test_log.py +++ b/oslo_log/tests/unit/test_log.py @@ -41,7 +41,14 @@ from oslo_log import log def _fake_context(): - return context.RequestContext(1, 1, overwrite=True) + ctxt = context.RequestContext(1, 1, overwrite=True) + ctxt.user = 'myuser' + ctxt.tenant = 'mytenant' + ctxt.domain = 'mydomain' + ctxt.project_domain = 'myprojectdomain' + ctxt.user_domain = 'myuserdomain' + + return ctxt class CommonLoggerTestsMixIn(object): @@ -422,6 +429,37 @@ class ContextFormatterTestCase(LogTestBase): message) self.assertEqual(expected, self.stream.getvalue()) + def test_user_identity_logging(self): + self.config(logging_context_format_string="HAS CONTEXT " + "[%(request_id)s " + "%(user_identity)s]: " + "%(message)s") + ctxt = _fake_context() + ctxt.request_id = u'99' + message = 'test' + self.log.info(message, context=ctxt) + expected = ("HAS CONTEXT [%s %s %s %s %s %s]: %s\n" % + (ctxt.request_id, ctxt.user, ctxt.tenant, ctxt.domain, + ctxt.user_domain, ctxt.project_domain, + six.text_type(message))) + self.assertEqual(expected, self.stream.getvalue()) + + def test_user_identity_logging_set_format(self): + self.config(logging_context_format_string="HAS CONTEXT " + "[%(request_id)s " + "%(user_identity)s]: " + "%(message)s", + logging_user_identity_format="%(user)s " + "%(tenant)s") + ctxt = _fake_context() + ctxt.request_id = u'99' + message = 'test' + self.log.info(message, context=ctxt) + expected = ("HAS CONTEXT [%s %s %s]: %s\n" % + (ctxt.request_id, ctxt.user, ctxt.tenant, + six.text_type(message))) + self.assertEqual(expected, self.stream.getvalue()) + class ExceptionLoggingTestCase(LogTestBase): """Test that Exceptions are logged.""" @@ -592,9 +630,6 @@ class DomainTestCase(LogTestBase): def test_domain_in_log_msg(self): ctxt = _fake_context() - ctxt.domain = 'mydomain' - ctxt.project_domain = 'myprojectdomain' - ctxt.user_domain = 'myuserdomain' user_identity = ctxt.to_dict()['user_identity'] self.assertTrue(ctxt.domain in user_identity) self.assertTrue(ctxt.project_domain in user_identity)