logger.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import logging
  2. from typing import Mapping, Optional
  3. from lightning_utilities.core.rank_zero import rank_prefixed_message, rank_zero_only
  4. class RankedLogger(logging.LoggerAdapter):
  5. """A multi-GPU-friendly python command line logger."""
  6. def __init__(
  7. self,
  8. name: str = __name__,
  9. rank_zero_only: bool = True,
  10. extra: Optional[Mapping[str, object]] = None,
  11. ) -> None:
  12. """Initializes a multi-GPU-friendly python command line logger that logs on all processes
  13. with their rank prefixed in the log message.
  14. :param name: The name of the logger. Default is ``__name__``.
  15. :param rank_zero_only: Whether to force all logs to only occur on the rank zero process. Default is `False`.
  16. :param extra: (Optional) A dict-like object which provides contextual information. See `logging.LoggerAdapter`.
  17. """
  18. logger = logging.getLogger(name)
  19. super().__init__(logger=logger, extra=extra)
  20. self.rank_zero_only = rank_zero_only
  21. def log(
  22. self, level: int, msg: str, rank: Optional[int] = None, *args, **kwargs
  23. ) -> None:
  24. """Delegate a log call to the underlying logger, after prefixing its message with the rank
  25. of the process it's being logged from. If `'rank'` is provided, then the log will only
  26. occur on that rank/process.
  27. :param level: The level to log at. Look at `logging.__init__.py` for more information.
  28. :param msg: The message to log.
  29. :param rank: The rank to log at.
  30. :param args: Additional args to pass to the underlying logging function.
  31. :param kwargs: Any additional keyword args to pass to the underlying logging function.
  32. """
  33. if self.isEnabledFor(level):
  34. msg, kwargs = self.process(msg, kwargs)
  35. current_rank = getattr(rank_zero_only, "rank", None)
  36. if current_rank is None:
  37. raise RuntimeError(
  38. "The `rank_zero_only.rank` needs to be set before use"
  39. )
  40. msg = rank_prefixed_message(msg, current_rank)
  41. if self.rank_zero_only:
  42. if current_rank == 0:
  43. self.logger.log(level, msg, *args, **kwargs)
  44. else:
  45. if rank is None:
  46. self.logger.log(level, msg, *args, **kwargs)
  47. elif current_rank == rank:
  48. self.logger.log(level, msg, *args, **kwargs)