logging.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. # Copyright (c) Microsoft Corporation.
  2. # SPDX-License-Identifier: Apache-2.0
  3. # DeepSpeed Team
  4. import functools
  5. import logging
  6. import sys
  7. import os
  8. log_levels = {
  9. "debug": logging.DEBUG,
  10. "info": logging.INFO,
  11. "warning": logging.WARNING,
  12. "error": logging.ERROR,
  13. "critical": logging.CRITICAL,
  14. }
  15. class LoggerFactory:
  16. @staticmethod
  17. def create_logger(name=None, level=logging.INFO):
  18. """create a logger
  19. Args:
  20. name (str): name of the logger
  21. level: level of logger
  22. Raises:
  23. ValueError is name is None
  24. """
  25. if name is None:
  26. raise ValueError("name for logger cannot be None")
  27. formatter = logging.Formatter("[%(asctime)s] [%(levelname)s] "
  28. "[%(filename)s:%(lineno)d:%(funcName)s] %(message)s")
  29. logger_ = logging.getLogger(name)
  30. logger_.setLevel(level)
  31. logger_.propagate = False
  32. ch = logging.StreamHandler(stream=sys.stdout)
  33. ch.setLevel(level)
  34. ch.setFormatter(formatter)
  35. logger_.addHandler(ch)
  36. return logger_
  37. logger = LoggerFactory.create_logger(name="DeepSpeed", level=logging.INFO)
  38. @functools.lru_cache(None)
  39. def warning_once(*args, **kwargs):
  40. """
  41. This method is identical to `logger.warning()`, but will emit the warning with the same message only once
  42. Note: The cache is for the function arguments, so 2 different callers using the same arguments will hit the cache.
  43. The assumption here is that all warning messages are unique across the code. If they aren't then need to switch to
  44. another type of cache that includes the caller frame information in the hashing function.
  45. """
  46. logger.warning(*args, **kwargs)
  47. logger.warning_once = warning_once
  48. def print_configuration(args, name):
  49. logger.info("{}:".format(name))
  50. for arg in sorted(vars(args)):
  51. dots = "." * (29 - len(arg))
  52. logger.info(" {} {} {}".format(arg, dots, getattr(args, arg)))
  53. def log_dist(message, ranks=None, level=logging.INFO):
  54. from deepspeed import comm as dist
  55. """Log message when one of following condition meets
  56. + not dist.is_initialized()
  57. + dist.get_rank() in ranks if ranks is not None or ranks = [-1]
  58. Args:
  59. message (str)
  60. ranks (list)
  61. level (int)
  62. """
  63. should_log = not dist.is_initialized()
  64. ranks = ranks or []
  65. my_rank = dist.get_rank() if dist.is_initialized() else -1
  66. if ranks and not should_log:
  67. should_log = ranks[0] == -1
  68. should_log = should_log or (my_rank in set(ranks))
  69. if should_log:
  70. final_message = "[Rank {}] {}".format(my_rank, message)
  71. logger.log(level, final_message)
  72. def print_json_dist(message, ranks=None, path=None):
  73. from deepspeed import comm as dist
  74. """Print message when one of following condition meets
  75. + not dist.is_initialized()
  76. + dist.get_rank() in ranks if ranks is not None or ranks = [-1]
  77. Args:
  78. message (str)
  79. ranks (list)
  80. path (str)
  81. """
  82. should_log = not dist.is_initialized()
  83. ranks = ranks or []
  84. my_rank = dist.get_rank() if dist.is_initialized() else -1
  85. if ranks and not should_log:
  86. should_log = ranks[0] == -1
  87. should_log = should_log or (my_rank in set(ranks))
  88. if should_log:
  89. message['rank'] = my_rank
  90. import json
  91. with open(path, 'w') as outfile:
  92. json.dump(message, outfile)
  93. os.fsync(outfile)
  94. def get_current_level():
  95. """
  96. Return logger's current log level
  97. """
  98. return logger.getEffectiveLevel()
  99. def should_log_le(max_log_level_str):
  100. """
  101. Args:
  102. max_log_level_str: maximum log level as a string
  103. Returns ``True`` if the current log_level is less or equal to the specified log level. Otherwise ``False``.
  104. Example:
  105. ``should_log_le("info")`` will return ``True`` if the current log level is either ``logging.INFO`` or ``logging.DEBUG``
  106. """
  107. if not isinstance(max_log_level_str, str):
  108. raise ValueError(f"{max_log_level_str} is not a string")
  109. max_log_level_str = max_log_level_str.lower()
  110. if max_log_level_str not in log_levels:
  111. raise ValueError(f"{max_log_level_str} is not one of the `logging` levels")
  112. return get_current_level() <= log_levels[max_log_level_str]