From 4281cf4addac73cdb7cee47d760ce460ad711b1c Mon Sep 17 00:00:00 2001 From: Artur Shiriev Date: Sat, 2 May 2026 19:08:03 +0300 Subject: [PATCH] feat: add logging_enabled flag, decouple from service_debug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LoggingInstrument.is_ready() previously returned False whenever service_debug=True — and BaseConfig.service_debug defaults to True. So out of the box, the structured-logging instrument silently skipped its own bootstrap, and users assumed the JSON logging pipeline was working when it wasn't. Add a dedicated logging_enabled: bool = True field on LoggingConfig and gate is_ready() on it instead. service_debug stays focused on framework debug mode (FastAPI/Litestar app.debug). Default behavior changes: projects with no overrides now get structured logging. Users who want the old skip-logging behavior set logging_enabled=False explicitly. Co-Authored-By: Claude Opus 4.7 --- lite_bootstrap/instruments/logging_instrument.py | 5 +++-- tests/test_free_bootstrap.py | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lite_bootstrap/instruments/logging_instrument.py b/lite_bootstrap/instruments/logging_instrument.py index 0481756..d183b34 100644 --- a/lite_bootstrap/instruments/logging_instrument.py +++ b/lite_bootstrap/instruments/logging_instrument.py @@ -111,12 +111,13 @@ class LoggingConfig(BaseConfig): default_factory=list, ) logging_time_stamper: "structlog.processors.TimeStamper | None" = None + logging_enabled: bool = True @dataclasses.dataclass(kw_only=True, slots=True, frozen=True) class LoggingInstrument(BaseInstrument): bootstrap_config: LoggingConfig - not_ready_message = "service_debug is True" + not_ready_message = "logging_enabled is False" missing_dependency_message = "structlog is not installed" _logger_factory: "MemoryLoggerFactory | None" = dataclasses.field( default_factory=lambda: None, init=False, repr=False, compare=False @@ -136,7 +137,7 @@ def structlog_pre_chain_processors(self) -> list[typing.Any]: ] def is_ready(self) -> bool: - return not self.bootstrap_config.service_debug and import_checker.is_structlog_installed + return self.bootstrap_config.logging_enabled and import_checker.is_structlog_installed @staticmethod def check_dependencies() -> bool: diff --git a/tests/test_free_bootstrap.py b/tests/test_free_bootstrap.py index 708a3b8..d9eb228 100644 --- a/tests/test_free_bootstrap.py +++ b/tests/test_free_bootstrap.py @@ -31,11 +31,11 @@ def test_free_bootstrap(free_bootstrapper_config: FreeBootstrapperConfig) -> Non bootstrapper.teardown() -def test_free_bootstrap_logging_not_ready() -> None: +def test_free_bootstrap_logging_disabled() -> None: with capture_logs() as cap_logs: FreeBootstrapper( bootstrap_config=FreeBootstrapperConfig( - service_debug=True, + logging_enabled=False, opentelemetry_instrumentors=[CustomInstrumentor()], opentelemetry_log_traces=True, sentry_dsn="https://testdsn@localhost/1", @@ -44,7 +44,7 @@ def test_free_bootstrap_logging_not_ready() -> None: ), ) assert cap_logs == [ - {"event": "LoggingInstrument is not ready: service_debug is True", "log_level": "info"}, + {"event": "LoggingInstrument is not ready: logging_enabled is False", "log_level": "info"}, {"event": "PyroscopeInstrument is not ready: pyroscope_endpoint is empty", "log_level": "info"}, ]