What's needed?
We sometimes want to configure some loggers differently (for example to print debug messages), but it is not easy at all to figure out which logger should be configured when some high-level module is composed of other private modules that the user is never supposed to import directly, and __name__ is used as logger name, which maps to the fully qualified module name.
For example, the timeseries module have these debug log messages:
src/frequenz/sdk/timeseries/_grid_frequency.py: _logger.debug("Sent request for grid frequency: %s", self._source_component)
src/frequenz/sdk/timeseries/_moving_window.py: _logger.debug("Returning value at index %s ", key)
src/frequenz/sdk/timeseries/_moving_window.py: _logger.debug("Received new sample: %s", sample)
src/frequenz/sdk/timeseries/_periodic_feature_extractor.py: _logger.debug("Initializing PeriodicFeatureExtractor!")
If someone configures the logger frequenz.sdk.timeseries to print debug logs, they won't actually see any logs.
In these cases, the users have basically no reliable way to know which loggers they should configure.
Proposed solution
Option 1
Use always the high-level module name as logger name. We could use a convention, for example, to have a _logger.py module that instantiates and exposes the global logger for that high-level module, and then all the internal sub-modules will just import that logger.
To follow the example, we would have a src/frequenz/sdk/timeseries/_logger.py module like:
import logging
logger = logging.getLogger(__name__[:__name__.rindex('.')])
And then in, for example, src/frequenz/sdk/timeseries/_moving_window.py:
from ._logger import logger
# ...
_logger.debug("Returning value at index %s ", key)
Option 2
Add some utility function to the SDK (or probably better core) to get the logger ignoring the private modules, like logger = get_public_logger(__name__):
import logging
def get_public_logger(module_name):
parts = module_name.split('.')
public_parts = []
for part in parts:
if part.startswith('_'):
break
public_parts.append(part)
return logging.getLogger('.'.join(public_parts))
What's needed?
We sometimes want to configure some loggers differently (for example to print debug messages), but it is not easy at all to figure out which logger should be configured when some high-level module is composed of other private modules that the user is never supposed to import directly, and
__name__is used as logger name, which maps to the fully qualified module name.For example, the
timeseriesmodule have these debug log messages:If someone configures the logger
frequenz.sdk.timeseriesto print debug logs, they won't actually see any logs.In these cases, the users have basically no reliable way to know which loggers they should configure.
Proposed solution
Option 1
Use always the high-level module name as logger name. We could use a convention, for example, to have a
_logger.pymodule that instantiates and exposes the global logger for that high-level module, and then all the internal sub-modules will just import that logger.To follow the example, we would have a
src/frequenz/sdk/timeseries/_logger.pymodule like:And then in, for example,
src/frequenz/sdk/timeseries/_moving_window.py:Option 2
Add some utility function to the SDK (or probably better core) to get the logger ignoring the private modules, like
logger = get_public_logger(__name__):