Skip to content

Commit f9ecb5a

Browse files
Consolidate command class loggers into base.Base property (#4486)
Depends on PR #4487 Problem: Command classes had duplicate logger setup code across 7+ files. Solution: Single @Property _log in base.Base with automatic step derivation. Key Changes: - Added @Property _log to base.Base with automatic step naming - Removed individual logger setups from 7 command classes - Eliminated 50+ lines of duplicate code - Updated tests for scenario logger pattern Impact: - Future command classes get logger automatically - Consistent scenario->step format across all commands - Perfect architectural separation of concerns Testing: All command tests pass with proper context validation. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 1ef06d8 commit f9ecb5a

File tree

14 files changed

+45
-84
lines changed

14 files changed

+45
-84
lines changed

src/molecule/command/base.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ def __init_subclass__(cls) -> None:
8181
for wrapper in logger.get_section_loggers():
8282
cls.execute = wrapper(cls.execute) # type: ignore[method-assign,assignment]
8383

84+
@property
85+
def _log(self) -> logger.ScenarioLoggerAdapter:
86+
"""Get a scenario logger with command-specific step context.
87+
88+
Returns:
89+
A scenario logger adapter with current scenario and step context.
90+
"""
91+
step_name = self.__class__.__name__.lower()
92+
return logger.get_scenario_logger(__name__, self._config.scenario.name, step_name)
93+
8494
@abc.abstractmethod
8595
def execute(
8696
self,

src/molecule/command/cleanup.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from typing import TYPE_CHECKING
2525

26-
from molecule import config, logger
2726
from molecule.click_cfg import click_command_ex, common_options
2827
from molecule.command import base
2928

@@ -37,15 +36,6 @@
3736
class Cleanup(base.Base):
3837
"""Cleanup Command Class."""
3938

40-
def __init__(self, c: config.Config) -> None:
41-
"""Initialize Cleanup command.
42-
43-
Args:
44-
c: An instance of a Molecule config.
45-
"""
46-
super().__init__(c)
47-
self._log = logger.get_scenario_logger(__name__, self._config.scenario.name)
48-
4939
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
5040
"""Execute the actions necessary to cleanup the instances.
5141

src/molecule/command/create.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from typing import TYPE_CHECKING
2525

26-
from molecule import config, logger
2726
from molecule.click_cfg import click_command_ex, common_options
2827
from molecule.command import base
2928

@@ -37,15 +36,6 @@
3736
class Create(base.Base):
3837
"""Create Command Class."""
3938

40-
def __init__(self, c: config.Config) -> None:
41-
"""Initialize Create command.
42-
43-
Args:
44-
c: An instance of a Molecule config.
45-
"""
46-
super().__init__(c)
47-
self._log = logger.get_scenario_logger(__name__, self._config.scenario.name)
48-
4939
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
5040
"""Execute the actions necessary to perform a `molecule create`.
5141

src/molecule/command/destroy.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from typing import TYPE_CHECKING
2525

26-
from molecule import config, logger
2726
from molecule.click_cfg import click_command_ex, common_options
2827
from molecule.command import base
2928

@@ -37,15 +36,6 @@
3736
class Destroy(base.Base):
3837
"""Destroy Command Class."""
3938

40-
def __init__(self, c: config.Config) -> None:
41-
"""Initialize Destroy command.
42-
43-
Args:
44-
c: An instance of a Molecule config.
45-
"""
46-
super().__init__(c)
47-
self._log = logger.get_scenario_logger(__name__, self._config.scenario.name)
48-
4939
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
5040
"""Execute the actions necessary to perform a `molecule destroy`.
5141

src/molecule/command/idempotence.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525

2626
from typing import TYPE_CHECKING
2727

28-
from molecule import config, logger
2928
from molecule.click_cfg import click_command_ex, common_options
3029
from molecule.command import base
3130
from molecule.exceptions import ScenarioFailureError
@@ -45,15 +44,6 @@ class Idempotence(base.Base):
4544
the scenario will be considered idempotent.
4645
"""
4746

48-
def __init__(self, c: config.Config) -> None:
49-
"""Initialize Idempotence command.
50-
51-
Args:
52-
c: An instance of a Molecule config.
53-
"""
54-
super().__init__(c)
55-
self._log = logger.get_scenario_logger(__name__, self._config.scenario.name)
56-
5747
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
5848
"""Execute the actions necessary to perform a `molecule idempotence`.
5949

src/molecule/command/init/scenario.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def __init__(self, config: Config, command_args: CommandArgs) -> None:
9898
self._command_args = command_args
9999
# For init scenario, use the scenario name from command args
100100
scenario_name = command_args.get("scenario_name", "unknown")
101-
self._log = logger.get_scenario_logger(__name__, scenario_name)
101+
self._log = logger.get_scenario_logger(__name__, scenario_name, "init")
102102

103103
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
104104
"""Execute the actions necessary to perform a `molecule init scenario`.

src/molecule/command/login.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
from typing import TYPE_CHECKING
2929

30-
from molecule import logger, scenarios
30+
from molecule import scenarios
3131
from molecule.click_cfg import click_command_ex, options
3232
from molecule.command import base
3333
from molecule.exceptions import MoleculeError
@@ -51,7 +51,6 @@ def __init__(self, c: config.Config) -> None:
5151
"""
5252
super().__init__(c)
5353
self._pt = None
54-
self._log = logger.get_scenario_logger(__name__, self._config.scenario.name)
5554

5655
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
5756
"""Execute the actions necessary to perform a `molecule login`.

src/molecule/command/prepare.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from typing import TYPE_CHECKING
2525

26-
from molecule import config, logger
2726
from molecule.click_cfg import click_command_ex, common_options
2827
from molecule.command import base
2928

@@ -87,15 +86,6 @@ class Prepare(base.Base):
8786
molecule.yml.
8887
"""
8988

90-
def __init__(self, c: config.Config) -> None:
91-
"""Initialize Prepare command.
92-
93-
Args:
94-
c: An instance of a Molecule config.
95-
"""
96-
super().__init__(c)
97-
self._log = logger.get_scenario_logger(__name__, self._config.scenario.name)
98-
9989
def execute(self, action_args: list[str] | None = None) -> None: # noqa: ARG002
10090
"""Execute the actions necessary to prepare the instances.
10191

src/molecule/command/side_effect.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323

2424
from typing import TYPE_CHECKING
2525

26-
from molecule import config, logger
2726
from molecule.click_cfg import click_command_ex, common_options
2827
from molecule.command import base
2928

@@ -40,15 +39,6 @@ class SideEffect(base.Base):
4039
See the provisioners documentation for further details.
4140
"""
4241

43-
def __init__(self, c: config.Config) -> None:
44-
"""Initialize SideEffect command.
45-
46-
Args:
47-
c: An instance of a Molecule config.
48-
"""
49-
super().__init__(c)
50-
self._log = logger.get_scenario_logger(__name__, self._config.scenario.name)
51-
5242
def execute(self, action_args: list[str] | None = None) -> None:
5343
"""Execute the actions necessary to perform a `molecule side-effect`.
5444

src/molecule/logger.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,22 +247,20 @@ def process(
247247
def get_scenario_logger(
248248
name: str,
249249
scenario_name: str,
250-
step_name: str | None = None,
250+
step_name: str,
251251
) -> ScenarioLoggerAdapter:
252252
"""Return a scenario-aware logger that includes scenario name in all messages.
253253
254254
Args:
255255
name: Name of the child logger.
256256
scenario_name: Name of the scenario for context.
257-
step_name: Optional step name (e.g., 'converge', 'create', 'destroy').
257+
step_name: Step name (e.g., 'converge', 'create', 'destroy').
258258
259259
Returns:
260260
A ScenarioLoggerAdapter that includes scenario context in all messages.
261261
"""
262262
logger = get_logger(name)
263-
extra = {"scenario_name": scenario_name}
264-
if step_name:
265-
extra["step_name"] = step_name
263+
extra = {"scenario_name": scenario_name, "step_name": step_name}
266264
return ScenarioLoggerAdapter(logger, extra)
267265

268266

0 commit comments

Comments
 (0)