diff options
-rw-r--r-- | oslo_log/formatters.py | 17 | ||||
-rw-r--r-- | oslo_log/tests/unit/test_log.py | 16 |
2 files changed, 32 insertions, 1 deletions
diff --git a/oslo_log/formatters.py b/oslo_log/formatters.py index 2545035..8386ccc 100644 --- a/oslo_log/formatters.py +++ b/oslo_log/formatters.py @@ -17,6 +17,7 @@ import itertools import logging import logging.config import logging.handlers +import re import socket import sys import traceback @@ -180,6 +181,9 @@ class _ReplaceFalseValue(dict): return dict.get(self, key, None) or '-' +_MSG_KEY_REGEX = re.compile('(%+)\((\w+)\)') + + class JSONFormatter(logging.Formatter): def __init__(self, fmt=None, datefmt=None, style='%'): # NOTE(sfinucan) we ignore the fmt and style arguments, but they're @@ -206,11 +210,22 @@ class JSONFormatter(logging.Formatter): return lines def format(self, record): + args = record.args + if isinstance(args, dict): + msg_keys = _MSG_KEY_REGEX.findall(record.msg) + # NOTE(bnemec): The logic around skipping escaped placeholders is + # tricky and error-prone to include in the regex. Much easier to + # just grab them all and filter after the fact. + msg_keys = [m[1] for m in msg_keys if len(m[0]) == 1] + # If no named keys were found, then the entire dict must have been + # the value to be formatted. Don't filter anything. + if msg_keys: + args = {k: v for k, v in args.items() if k in msg_keys} message = {'message': record.getMessage(), 'asctime': self.formatTime(record, self.datefmt), 'name': record.name, 'msg': record.msg, - 'args': record.args, + 'args': args, 'levelname': record.levelname, 'levelno': record.levelno, 'pathname': record.pathname, diff --git a/oslo_log/tests/unit/test_log.py b/oslo_log/tests/unit/test_log.py index 7878525..84950a4 100644 --- a/oslo_log/tests/unit/test_log.py +++ b/oslo_log/tests/unit/test_log.py @@ -591,6 +591,22 @@ class JSONFormatterTestCase(LogTestBase): # convert it using repr() to prevent serialization error on logging. self.assertEqual(['repr'], data['args']) + def test_extra_args_filtered(self): + test_msg = 'This is a %(test)s line %%(unused)' + test_data = {'test': 'log', 'unused': 'removeme'} + self.log.debug(test_msg, test_data) + + data = jsonutils.loads(self.stream.getvalue()) + self.assertNotIn('unused', data['args']) + + def test_entire_dict(self): + test_msg = 'This is a %s dict' + test_data = {'test': 'log', 'other': 'value'} + self.log.debug(test_msg, test_data) + + data = jsonutils.loads(self.stream.getvalue()) + self.assertEqual(test_data, data['args']) + def get_fake_datetime(retval): class FakeDateTime(datetime.datetime): |