Skip to content

Commit e497b2d

Browse files
authored
fix: keep lite-bootstrap's root log handler on Litestar and write litestar records once (#345)
1 parent 31b47e4 commit e497b2d

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

‎lite_bootstrap/bootstrappers/litestar_bootstrapper.py‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from litestar import status_codes
3232
from litestar.config.app import AppConfig
3333
from litestar.config.cors import CORSConfig
34+
from litestar.logging.config import LoggingConfig as LitestarStandardLoggingConfig
3435
from litestar.logging.config import StructLoggingConfig
3536
from litestar.middleware.logging import LoggingMiddlewareConfig
3637
from litestar.openapi import OpenAPIConfig
@@ -187,6 +188,16 @@ def _build_logging_middleware_config(self) -> "LoggingMiddlewareConfig":
187188
exclude=excluded_paths or None,
188189
)
189190

191+
def _build_standard_lib_logging_config(self) -> "LitestarStandardLoggingConfig":
192+
"""Leave stdlib output to lite-bootstrap: Litestar's default config replaces the root handler."""
193+
return LitestarStandardLoggingConfig(
194+
configure_root_logger=False,
195+
handlers={"console": {"class": "logging.NullHandler"}, "queue_listener": {"class": "logging.NullHandler"}},
196+
loggers={
197+
"litestar": {"level": self.bootstrap_config.logging_log_level, "handlers": [], "propagate": False}
198+
},
199+
)
200+
190201
def _configure_structlog_loggers(self) -> None:
191202
"""Register Litestar's StructlogPlugin instead of calling ``structlog.configure`` directly."""
192203
self.bootstrap_config.application_config.plugins.append(
@@ -198,7 +209,7 @@ def _configure_structlog_loggers(self) -> None:
198209
wrapper_class=structlog.stdlib.BoundLogger,
199210
cache_logger_on_first_use=True,
200211
pretty_print_tty=False,
201-
standard_lib_logging_config=None,
212+
standard_lib_logging_config=self._build_standard_lib_logging_config(),
202213
),
203214
# Litestar defaults this to True, which logs full request/response bodies.
204215
enable_middleware_logging=self.bootstrap_config.litestar_logging_middleware_enabled,

‎tests/test_litestar_bootstrap.py‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import inspect
44
import json
55
import logging
6+
import logging.handlers
67
import sys
78
import typing
89
import warnings
@@ -309,6 +310,31 @@ async def _login_handler(data: dict[str, str]) -> dict[str, str]:
309310
return log_lines
310311

311312

313+
def test_litestar_logging_keeps_lite_bootstrap_root_handler(litestar_config: LitestarConfig) -> None:
314+
application = LitestarBootstrapper(bootstrap_config=litestar_config).bootstrap()
315+
with TestClient(app=application):
316+
root_handlers = logging.getLogger().handlers
317+
assert not any(isinstance(handler, logging.handlers.QueueHandler) for handler in root_handlers)
318+
assert any(isinstance(handler.formatter, structlog.stdlib.ProcessorFormatter) for handler in root_handlers)
319+
320+
321+
def test_litestar_access_log_has_one_handler(litestar_config: LitestarConfig) -> None:
322+
@litestar.get("/ping")
323+
async def _ping_handler() -> dict[str, bool]:
324+
return {"ok": True}
325+
326+
config = dataclasses.replace(
327+
litestar_config,
328+
litestar_logging_middleware_enabled=True,
329+
application_config=AppConfig(route_handlers=[_ping_handler]),
330+
)
331+
application = LitestarBootstrapper(bootstrap_config=config).bootstrap()
332+
with TestClient(app=application) as client:
333+
assert client.get("/ping").status_code == status_codes.HTTP_200_OK
334+
litestar_handlers = logging.getLogger("litestar").handlers
335+
assert [type(handler) for handler in litestar_handlers] == [logging.handlers.MemoryHandler]
336+
337+
312338
def test_litestar_access_logging_disabled_by_default(litestar_config: LitestarConfig) -> None:
313339
log_lines = _post_password(litestar_config)
314340

0 commit comments

Comments
 (0)