Skip to content

Commit fe13eeb

Browse files
authored
Keep Named*Logger.name working across structlog releases (#66875)
A pre-emptive change to support - structlog 26.1.0 (most likely version - merged in hynek/structlog#786) adds a `name` slot and a kwarg-only `name` argument to `BytesLogger`, and its `__init__` now assigns `self.name = name`. In NamedBytesLogger we were setting `self.name` *before* calling `super().__init__`, so on the new release the parent would overwrite the caller-supplied name with `None`, breaking the per-logger level lookup in `make_filtering_logger` and any other consumer that reads `logger.name`. Since this is not yet released, I can't be certain if it will be in 26.1.0 or something else, but it's merged to structlog's main, so lets cope with it when ever it lands.
1 parent 1193e5e commit fe13eeb

2 files changed

Lines changed: 41 additions & 6 deletions

File tree

shared/logging/src/airflow_shared/logging/structlog.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,24 +204,38 @@ def maker(logger: WrappedLogger, *args, **kwargs):
204204
return maker
205205

206206

207+
# structlog >= 26.1.0 added a `name` slot + kwarg to BytesLogger
208+
# (hynek/structlog#786). Detect it once so we can avoid a redundant slot and
209+
# forward `name` through the parent init. The same detection is applied to
210+
# WriteLogger so the analogous upstream change lands without a regression.
211+
_BYTES_LOGGER_HAS_NAME = "name" in getattr(structlog.BytesLogger, "__slots__", ())
212+
_WRITE_LOGGER_HAS_NAME = "name" in getattr(structlog.WriteLogger, "__slots__", ())
213+
214+
207215
class NamedBytesLogger(structlog.BytesLogger):
208-
__slots__ = ("name",)
216+
__slots__ = () if _BYTES_LOGGER_HAS_NAME else ("name",)
209217

210218
def __init__(self, name: str | None = None, file: BinaryIO | None = None):
211-
self.name = name
212219
if file is not None:
213220
file = make_file_io_non_caching(file)
214-
super().__init__(file)
221+
if _BYTES_LOGGER_HAS_NAME:
222+
super().__init__(file, name=name) # type: ignore[call-arg]
223+
else:
224+
super().__init__(file)
225+
self.name = name
215226

216227

217228
class NamedWriteLogger(structlog.WriteLogger):
218-
__slots__ = ("name",)
229+
__slots__ = () if _WRITE_LOGGER_HAS_NAME else ("name",)
219230

220231
def __init__(self, name: str | None = None, file: TextIO | None = None):
221-
self.name = name
222232
if file is not None:
223233
file = make_file_io_non_caching(file)
224-
super().__init__(file)
234+
if _WRITE_LOGGER_HAS_NAME:
235+
super().__init__(file, name=name) # type: ignore[call-arg]
236+
else:
237+
super().__init__(file)
238+
self.name = name
225239

226240

227241
LogOutputType = TypeVar("LogOutputType", bound=TextIO | BinaryIO)

shared/logging/tests/logging/test_structlog.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,3 +707,24 @@ def test_dict_positional_arg_formatting(structlog_config, get_logger, message, a
707707

708708
written = json.load(bio)
709709
assert written["event"] == expected_event
710+
711+
712+
def test_named_bytes_logger_preserves_name():
713+
"""
714+
structlog 26.1.0 (hynek/structlog#786) gives ``BytesLogger`` its own ``name``
715+
slot and sets ``self.name`` in ``__init__``; older releases do not. This test
716+
runs against whichever version is installed and pins the contract that the
717+
supplied name survives construction on both.
718+
"""
719+
from airflow_shared.logging.structlog import NamedBytesLogger
720+
721+
assert NamedBytesLogger("my.logger").name == "my.logger"
722+
assert NamedBytesLogger().name is None
723+
724+
725+
def test_named_write_logger_preserves_name():
726+
"""Same contract for NamedWriteLogger in case structlog mirrors #786 for WriteLogger."""
727+
from airflow_shared.logging.structlog import NamedWriteLogger
728+
729+
assert NamedWriteLogger("my.logger").name == "my.logger"
730+
assert NamedWriteLogger().name is None

0 commit comments

Comments
 (0)