Skip to content

Commit a582535

Browse files
authored
Update the logging config actor configuration format (#1204)
The logging configuration was changed to make it compatible with the configuration generated by the UI, which doesn't quote sub-key properly. To do this, the logger name needs to be specified explicitly now (the configuration key was used as the name before).
2 parents 656386f + c6cf365 commit a582535

File tree

6 files changed

+87
-26
lines changed

6 files changed

+87
-26
lines changed

RELEASE_NOTES.md

+31
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,37 @@
88

99
* Includes a major update of the numpy dependency to v2.x.
1010

11+
* The logging configuration was changed, and now the logger name needs to be specified explicitly (the configuration key was used as the name before). This is to be compatible with configuration generated by the UI, which doesn't quote sub-key properly.
12+
13+
- Before:
14+
15+
```toml
16+
[logging.loggers."frequenz.sdk.config"]
17+
level = "DEBUG"
18+
```
19+
20+
- Now:
21+
22+
```toml
23+
[logging.loggers.frequenz_config]
24+
name = "frequenz.sdk.config"
25+
level = "DEBUG"
26+
```
27+
28+
* The logging configuration now uses a separate class for the root logger configuration: `RootLoggerConfig`.
29+
30+
- Before:
31+
32+
```py
33+
LoggingConfig(root_logger=LoggerConfig(level="ERROR"))
34+
```
35+
36+
- Now:
37+
38+
```py
39+
LoggingConfig(root_logger=RootLoggerConfig(level="ERROR"))
40+
```
41+
1142
## New Features
1243

1344
<!-- Here goes the main new features and examples or instructions on how to use them -->

mkdocs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ plugins:
101101
default_handler: python
102102
handlers:
103103
python:
104+
paths: ["src"]
104105
options:
105-
paths: ["src"]
106106
docstring_section_style: spacy
107107
inherited_members: true
108108
merge_init_into_class: false

src/frequenz/sdk/config/__init__.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,12 @@ async def run(self) -> None:
424424
"""
425425

426426
from ._base_schema import BaseConfigSchema
427-
from ._logging_actor import LoggerConfig, LoggingConfig, LoggingConfigUpdatingActor
427+
from ._logging_actor import (
428+
LoggerConfig,
429+
LoggingConfig,
430+
LoggingConfigUpdatingActor,
431+
RootLoggerConfig,
432+
)
428433
from ._manager import ConfigManager, InvalidValueForKeyError, wait_for_first
429434
from ._managing_actor import ConfigManagingActor
430435
from ._util import load_config
@@ -437,6 +442,7 @@ async def run(self) -> None:
437442
"LoggerConfig",
438443
"LoggingConfig",
439444
"LoggingConfigUpdatingActor",
445+
"RootLoggerConfig",
440446
"load_config",
441447
"wait_for_first",
442448
]

src/frequenz/sdk/config/_logging_actor.py

+32-14
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525

2626

2727
@dataclass(frozen=True, kw_only=True)
28-
class LoggerConfig:
29-
"""A configuration for a logger."""
28+
class RootLoggerConfig:
29+
"""A configuration for the root logger."""
3030

3131
level: LogLevel = field(
3232
default="NOTSET",
@@ -36,15 +36,29 @@ class LoggerConfig:
3636
},
3737
},
3838
)
39-
"""The log level for the logger."""
39+
"""The log level for the root logger."""
40+
41+
42+
@dataclass(frozen=True, kw_only=True)
43+
class LoggerConfig(RootLoggerConfig):
44+
"""A configuration for a logger."""
45+
46+
name: str = field(
47+
metadata={
48+
"metadata": {
49+
"description": "The name of the logger that will be affected by this "
50+
"configuration."
51+
},
52+
},
53+
)
4054

4155

4256
@dataclass(frozen=True, kw_only=True)
4357
class LoggingConfig:
4458
"""A configuration for the logging system."""
4559

46-
root_logger: LoggerConfig = field(
47-
default_factory=lambda: LoggerConfig(level="INFO"),
60+
root_logger: RootLoggerConfig = field(
61+
default_factory=lambda: RootLoggerConfig(level="INFO"),
4862
metadata={
4963
"metadata": {
5064
"description": "Default default configuration for all loggers.",
@@ -73,10 +87,12 @@ class LoggingConfigUpdatingActor(Actor):
7387
[logging.root_logger]
7488
level = "INFO"
7589
76-
[logging.loggers."frequenz.sdk.actor.power_distributing"]
90+
[logging.loggers.power_dist]
91+
name = "frequenz.sdk.actor.power_distributing"
7792
level = "DEBUG"
7893
79-
[logging.loggers."frequenz.channels"]
94+
[logging.loggers.chan]
95+
name = "frequenz.channels"
8096
level = "DEBUG"
8197
```
8298
@@ -184,10 +200,12 @@ def _reconfigure(self, config_update: LoggingConfig | Exception | None) -> None:
184200
def _update_logging(self, config: LoggingConfig) -> None:
185201
"""Configure the logging level."""
186202
# If the logger is not in the new config, set it to NOTSET
187-
loggers_to_unset = self._current_config.loggers.keys() - config.loggers.keys()
188-
for logger_id in loggers_to_unset:
189-
_logger.debug("Unsetting log level for logger '%s'", logger_id)
190-
logging.getLogger(logger_id).setLevel(logging.NOTSET)
203+
old_names = {old.name for old in self._current_config.loggers.values()}
204+
new_names = {new.name for new in config.loggers.values()}
205+
loggers_to_unset = old_names - new_names
206+
for logger_name in loggers_to_unset:
207+
_logger.debug("Unsetting log level for logger '%s'", logger_name)
208+
logging.getLogger(logger_name).setLevel(logging.NOTSET)
191209

192210
self._current_config = config
193211
_logger.info(
@@ -196,12 +214,12 @@ def _update_logging(self, config: LoggingConfig) -> None:
196214
logging.getLogger().setLevel(self._current_config.root_logger.level)
197215

198216
# For each logger in the new config, set the log level
199-
for logger_id, logger_config in self._current_config.loggers.items():
217+
for logger_config in self._current_config.loggers.values():
200218
_logger.info(
201219
"Setting log level for logger '%s' to '%s'",
202-
logger_id,
220+
logger_config.name,
203221
logger_config.level,
204222
)
205-
logging.getLogger(logger_id).setLevel(logger_config.level)
223+
logging.getLogger(logger_config.name).setLevel(logger_config.level)
206224

207225
_logger.info("Logging config update completed.")

tests/config/test_logging_actor.py

+13-10
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
LoggerConfig,
1717
LoggingConfig,
1818
LoggingConfigUpdatingActor,
19+
RootLoggerConfig,
1920
load_config,
2021
)
2122

@@ -25,15 +26,15 @@ def test_logging_config() -> None:
2526
config_raw = {
2627
"root_logger": {"level": "DEBUG"},
2728
"loggers": {
28-
"frequenz.sdk.actor": {"level": "INFO"},
29-
"frequenz.sdk.timeseries": {"level": "ERROR"},
29+
"actor": {"name": "frequenz.sdk.actor", "level": "INFO"},
30+
"timeseries": {"name": "frequenz.sdk.timeseries", "level": "ERROR"},
3031
},
3132
}
3233
config = LoggingConfig(
33-
root_logger=LoggerConfig(level="DEBUG"),
34+
root_logger=RootLoggerConfig(level="DEBUG"),
3435
loggers={
35-
"frequenz.sdk.actor": LoggerConfig(level="INFO"),
36-
"frequenz.sdk.timeseries": LoggerConfig(level="ERROR"),
36+
"actor": LoggerConfig(name="frequenz.sdk.actor", level="INFO"),
37+
"timeseries": LoggerConfig(name="frequenz.sdk.timeseries", level="ERROR"),
3738
},
3839
)
3940

@@ -92,10 +93,12 @@ async def test_logging_config_updating_actor(
9293

9394
# Send first config
9495
expected_config = LoggingConfig(
95-
root_logger=LoggerConfig(level="ERROR"),
96+
root_logger=RootLoggerConfig(level="ERROR"),
9697
loggers={
97-
"frequenz.sdk.actor": LoggerConfig(level="DEBUG"),
98-
"frequenz.sdk.timeseries": LoggerConfig(level="ERROR"),
98+
"actor": LoggerConfig(name="frequenz.sdk.actor", level="DEBUG"),
99+
"timeseries": LoggerConfig(
100+
name="frequenz.sdk.timeseries", level="ERROR"
101+
),
99102
},
100103
)
101104
await mock_config_manager.config_channel.new_sender().send(expected_config)
@@ -121,9 +124,9 @@ async def test_logging_config_updating_actor(
121124

122125
# Update config
123126
expected_config = LoggingConfig(
124-
root_logger=LoggerConfig(level="WARNING"),
127+
root_logger=RootLoggerConfig(level="WARNING"),
125128
loggers={
126-
"frequenz.sdk.actor": LoggerConfig(level="INFO"),
129+
"actor": LoggerConfig(name="frequenz.sdk.actor", level="INFO"),
127130
},
128131
)
129132
await mock_config_manager.config_channel.new_sender().send(expected_config)

tests/config/test_manager.py

+3
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ def config_file(self, tmp_path: pathlib.Path) -> pathlib.Path:
250250
value = 42
251251
252252
[logging.loggers.test]
253+
name = "test"
253254
level = "DEBUG"
254255
"""
255256
)
@@ -276,6 +277,7 @@ async def test_full_config_flow(self, config_file: pathlib.Path) -> None:
276277
value = 43
277278
278279
[logging.loggers.test]
280+
name = "test"
279281
level = "INFO"
280282
"""
281283
)
@@ -313,6 +315,7 @@ async def test_full_config_flow_without_logging(
313315
value = 43
314316
315317
[logging.loggers.test]
318+
name = "test"
316319
level = "DEBUG"
317320
"""
318321
)

0 commit comments

Comments
 (0)