Skip to content

Commit

Permalink
fix(logging-filter): add some changes for discussion
Browse files Browse the repository at this point in the history
  • Loading branch information
open-dynaMIX committed Dec 26, 2021
1 parent 2164442 commit 2de822d
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 43 deletions.
72 changes: 46 additions & 26 deletions anonip.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,46 +304,66 @@ def truncate_address(self, ip):
return ip.supernet(new_prefix=self._prefixes[ip.version])[0]


class AnonipFilter:
def __init__(self, args=None, extra=None, anonip=None):
class AnonipFilter(logging.Filter):
def __init__(self, name="", args=None, extra=None, anonip=None):
"""
An implementation of Python logging.Filter using anonip.
:param name: str
:param args: list of log message args to filter. Defaults to []
:param extra: list of LogRecord attributes to filter. Defaults to []
:param anonip: dict of parameters for Anonip instance
"""
super(AnonipFilter, self).__init__(name)
self.args = [] if args is None else args
self.extra = [] if extra is None else extra
self.anonip = Anonip(**(anonip or {}))

def _set_args_attr(self, args, key):
value = args[key]
if not isinstance(value, str):
return args

orig_type = type(args)
temp_type = list
if isinstance(args, abc.Mapping):
temp_type = dict
has_setitem = hasattr(args, "__setitem__")
if not has_setitem:
args = temp_type(args)
ip = self.anonip.extract_ip(value)[1]
args[key] = str(self.anonip.process_ip(ip))
if not has_setitem:
args = orig_type(args)
return args

def filter(self, record):
"""
See logging.Filter.filter()
Apply anonip IP masking.
:param record: logging.LogRecord
:return: bool
"""
if record.name != "anonip":
for key in self.args:
if isinstance(record.args, abc.Mapping):
if key in record.args:
value = record.args[key]
if isinstance(value, str):
record.args[key] = self.anonip.process_line(value)
elif isinstance(record.args, abc.Sequence):
if key < len(record.args):
value = record.args[key]
if isinstance(value, str):
is_tuple = isinstance(record.args, tuple)
if is_tuple:
record.args = list(record.args)
record.args[key] = self.anonip.process_line(value)
if is_tuple:
record.args = tuple(record.args)

for key in self.extra:
if hasattr(record, key):
value = getattr(record, key)
if (isinstance(value, str)):
setattr(record, key, self.anonip.process_line(value))
if not super(AnonipFilter, self).filter(record):
return False

for key in self.args:
if isinstance(record.args, abc.Mapping):
if key in record.args:
record.args = self._set_args_attr(record.args, key)
elif isinstance(record.args, abc.Sequence):
if isinstance(key, int) and key < len(record.args):
record.args = self._set_args_attr(record.args, key)

for key in self.extra:
if hasattr(record, key):
value = getattr(record, key)
if isinstance(value, str):
ip = self.anonip.extract_ip(value)[1]
setattr(record, key, str(self.anonip.process_ip(ip)))

# IP is not in args or extra, but in msg
record.msg = self.anonip.process_line(record.msg)

return True

Expand Down
9 changes: 9 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import sys

import pytest
Expand All @@ -8,3 +9,11 @@ def backup_and_restore_sys_argv():
old_sys_argv = sys.argv
yield
sys.argv = old_sys_argv


@pytest.fixture()
def enable_logging():
logging.disable(logging.NOTSET)
logging.getLogger("anonip").setLevel(logging.CRITICAL)
yield
logging.disable(logging.CRITICAL)
51 changes: 34 additions & 17 deletions tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,27 +432,44 @@ def test_logging_filter_defaults(caplog):
logging.disable(logging.CRITICAL)


def test_logging_filter_args(caplog):
logging.disable(logging.NOTSET)
logging.getLogger("anonip").setLevel(logging.CRITICAL)

@pytest.mark.parametrize(
"string,args,filter_attr,expected",
[
("%(ip)s string", {"ip": "192.168.100.200"}, "ip", "192.168.96.0 string"),
("%s string", "192.168.100.200", 0, "192.168.96.0 string"),
(
"%(ip)s string",
{"ip": "2001:0db8:85a3:0000:0000:8a2e:0370:7334"},
"ip",
"2001:db8:85a0:: string",
),
("string", None, "ip", "string"),
("string", {"ip": ["in a list"]}, "ip", "string"),
("", "", "", ""), # make base logging filter return False for coverage
],
)
def test_logging_filter_args(
string, args, filter_attr, expected, mocker, caplog, enable_logging
):
logger = logging.getLogger("filter_args")
logger.addFilter(anonip.AnonipFilter(args=["ip", "non-existing-attr"], extra=[]))
logger.addFilter(
anonip.AnonipFilter(args=[filter_attr, "non-existing-attr"], extra=[])
)
logger.setLevel(logging.INFO)

logger.info("%(ip)s string", {"ip": "192.168.100.200"})
logger.info("string %(ip)s", {"ip": "1.2.3.4"})
logger.info("%(ip)s string", {"ip": "2001:0db8:85a3:0000:0000:8a2e:0370:7334"})
logger.info("string")

assert caplog.record_tuples == [
("filter_args", logging.INFO, "192.168.96.0 string"),
("filter_args", logging.INFO, "string 1.2.0.0"),
("filter_args", logging.INFO, "2001:db8:85a0:: string"),
("filter_args", logging.INFO, "string"),
]
log_args = [string]
if args:
log_args.append(args)

logging.disable(logging.CRITICAL)
if string == args == filter_attr == expected == "":
mocker.patch.object(logging.Filter, "filter", return_value=False)
logger.info(*log_args)
assert len(caplog.record_tuples) == 0
else:
logger.info(*log_args)
assert caplog.record_tuples == [
("filter_args", logging.INFO, expected),
]


def test_logging_filter_extra(caplog):
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ envlist = py{27,36,37,38,39,310}, pypy3, flake8, black
deps=
pytest
pytest-cov
pytest-mock
commands=pytest -r a -vv tests.py anonip.py

[testenv:flake8]
Expand Down

0 comments on commit 2de822d

Please sign in to comment.