From ef0c00913d7eacb647b10f9a29e4cb903f6928e1 Mon Sep 17 00:00:00 2001 From: William Barnhart Date: Sun, 19 Jul 2026 17:48:16 +0000 Subject: [PATCH] Add --override-logging CLI flag to opt out of root logger reconfiguration mode.Worker already supports override_logging=False to skip setup_logging()'s logging.root.handlers.clear() + colorlog reconfiguration, letting a caller's own logging setup (e.g. structlog piped into stdlib logging, or a plain logging.basicConfig()) survive worker startup. But faust's CLI (Command.worker_for_service()) constructed the Worker with a hardcoded kwarg list that never passed override_logging through, and there was no CLI flag for it -- so anyone running the standard `faust worker` command had no way to reach a lever that already existed one layer down. Add --override-logging/--no-override-logging, following the same compat_option pattern already used for --loglevel/--blocking-timeout/ --console-port: CLI flag -> State.override_logging -> Command ._override_logging -> Command.override_logging property (default True, matching mode.Worker's own default) -> Worker(override_logging=...). Fixes faust-streaming/mode#36. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01HHPL4VFWQRQPpjR1gXSKyL --- faust/cli/base.py | 24 ++++++++++++++++++++++++ tests/unit/cli/test_base.py | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/faust/cli/base.py b/faust/cli/base.py index 8e57b6485..b80baea88 100644 --- a/faust/cli/base.py +++ b/faust/cli/base.py @@ -156,6 +156,7 @@ class State: loglevel: Optional[int] = None blocking_timeout: Optional[float] = None console_port: Optional[int] = None + override_logging: Optional[bool] = None def compat_option( @@ -208,6 +209,17 @@ def _callback( type=params.TCPPort(), help="when --debug: Port to run debugger console on.", ), + compat_option( + "--override-logging/--no-override-logging", + state_key="override_logging", + default=True, + help=( + "Reconfigure the root logger when the worker starts " + "(clears any existing handlers, e.g. from structlog or " + "logging.basicConfig). Pass --no-override-logging to leave " + "your own logging setup untouched." + ), + ), ] core_options: OptionSequence = [ @@ -509,6 +521,7 @@ class Command(abc.ABC): # noqa: B024 _loglevel: Optional[str] _blocking_timeout: Optional[float] _console_port: Optional[int] + _override_logging: Optional[bool] stdout: IO stderr: IO @@ -592,6 +605,7 @@ def __init__(self, ctx: click.Context, *args: Any, **kwargs: Any) -> None: self._loglevel = self.state.loglevel self._blocking_timeout = self.state.blocking_timeout self._console_port = self.state.console_port + self._override_logging = self.state.override_logging @no_type_check # Subclasses can omit *args, **kwargs in signature. # noqa: B027 async def run(self, *args: Any, **kwargs: Any) -> Any: # noqa: B027 @@ -662,6 +676,7 @@ def worker_for_service( redirect_stdouts_level=self.redirect_stdouts_level, loop=loop or asyncio.get_event_loop_policy().get_event_loop(), daemon=self.daemon, + override_logging=self.override_logging, ) @property @@ -772,6 +787,15 @@ def console_port(self) -> int: def console_port(self, port: int) -> None: self._console_port = port + @property + def override_logging(self) -> bool: + """Return whether the worker should reconfigure the root logger.""" + return True if self._override_logging is None else self._override_logging + + @override_logging.setter + def override_logging(self, override: bool) -> None: + self._override_logging = override + class AppCommand(Command): """Command that takes ``-A app`` as argument.""" diff --git a/tests/unit/cli/test_base.py b/tests/unit/cli/test_base.py index 5fd3aeab0..7093d9f99 100644 --- a/tests/unit/cli/test_base.py +++ b/tests/unit/cli/test_base.py @@ -368,6 +368,7 @@ def test_worker_for_service(self, *, command): redirect_stdouts_level=command.redirect_stdouts_level, loop=loop, daemon=command.daemon, + override_logging=command.override_logging, ) def test__Worker(self, *, command): @@ -471,6 +472,13 @@ def test_console_port(self, *, command, ctx): command.console_port = None assert command.console_port == CONSOLE_PORT + def test_override_logging(self, *, command, ctx): + assert command.override_logging == ctx.ensure_object().override_logging + command.override_logging = False + assert command.override_logging is False + command.override_logging = None + assert command.override_logging is True + class Test_AppCommand: @pytest.fixture()