summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2017-11-17 08:51:44 +0000
committerGerrit Code Review <review@openstack.org>2017-11-17 08:51:44 +0000
commit1608ad457529ea5b2866de1b6a704872cd8fe078 (patch)
tree0042000c281b0a30ee8a2e56369e7953dcf402fd
parent48d0294d5609be87a65cbded9dcaff399f396744 (diff)
parent1b012d0fc6811f00e032e52ed586fe37e157584d (diff)
downloadoslo-log-1608ad457529ea5b2866de1b6a704872cd8fe078.tar.gz
Merge "Capture context in its own key for JSON-based formatters"3.34.0
-rw-r--r--oslo_log/formatters.py32
-rw-r--r--oslo_log/tests/unit/test_log.py29
-rw-r--r--releasenotes/notes/add-context-section-0b2f411ec64f42f6.yaml6
3 files changed, 53 insertions, 14 deletions
diff --git a/oslo_log/formatters.py b/oslo_log/formatters.py
index ae2d04d..7fd5d42 100644
--- a/oslo_log/formatters.py
+++ b/oslo_log/formatters.py
@@ -221,12 +221,18 @@ class JSONFormatter(logging.Formatter):
for key in getattr(record, 'extra_keys', []):
if key not in extra:
extra[key] = getattr(record, key)
- # If we saved a context object, explode it into the extra
- # dictionary because the values are more useful than the
+ # The context object might have been given from the logging call. if
+ # that was the case, it'll come in the 'extra' entry already. If not,
+ # lets use the context we fetched above. In either case, we explode it
+ # into the 'context' entry because the values are more useful than the
# object reference.
- if 'context' in extra:
- extra.update(_dictify_context(context))
- del extra['context']
+ if 'context' in extra and extra['context']:
+ message['context'] = _dictify_context(extra['context'])
+ elif context:
+ message['context'] = _dictify_context(context)
+ else:
+ message['context'] = {}
+ extra.pop('context', None)
message['extra'] = extra
if record.exc_info:
@@ -290,12 +296,18 @@ class FluentFormatter(logging.Formatter):
for key in getattr(record, 'extra_keys', []):
if key not in extra:
extra[key] = getattr(record, key)
- # If we saved a context object, explode it into the extra
- # dictionary because the values are more useful than the
+ # The context object might have been given from the logging call. if
+ # that was the case, it'll come in the 'extra' entry already. If not,
+ # lets use the context we fetched above. In either case, we explode it
+ # into the extra dictionary because the values are more useful than the
# object reference.
- if 'context' in extra:
- extra.update(_dictify_context(context))
- del extra['context']
+ if 'context' in extra and extra['context']:
+ message['context'] = _dictify_context(extra['context'])
+ elif context:
+ message['context'] = _dictify_context(context)
+ else:
+ message['context'] = {}
+ extra.pop('context', None)
message['extra'] = extra
if record.exc_info:
diff --git a/oslo_log/tests/unit/test_log.py b/oslo_log/tests/unit/test_log.py
index 6d63b2d..a35cc17 100644
--- a/oslo_log/tests/unit/test_log.py
+++ b/oslo_log/tests/unit/test_log.py
@@ -415,18 +415,38 @@ class JSONFormatterTestCase(LogTestBase):
formatter=formatters.JSONFormatter)
self._set_log_level_with_cleanup(self.log, logging.DEBUG)
- def test_json(self):
+ def test_json_w_context_in_extras(self):
test_msg = 'This is a %(test)s line'
test_data = {'test': 'log'}
local_context = _fake_context()
self.log.debug(test_msg, test_data, key='value', context=local_context)
+ self._validate_json_data('test_json_w_context_in_extras', test_msg,
+ test_data, local_context)
+ def test_json_w_fetched_global_context(self):
+ test_msg = 'This is a %(test)s line'
+ test_data = {'test': 'log'}
+ local_context = _fake_context()
+ # NOTE we're not passing the context explicitly here. But it'll add the
+ # context to the extras anyway since the call to fake_context adds the
+ # context to the thread. The context will be fetched with the
+ # _update_record_with_context call that's done in the formatter.
+ self.log.debug(test_msg, test_data, key='value')
+ self._validate_json_data('test_json_w_fetched_global_context',
+ test_msg, test_data, local_context)
+
+ def _validate_json_data(self, testname, test_msg, test_data, ctx):
data = jsonutils.loads(self.stream.getvalue())
self.assertTrue(data)
self.assertIn('extra', data)
+ self.assertIn('context', data)
extra = data['extra']
+ context = data['context']
+ self.assertNotIn('context', extra)
self.assertEqual('value', extra['key'])
- self.assertEqual(local_context.user, extra['user'])
+ self.assertEqual(ctx.user, context['user'])
+ self.assertEqual(ctx.user_name, context['user_name'])
+ self.assertEqual(ctx.project_name, context['project_name'])
self.assertEqual('test-json', data['name'])
self.assertEqual(test_msg % test_data, data['message'])
@@ -434,7 +454,7 @@ class JSONFormatterTestCase(LogTestBase):
self.assertEqual(test_data, data['args'])
self.assertEqual('test_log.py', data['filename'])
- self.assertEqual('test_json', data['funcname'])
+ self.assertEqual(testname, data['funcname'])
self.assertEqual('DEBUG', data['levelname'])
self.assertEqual(logging.DEBUG, data['levelno'])
@@ -563,8 +583,9 @@ class FluentFormatterTestCase(LogTestBase):
self.assertIn('lineno', data)
self.assertIn('extra', data)
extra = data['extra']
+ context = data['context']
self.assertEqual('value', extra['key'])
- self.assertEqual(local_context.user, extra['user'])
+ self.assertEqual(local_context.user, context['user'])
self.assertEqual('test-fluent', data['name'])
self.assertEqual(test_msg % test_data, data['message'])
diff --git a/releasenotes/notes/add-context-section-0b2f411ec64f42f6.yaml b/releasenotes/notes/add-context-section-0b2f411ec64f42f6.yaml
new file mode 100644
index 0000000..200463c
--- /dev/null
+++ b/releasenotes/notes/add-context-section-0b2f411ec64f42f6.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - |
+ The JSON based formatters (namely JSONFormatter and FluentFormatter) now
+ output an extra section called 'context' that contains the context-related
+ keys and values, e.g. user, project and domain.