Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions acai_aws/common/logger/common_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def __init__(self):

@classmethod
def register_callback(cls, callback):
if callback in cls._callbacks:
return
cls._callbacks.append(callback)

@classmethod
Expand Down
15 changes: 14 additions & 1 deletion acai_aws/common/logger/redaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,22 @@ class RedactionFilter:

def __init__(self, **kwargs):
self._keys = {str(key).lower() for key in kwargs.get('keys') or []}
self._patterns = [re.compile(pattern) for pattern in kwargs.get('patterns') or []]
self._pattern_sources = tuple(kwargs.get('patterns') or [])
self._patterns = [re.compile(pattern) for pattern in self._pattern_sources]
self._redact_with = kwargs.get('redact_with', self.DEFAULT_REDACTION)

def __eq__(self, other):
if not isinstance(other, RedactionFilter):
return NotImplemented
return (
self._keys == other._keys
and self._pattern_sources == other._pattern_sources
and self._redact_with == other._redact_with
)

def __hash__(self):
return hash((frozenset(self._keys), self._pattern_sources, self._redact_with))

def __call__(self, record):
if isinstance(record, dict) and 'log' in record:
record['log'] = self._scrub(record['log'])
Expand Down
10 changes: 10 additions & 0 deletions tests/acai_aws/common/test_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,5 +212,15 @@ def test_reset_callbacks_clears_registry(self):
CommonLogger.reset_callbacks()
self.assertEqual(0, len(CommonLogger._callbacks))

def test_register_callback_dedupes_equal_filters(self):
CommonLogger.register_callback(RedactionFilter(keys=['email']))
CommonLogger.register_callback(RedactionFilter(keys=['email']))
self.assertEqual(1, len(CommonLogger._callbacks))

def test_register_callback_keeps_distinct_filters(self):
CommonLogger.register_callback(RedactionFilter(keys=['email']))
CommonLogger.register_callback(RedactionFilter(keys=['phone']))
self.assertEqual(2, len(CommonLogger._callbacks))

def test_common_logger_is_singleton(self):
self.assertIs(CommonLogger(), CommonLogger())
15 changes: 15 additions & 0 deletions tests/acai_aws/common/test_redaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,18 @@ def test_keys_and_patterns_together(self):
self.assertEqual('[REDACTED]', log['email'])
self.assertNotIn('123-45-6789', log['message'])
self.assertNotIn('ada@example.com', log['message'])

def test_equal_filters_compare_equal_and_hash_equal(self):
one = RedactionFilter(keys=['email', 'ssn'], patterns=[r'\d{3}'], redact_with='*')
two = RedactionFilter(keys=['ssn', 'email'], patterns=[r'\d{3}'], redact_with='*')
self.assertEqual(one, two)
self.assertEqual(hash(one), hash(two))

def test_filters_with_different_config_are_not_equal(self):
base = RedactionFilter(keys=['email'], patterns=[r'\d{3}'])
self.assertNotEqual(base, RedactionFilter(keys=['email'], patterns=[r'\d{4}']))
self.assertNotEqual(base, RedactionFilter(keys=['phone'], patterns=[r'\d{3}']))
self.assertNotEqual(base, RedactionFilter(keys=['email'], patterns=[r'\d{3}'], redact_with='*'))

def test_not_equal_to_non_filter(self):
self.assertNotEqual(RedactionFilter(keys=['email']), 'not-a-filter')