Source code for gfw.common.logging
"""Logging utilities."""
import logging
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Optional, TextIO, Union
from rich.logging import RichHandler
_TIME_ENTRY = "%(asctime)s - "
_DEFAULT_LOG_FORMAT = f"{_TIME_ENTRY}%(name)s - %(message)s"
[docs]
@dataclass
class LoggerConfig:
"""Helper class to setup the root logger.
Args:
format_:
Logger format.
warning_level:
List of packages/modules for which to set the log level as ``WARNING``.
error_level:
List of packages/modules for which to set the log level as ``ERROR``.
"""
format_: str = _DEFAULT_LOG_FORMAT
warning_level: tuple[str, ...] = ()
error_level: tuple[str, ...] = ()
[docs]
def setup(
self,
verbose: bool = False,
rich: bool = True,
log_file: Optional[Union[str, Path]] = None,
log_stream: TextIO = sys.stderr,
) -> logging.Logger:
"""Initializes and configures the root logger.
Args:
verbose:
If true, turns logger level to ``DEBUG``.
rich:
Whether to use `Rich <https://rich.readthedocs.io/en/stable/>`_ library
to colorize console output.
log_file:
Path to file in which to save logs.
log_stream:
Destination stream for log output. Defaults to :py:attr:`sys.stderr`.
Typically set to :py:attr:`sys.stdout` or :py:attr:`sys.stderr`,
but can be any text-mode file-like object
(e.g. an open file or :class:`io.StringIO`)
that implements a ``write(str)`` method.
Ignored if ``rich=True``, since ``RichHandler`` manages its own output stream.
"""
logger = logging.getLogger()
logger.handlers.clear()
handlers: list[Any] = []
fmt = self.format_
if rich:
handlers.append(RichHandler())
fmt = fmt.replace(_TIME_ENTRY, "")
else:
handlers.append(logging.StreamHandler(log_stream))
if log_file is not None:
handlers.append(logging.FileHandler(Path(log_file)))
logger.setLevel(logging.DEBUG if verbose else logging.INFO)
formatter = logging.Formatter(fmt)
for handler in handlers:
handler.setFormatter(formatter)
logger.addHandler(handler)
for module in self.warning_level:
logging.getLogger(module).setLevel(logging.WARNING)
for module in self.error_level:
logging.getLogger(module).setLevel(logging.ERROR)
return logger