111 lines
3.2 KiB
Python
111 lines
3.2 KiB
Python
# This module contains the logger used throughout Griffe.
|
|
# The logger is actually a wrapper around the standard Python logger.
|
|
# We wrap it so that it is easier for other downstream libraries to patch it.
|
|
# For example, mkdocstrings-python patches the logger to relocate it as a child
|
|
# of `mkdocs.plugins` so that it fits in the MkDocs logging configuration.
|
|
#
|
|
# We use a single, global logger because our public API is exposed in a single module, `griffe`.
|
|
# Extensions however should use their own logger, which is why we provide the `get_logger` function.
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from contextlib import contextmanager
|
|
from typing import TYPE_CHECKING, Any, Callable, ClassVar
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Iterator
|
|
|
|
|
|
class Logger:
|
|
_default_logger: Any = logging.getLogger
|
|
_instances: ClassVar[dict[str, Logger]] = {}
|
|
|
|
def __init__(self, name: str) -> None:
|
|
# Default logger that can be patched by third-party.
|
|
self._logger = self.__class__._default_logger(name)
|
|
|
|
def __getattr__(self, name: str) -> Any:
|
|
# Forward everything to the logger.
|
|
return getattr(self._logger, name)
|
|
|
|
@contextmanager
|
|
def disable(self) -> Iterator[None]:
|
|
"""Temporarily disable logging."""
|
|
old_level = self._logger.level
|
|
self._logger.setLevel(100)
|
|
try:
|
|
yield
|
|
finally:
|
|
self._logger.setLevel(old_level)
|
|
|
|
@classmethod
|
|
def _get(cls, name: str = "griffe") -> Logger:
|
|
if name not in cls._instances:
|
|
cls._instances[name] = cls(name)
|
|
return cls._instances[name]
|
|
|
|
@classmethod
|
|
def _patch_loggers(cls, get_logger_func: Callable) -> None:
|
|
# Patch current instances.
|
|
for name, instance in cls._instances.items():
|
|
instance._logger = get_logger_func(name)
|
|
|
|
# Future instances will be patched as well.
|
|
cls._default_logger = get_logger_func
|
|
|
|
|
|
logger: Logger = Logger._get()
|
|
"""Our global logger, used throughout the library.
|
|
|
|
Griffe's output and error messages are logging messages.
|
|
|
|
Griffe provides the [`patch_loggers`][griffe.patch_loggers]
|
|
function so dependent libraries can patch Griffe loggers as they see fit.
|
|
|
|
For example, to fit in the MkDocs logging configuration
|
|
and prefix each log message with the module name:
|
|
|
|
```python
|
|
import logging
|
|
from griffe import patch_loggers
|
|
|
|
|
|
class LoggerAdapter(logging.LoggerAdapter):
|
|
def __init__(self, prefix, logger):
|
|
super().__init__(logger, {})
|
|
self.prefix = prefix
|
|
|
|
def process(self, msg, kwargs):
|
|
return f"{self.prefix}: {msg}", kwargs
|
|
|
|
|
|
def get_logger(name):
|
|
logger = logging.getLogger(f"mkdocs.plugins.{name}")
|
|
return LoggerAdapter(name, logger)
|
|
|
|
|
|
patch_loggers(get_logger)
|
|
```
|
|
"""
|
|
|
|
|
|
def get_logger(name: str = "griffe") -> Logger:
|
|
"""Create and return a new logger instance.
|
|
|
|
Parameters:
|
|
name: The logger name.
|
|
|
|
Returns:
|
|
The logger.
|
|
"""
|
|
return Logger._get(name)
|
|
|
|
|
|
def patch_loggers(get_logger_func: Callable[[str], Any]) -> None:
|
|
"""Patch Griffe logger and Griffe extensions' loggers.
|
|
|
|
Parameters:
|
|
get_logger_func: A function accepting a name as parameter and returning a logger.
|
|
"""
|
|
Logger._patch_loggers(get_logger_func)
|