Skip to content

Commit 8766ba8

Browse files
committed
Allow configuring logging via ConfigManager
Signed-off-by: Leandro Lucarella <[email protected]>
1 parent 8607207 commit 8766ba8

File tree

1 file changed

+51
-8
lines changed

1 file changed

+51
-8
lines changed

src/frequenz/sdk/config/_manager.py

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def __init__( # pylint: disable=too-many-arguments
5656
/,
5757
*,
5858
force_polling: bool = True,
59+
logging_config_key: str | Sequence[str] | None = "logging",
5960
name: str | None = None,
6061
polling_interval: timedelta = timedelta(seconds=1),
6162
) -> None:
@@ -68,6 +69,10 @@ def __init__( # pylint: disable=too-many-arguments
6869
the previous paths. Dict keys will be merged recursively, but other
6970
objects (like lists) will be replaced by the value in the last path.
7071
force_polling: Whether to force file polling to check for changes.
72+
logging_config_key: The key to use for the logging configuration. If `None`,
73+
logging configuration will not be managed. If a key is provided, the
74+
manager update the logging configuration whenever the configuration
75+
changes.
7176
name: A name to use when creating actors. If `None`, `str(id(self))` will
7277
be used. This is used mostly for debugging purposes.
7378
polling_interval: The interval to poll for changes. Only relevant if
@@ -80,7 +85,7 @@ def __init__( # pylint: disable=too-many-arguments
8085
)
8186
"""The broadcast channel for the configuration."""
8287

83-
self.actor: Final[ConfigManagingActor] = ConfigManagingActor(
88+
self.config_actor: Final[ConfigManagingActor] = ConfigManagingActor(
8489
config_paths,
8590
self.config_channel.new_sender(),
8691
name=self.name,
@@ -89,16 +94,31 @@ def __init__( # pylint: disable=too-many-arguments
8994
)
9095
"""The actor that manages the configuration."""
9196

97+
# pylint: disable-next=import-outside-toplevel,cyclic-import
98+
from ._logging_actor import LoggingConfigUpdatingActor
99+
100+
self.logging_actor: Final[LoggingConfigUpdatingActor | None] = (
101+
None
102+
if logging_config_key is None
103+
else LoggingConfigUpdatingActor(
104+
self, config_key=logging_config_key, name=self.name
105+
)
106+
)
107+
92108
@override
93109
def start(self) -> None:
94110
"""Start this config manager."""
95-
self.actor.start()
111+
self.config_actor.start()
112+
if self.logging_actor:
113+
self.logging_actor.start()
96114

97115
@property
98116
@override
99117
def is_running(self) -> bool:
100118
"""Whether this config manager is running."""
101-
return self.actor.is_running
119+
return self.config_actor.is_running or (
120+
self.logging_actor is not None and self.logging_actor.is_running
121+
)
102122

103123
@override
104124
def cancel(self, msg: str | None = None) -> None:
@@ -107,11 +127,12 @@ def cancel(self, msg: str | None = None) -> None:
107127
Args:
108128
msg: The message to be passed to the tasks being cancelled.
109129
"""
110-
self.actor.cancel(msg)
130+
if self.logging_actor:
131+
self.logging_actor.cancel(msg)
132+
self.config_actor.cancel(msg)
111133

112-
# We need the noqa because the `BaseExceptionGroup` is raised indirectly.
113134
@override
114-
async def wait(self) -> None: # noqa: DOC502
135+
async def wait(self) -> None:
115136
"""Wait this config manager to finish.
116137
117138
Wait until all tasks and actors are finished.
@@ -121,12 +142,34 @@ async def wait(self) -> None: # noqa: DOC502
121142
exception (`CancelError` is not considered an error and not returned in
122143
the exception group).
123144
"""
124-
await self.actor
145+
exceptions: list[BaseException] = []
146+
if self.logging_actor:
147+
try:
148+
await self.logging_actor
149+
except BaseExceptionGroup as err: # pylint: disable=try-except-raise
150+
exceptions.append(err)
151+
152+
try:
153+
await self.config_actor
154+
except BaseExceptionGroup as err: # pylint: disable=try-except-raise
155+
exceptions.append(err)
156+
157+
if exceptions:
158+
raise BaseExceptionGroup(f"Error while stopping {self!r}", exceptions)
125159

126160
@override
127161
def __repr__(self) -> str:
128162
"""Return a string representation of this config manager."""
129-
return f"config_channel={self.config_channel!r}, " f"actor={self.actor!r}>"
163+
logging_actor = (
164+
f"logging_actor={self.logging_actor!r}, " if self.logging_actor else ""
165+
)
166+
return (
167+
f"<{self.__class__.__name__}: "
168+
f"name={self.name!r}, "
169+
f"config_channel={self.config_channel!r}, "
170+
+ logging_actor
171+
+ f"config_actor={self.config_actor!r}>"
172+
)
130173

131174
def new_receiver( # pylint: disable=too-many-arguments
132175
self,

0 commit comments

Comments
 (0)