diff --git a/logredactor/redacting_filter.py b/logredactor/redacting_filter.py index 08f1a2e..3fc2c36 100644 --- a/logredactor/redacting_filter.py +++ b/logredactor/redacting_filter.py @@ -4,34 +4,35 @@ class RedactingFilter(logging.Filter): # Do not try and redact the built in values. With the wrong regex it can break the logging - ignore_keys = [ + ignore_keys = { 'name', 'levelname', 'levelno', 'pathname', 'filename', 'module', 'exc_info', 'exc_text', 'stack_info', 'lineno', 'funcName', 'created', 'msecs', 'relativeCreated', 'thread', 'threadName', 'process', 'processName', 'args', - ] + } - def __init__(self, patterns, default_mask='****'): + def __init__(self, patterns, default_mask='****', mask_keys=None): super(RedactingFilter, self).__init__() self._patterns = patterns self._default_mask = str(default_mask) + self._mask_keys = set(mask_keys or {}) def filter(self, record): d = vars(record) for k, content in d.items(): if k not in self.ignore_keys: - d[k] = self.redact(content) + d[k] = self.redact(content, k) # Also clean any contents in args if isinstance(record.args, dict): for k in record.args.keys(): - record.args[k] = self.redact(record.args[k]) + record.args[k] = self.redact(record.args[k], k) else: record.args = tuple(self.redact(arg) for arg in record.args) return True - def redact(self, content): + def redact(self, content, key=None): if content: if isinstance(content, dict): for k, v in content.items(): @@ -40,6 +41,9 @@ def redact(self, content): elif isinstance(content, (list, tuple)): for i, v in enumerate(content): content[i] = self.redact(v) + + elif key and key in self._mask_keys: + content = self._default_mask else: content = isinstance(content, str) and content or str(content) diff --git a/tests/test_redacting_filter.py b/tests/test_redacting_filter.py index eecb36d..8226b5c 100644 --- a/tests/test_redacting_filter.py +++ b/tests/test_redacting_filter.py @@ -12,7 +12,8 @@ def get_logger(filters): logger.addFilter( logredactor.RedactingFilter( filters, - default_mask='****' + default_mask='****', + mask_keys={'phonenumber',} ) ) return logger @@ -38,6 +39,12 @@ def test_arg_dict(caplog, logger_setup): assert caplog.records[0].message == "foo ****" +def test_arg_dict_with_key_to_remove(caplog, logger_setup): + logger = logger_setup([re.compile(r'\d{3}')]) + logger.warning("foo %s", {'phonenumber': '123'}) + assert caplog.records[0].message == "foo {'phonenumber': '****'}" + + def test_extra_string_value(caplog, logger_setup): logger = logger_setup([re.compile(r'\d{3}')]) logger.warning("foo", extra={'bar': '123 too'}) @@ -92,3 +99,9 @@ def test_match_group(caplog, logger_setup): logger = logger_setup([re.compile(r'(?<=api_key=)[\w-]+')]) logger.warning("example.com?api_key=this-is-my-key&sort=price") assert caplog.records[0].message == "example.com?api_key=****&sort=price" + + +def test_extra_do_redact_specific_key(caplog, logger_setup): + logger = logger_setup([re.compile(r'\d{3}')]) + logger.warning("foo", extra={'phonenumber': 'foobar'}) + assert caplog.records[0].phonenumber == "****"