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