logging.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from __future__ import absolute_import
  2. import contextlib
  3. import logging
  4. import logging.handlers
  5. import os
  6. from pip._internal.compat import WINDOWS
  7. from pip._internal.utils.misc import ensure_dir
  8. try:
  9. import threading
  10. except ImportError:
  11. import dummy_threading as threading # type: ignore
  12. try:
  13. from pip._vendor import colorama
  14. # Lots of different errors can come from this, including SystemError and
  15. # ImportError.
  16. except Exception:
  17. colorama = None
  18. _log_state = threading.local()
  19. _log_state.indentation = 0
  20. @contextlib.contextmanager
  21. def indent_log(num=2):
  22. """
  23. A context manager which will cause the log output to be indented for any
  24. log messages emitted inside it.
  25. """
  26. _log_state.indentation += num
  27. try:
  28. yield
  29. finally:
  30. _log_state.indentation -= num
  31. def get_indentation():
  32. return getattr(_log_state, 'indentation', 0)
  33. class IndentingFormatter(logging.Formatter):
  34. def format(self, record):
  35. """
  36. Calls the standard formatter, but will indent all of the log messages
  37. by our current indentation level.
  38. """
  39. formatted = logging.Formatter.format(self, record)
  40. formatted = "".join([
  41. (" " * get_indentation()) + line
  42. for line in formatted.splitlines(True)
  43. ])
  44. return formatted
  45. def _color_wrap(*colors):
  46. def wrapped(inp):
  47. return "".join(list(colors) + [inp, colorama.Style.RESET_ALL])
  48. return wrapped
  49. class ColorizedStreamHandler(logging.StreamHandler):
  50. # Don't build up a list of colors if we don't have colorama
  51. if colorama:
  52. COLORS = [
  53. # This needs to be in order from highest logging level to lowest.
  54. (logging.ERROR, _color_wrap(colorama.Fore.RED)),
  55. (logging.WARNING, _color_wrap(colorama.Fore.YELLOW)),
  56. ]
  57. else:
  58. COLORS = []
  59. def __init__(self, stream=None, no_color=None):
  60. logging.StreamHandler.__init__(self, stream)
  61. self._no_color = no_color
  62. if WINDOWS and colorama:
  63. self.stream = colorama.AnsiToWin32(self.stream)
  64. def should_color(self):
  65. # Don't colorize things if we do not have colorama or if told not to
  66. if not colorama or self._no_color:
  67. return False
  68. real_stream = (
  69. self.stream if not isinstance(self.stream, colorama.AnsiToWin32)
  70. else self.stream.wrapped
  71. )
  72. # If the stream is a tty we should color it
  73. if hasattr(real_stream, "isatty") and real_stream.isatty():
  74. return True
  75. # If we have an ASNI term we should color it
  76. if os.environ.get("TERM") == "ANSI":
  77. return True
  78. # If anything else we should not color it
  79. return False
  80. def format(self, record):
  81. msg = logging.StreamHandler.format(self, record)
  82. if self.should_color():
  83. for level, color in self.COLORS:
  84. if record.levelno >= level:
  85. msg = color(msg)
  86. break
  87. return msg
  88. class BetterRotatingFileHandler(logging.handlers.RotatingFileHandler):
  89. def _open(self):
  90. ensure_dir(os.path.dirname(self.baseFilename))
  91. return logging.handlers.RotatingFileHandler._open(self)
  92. class MaxLevelFilter(logging.Filter):
  93. def __init__(self, level):
  94. self.level = level
  95. def filter(self, record):
  96. return record.levelno < self.level