diff --git a/CHANGELOG.md b/CHANGELOG.md index 9edf242b..645fffaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ You can find our backwards-compatibility policy [here](https://github.com/hynek/ - The `structlog.processors.CallsiteParameterAdder` can now be pickled. [#603](https://github.com/hynek/structlog/pull/603) +- `structlog.processors.CallsiteParameterAdder` now also works with `structlog.stdlib.BoundLogger`'s non-standard async methods (`ainfo()`, and so forth) + [#618](https://github.com/hynek/structlog/pull/618) + ### Changed diff --git a/src/structlog/stdlib.py b/src/structlog/stdlib.py index 27ab1f9d..ce64bd96 100644 --- a/src/structlog/stdlib.py +++ b/src/structlog/stdlib.py @@ -144,6 +144,10 @@ class BoundLogger(BoundLoggerBase): .. versionadded:: 23.1.0 Async variants `alog()`, `adebug()`, `ainfo()`, and so forth. + + .. versionchanged:: 24.2.0 + Callsite parameters are now also collected by + `structlog.processors.CallsiteParameterAdder` for async log methods. """ _logger: logging.Logger @@ -393,12 +397,16 @@ async def _dispatch_to_sync( """ Merge contextvars and log using the sync logger in a thread pool. """ + scs_token = _ASYNC_CALLING_STACK.set(sys._getframe().f_back.f_back) # type: ignore[union-attr, arg-type, unused-ignore] ctx = contextvars.copy_context() - await asyncio.get_running_loop().run_in_executor( - None, - lambda: ctx.run(lambda: meth(event, *args, **kw)), - ) + try: + await asyncio.get_running_loop().run_in_executor( + None, + lambda: ctx.run(lambda: meth(event, *args, **kw)), + ) + finally: + _ASYNC_CALLING_STACK.reset(scs_token) async def adebug(self, event: str, *args: Any, **kw: Any) -> None: """ @@ -499,6 +507,8 @@ class AsyncBoundLogger: .. versionchanged:: 20.2.0 fix _dispatch_to_sync contextvars usage .. deprecated:: 23.1.0 Use the regular `BoundLogger` with its a-prefixed methods instead. + .. versionchanged:: 23.3.0 + Callsite parameters are now also collected for async log methods. """ __slots__ = ("sync_bl", "_loop") @@ -594,8 +604,6 @@ async def _dispatch_to_sync( ) -> None: """ Merge contextvars and log using the sync logger in a thread pool. - .. versionchanged:: 23.3.0 - Callsite parameters are now also collected under asyncio. """ scs_token = _ASYNC_CALLING_STACK.set(sys._getframe().f_back.f_back) # type: ignore[union-attr, arg-type, unused-ignore] ctx = contextvars.copy_context() diff --git a/tests/processors/test_processors.py b/tests/processors/test_processors.py index cec06474..91443509 100644 --- a/tests/processors/test_processors.py +++ b/tests/processors/test_processors.py @@ -298,28 +298,35 @@ def test_all_parameters(self) -> None: assert self.parameter_strings == self.get_callsite_parameters().keys() @pytest.mark.asyncio() - async def test_async(self) -> None: + @pytest.mark.parametrize( + ("wrapper_class", "method_name"), + [ + (structlog.stdlib.BoundLogger, "ainfo"), + (structlog.stdlib.AsyncBoundLogger, "info"), + ], + ) + async def test_async(self, wrapper_class, method_name) -> None: """ Callsite information for async invocations are correct. """ string_io = StringIO() - class StingIOLogger(structlog.PrintLogger): + class StringIOLogger(structlog.PrintLogger): def __init__(self): super().__init__(file=string_io) processor = self.make_processor(None, ["concurrent", "threading"]) structlog.configure( processors=[processor, JSONRenderer()], - logger_factory=StingIOLogger, - wrapper_class=structlog.stdlib.AsyncBoundLogger, + logger_factory=StringIOLogger, + wrapper_class=wrapper_class, cache_logger_on_first_use=True, ) logger = structlog.stdlib.get_logger() callsite_params = self.get_callsite_parameters() - await logger.info("baz") + await getattr(logger, method_name)("baz") logger_params = json.loads(string_io.getvalue()) # These are different when running under async