Skip to content

Commit

Permalink
Make filtering loggers pickleable
Browse files Browse the repository at this point in the history
  • Loading branch information
hynek committed Feb 18, 2021
1 parent a9bed6f commit 334f029
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Changes:
`#290 <https://github.com/hynek/structlog/pull/290>`_
- Fix isolation in ``structlog.contextvars``.
`#302 <https://github.com/hynek/structlog/pull/302>`_
- The default configuration and loggers are pickleable again.
`#301 <https://github.com/hynek/structlog/pull/301>`_


----
Expand Down
30 changes: 30 additions & 0 deletions src/structlog/_log_levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,18 @@ def make_filtering_bound_logger(min_level: int) -> Type[FilteringBoundLogger]:
values.
.. versionadded:: 20.2.0
.. versionchanged:: 21.1.0 The returned loggers are now pickleable.
"""

return _LEVEL_TO_FILTERING_LOGGER[min_level]


def _make_filtering_bound_logger(min_level: int) -> Type[FilteringBoundLogger]:
"""
Create a new `FilteringBoundLogger` that only logs *min_level* or higher.
The logger is optimized such that log levels below *min_level* only consist
of a ``return None``.
"""

def make_method(level: int) -> Callable[..., Any]:
Expand Down Expand Up @@ -124,3 +136,21 @@ def meth(self: Any, event: str, **kw: Any) -> Any:
(BoundLoggerBase,),
meths,
)


# Pre-create all possible filters to make them pickleable.
BoundLoggerFilteringAtNotset = _make_filtering_bound_logger(NOTSET)
BoundLoggerFilteringAtDebug = _make_filtering_bound_logger(DEBUG)
BoundLoggerFilteringAtInfo = _make_filtering_bound_logger(INFO)
BoundLoggerFilteringAtWarning = _make_filtering_bound_logger(WARNING)
BoundLoggerFilteringAtError = _make_filtering_bound_logger(ERROR)
BoundLoggerFilteringAtCritical = _make_filtering_bound_logger(CRITICAL)

_LEVEL_TO_FILTERING_LOGGER = {
CRITICAL: BoundLoggerFilteringAtCritical,
ERROR: BoundLoggerFilteringAtError,
WARNING: BoundLoggerFilteringAtWarning,
INFO: BoundLoggerFilteringAtInfo,
DEBUG: BoundLoggerFilteringAtDebug,
NOTSET: BoundLoggerFilteringAtNotset,
}
11 changes: 11 additions & 0 deletions tests/test_log_levels.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
# repository for complete details.

import logging
import pickle

import pytest

from structlog import make_filtering_bound_logger
from structlog._log_levels import _LEVEL_TO_NAME
from structlog.testing import CapturingLogger


Expand Down Expand Up @@ -53,3 +55,12 @@ def test_exception_passed(self, bl, cl):
bl.exception("boom", exc_info=42)

assert [("error", (), {"event": "boom", "exc_info": 42})]

@pytest.mark.parametrize("level", tuple(_LEVEL_TO_NAME.keys()))
def test_pickle(self, level):
"""
FilteringBoundLogger are pickleable.
"""
bl = make_filtering_bound_logger(level)

assert bl == pickle.loads(pickle.dumps(bl))

0 comments on commit 334f029

Please sign in to comment.