You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

225 lines
6.1 KiB

4 years ago
  1. from __future__ import absolute_import
  2. import contextlib
  3. import logging
  4. import logging.handlers
  5. import os
  6. from pip._internal.utils.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 ANSI 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
  97. def setup_logging(verbosity, no_color, user_log_file):
  98. """Configures and sets up all of the logging
  99. """
  100. # Determine the level to be logging at.
  101. if verbosity >= 1:
  102. level = "DEBUG"
  103. elif verbosity == -1:
  104. level = "WARNING"
  105. elif verbosity == -2:
  106. level = "ERROR"
  107. elif verbosity <= -3:
  108. level = "CRITICAL"
  109. else:
  110. level = "INFO"
  111. # The "root" logger should match the "console" level *unless* we also need
  112. # to log to a user log file.
  113. include_user_log = user_log_file is not None
  114. if include_user_log:
  115. additional_log_file = user_log_file
  116. root_level = "DEBUG"
  117. else:
  118. additional_log_file = "/dev/null"
  119. root_level = level
  120. # Disable any logging besides WARNING unless we have DEBUG level logging
  121. # enabled for vendored libraries.
  122. vendored_log_level = "WARNING" if level in ["INFO", "ERROR"] else "DEBUG"
  123. # Shorthands for clarity
  124. log_streams = {
  125. "stdout": "ext://sys.stdout",
  126. "stderr": "ext://sys.stderr",
  127. }
  128. handler_classes = {
  129. "stream": "pip._internal.utils.logging.ColorizedStreamHandler",
  130. "file": "pip._internal.utils.logging.BetterRotatingFileHandler",
  131. }
  132. logging.config.dictConfig({
  133. "version": 1,
  134. "disable_existing_loggers": False,
  135. "filters": {
  136. "exclude_warnings": {
  137. "()": "pip._internal.utils.logging.MaxLevelFilter",
  138. "level": logging.WARNING,
  139. },
  140. },
  141. "formatters": {
  142. "indent": {
  143. "()": IndentingFormatter,
  144. "format": "%(message)s",
  145. },
  146. },
  147. "handlers": {
  148. "console": {
  149. "level": level,
  150. "class": handler_classes["stream"],
  151. "no_color": no_color,
  152. "stream": log_streams["stdout"],
  153. "filters": ["exclude_warnings"],
  154. "formatter": "indent",
  155. },
  156. "console_errors": {
  157. "level": "WARNING",
  158. "class": handler_classes["stream"],
  159. "no_color": no_color,
  160. "stream": log_streams["stderr"],
  161. "formatter": "indent",
  162. },
  163. "user_log": {
  164. "level": "DEBUG",
  165. "class": handler_classes["file"],
  166. "filename": additional_log_file,
  167. "delay": True,
  168. "formatter": "indent",
  169. },
  170. },
  171. "root": {
  172. "level": root_level,
  173. "handlers": ["console", "console_errors"] + (
  174. ["user_log"] if include_user_log else []
  175. ),
  176. },
  177. "loggers": {
  178. "pip._vendor": {
  179. "level": vendored_log_level
  180. }
  181. },
  182. })