Skip to content

Commit

Permalink
refactor(logging): improve LoggingConfig & deprecate `LoggingConfig…
Browse files Browse the repository at this point in the history
….propagate` (#3543)

* refactor(logging): improve `LoggingConfig` tests
* refactor(logging): merge tests from `LoggingConfig`
* refactor(logging): refactor and improve `LoggingConfig`
* fix(logging): Mark `LoggingConfig.propagate` as deprecated
* fix(logging): typing errors in tests
* docs(logging): update docstrings
  • Loading branch information
jderrien authored Jun 16, 2024
1 parent 65f01ea commit 1db20fc
Show file tree
Hide file tree
Showing 2 changed files with 305 additions and 103 deletions.
83 changes: 55 additions & 28 deletions litestar/logging/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@
}


def _get_default_formatters() -> dict[str, dict[str, Any]]:
return {
"standard": {"format": "%(levelname)s - %(asctime)s - %(name)s - %(module)s - %(message)s"},
}


def _get_default_loggers() -> dict[str, dict[str, Any]]:
return {
"litestar": {"level": "INFO", "handlers": ["queue_listener"], "propagate": False},
}


def get_logger_placeholder(_: str | None = None) -> NoReturn:
"""Raise: An :class:`ImproperlyConfiguredException <.exceptions.ImproperlyConfiguredException>`"""
raise ImproperlyConfiguredException(
Expand Down Expand Up @@ -184,27 +196,34 @@ class LoggingConfig(BaseLoggingConfig):
disable_existing_loggers: bool = field(default=False)
"""Whether any existing non-root loggers are to be disabled."""
filters: dict[str, dict[str, Any]] | None = field(default=None)
"""A dict in which each key is a filter id and each value is a dict describing how to configure the corresponding
Filter instance.
"""A dict in which each key is a filter id and each value is a dict describing how to configure the
corresponding Filter_ instance.
.. _Filter: https://docs.python.org/3/library/logging.html#filter-objects
"""
propagate: bool = field(default=True)
"""If messages must propagate to handlers higher up the logger hierarchy from this logger."""
formatters: dict[str, dict[str, Any]] = field(
default_factory=lambda: {
"standard": {"format": "%(levelname)s - %(asctime)s - %(name)s - %(module)s - %(message)s"}
}
)
"""If messages must propagate to handlers higher up the logger hierarchy from this logger.
.. deprecated:: 2.10.0
This parameter is deprecated. It will be removed in a future release. Use ``propagate`` at the logger level.
"""
formatters: dict[str, dict[str, Any]] = field(default_factory=_get_default_formatters)
"""A dict in which each key is a formatter and each value is a dict describing how to configure the
corresponding Formatter_ instance. A ``standard`` formatter is provided.
.. _Formatter: https://docs.python.org/3/library/logging.html#formatter-objects
"""
handlers: dict[str, dict[str, Any]] = field(default_factory=_get_default_handlers)
"""A dict in which each key is a handler id and each value is a dict describing how to configure the corresponding
Handler instance.
"""A dict in which each key is a handler id and each value is a dict describing how to configure the
corresponding Handler_ instance. Two handlers are provided, ``console`` and ``queue_listener``.
.. _Handler: https://docs.python.org/3/library/logging.html#handler-objects
"""
loggers: dict[str, dict[str, Any]] = field(
default_factory=lambda: {
"litestar": {"level": "INFO", "handlers": ["queue_listener"], "propagate": False},
}
)
"""A dict in which each key is a logger name and each value is a dict describing how to configure the corresponding
Logger instance.
loggers: dict[str, dict[str, Any]] = field(default_factory=_get_default_loggers)
"""A dict in which each key is a logger name and each value is a dict describing how to configure the
corresponding Logger_ instance. A ``litestar`` logger is mandatory and will be configured as required.
.. _Logger: https://docs.python.org/3/library/logging.html#logger-objects
"""
root: dict[str, dict[str, Any] | list[Any] | str] = field(
default_factory=lambda: {
Expand All @@ -230,15 +249,17 @@ class LoggingConfig(BaseLoggingConfig):
"""Handler function for logging exceptions."""

def __post_init__(self) -> None:
if "standard" not in self.formatters:
self.formatters["standard"] = _get_default_formatters()["standard"]

if "console" not in self.handlers:
self.handlers["console"] = _get_default_handlers()["console"]

if "queue_listener" not in self.handlers:
self.handlers["queue_listener"] = _get_default_handlers()["queue_listener"]

if "litestar" not in self.loggers:
self.loggers["litestar"] = {
"level": "INFO",
"handlers": ["queue_listener"],
"propagate": False,
}
self.loggers["litestar"] = _get_default_loggers()["litestar"]

if self.log_exceptions != "never" and self.exception_logging_handler is None:
self.exception_logging_handler = _default_exception_logging_handler_factory(
Expand All @@ -252,27 +273,33 @@ def configure(self) -> GetLogger:
A 'logging.getLogger' like function.
"""

excluded_fields: tuple[str, ...]
excluded_fields: set[str] = {
"configure_root_logger",
"exception_logging_handler",
"log_exceptions",
"propagate",
"traceback_line_limit",
}

if not self.configure_root_logger:
excluded_fields.add("root")

if "picologging" in " ".join([handler["class"] for handler in self.handlers.values()]):
try:
from picologging import config, getLogger
except ImportError as e:
raise MissingDependencyException("picologging") from e

excluded_fields = ("incremental", "configure_root_logger")
excluded_fields.add("incremental")
else:
from logging import config, getLogger # type: ignore[no-redef, assignment]

excluded_fields = ("configure_root_logger",)

values = {
_field.name: getattr(self, _field.name)
for _field in fields(self)
if getattr(self, _field.name) is not None and _field.name not in excluded_fields
}

if not self.configure_root_logger:
values.pop("root")
config.dictConfig(values)
return cast("Callable[[str], Logger]", getLogger)

Expand Down
Loading

0 comments on commit 1db20fc

Please sign in to comment.