1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140

build / android / pylib / utils / logging_utils.py [blame]

# Copyright 2014 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import contextlib
import logging
import os

from pylib.constants import host_paths

_COLORAMA_PATH = os.path.join(
    host_paths.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src')

with host_paths.SysPath(_COLORAMA_PATH, position=0):
  import colorama

BACK = colorama.Back
FORE = colorama.Fore
STYLE = colorama.Style


class _ColorFormatter(logging.Formatter):
  # pylint does not see members added dynamically in the constructor.
  # pylint: disable=no-member
  color_map = {
    logging.DEBUG: (FORE.CYAN),
    logging.INFO: (),  # Use default style.
    logging.WARNING: (FORE.YELLOW),
    logging.ERROR: (FORE.RED),
    logging.CRITICAL: (BACK.RED),
  }

  def __init__(self, wrapped_formatter=None):
    """Wraps a |logging.Formatter| and adds color."""
    super().__init__()
    self._wrapped_formatter = wrapped_formatter or logging.Formatter()

  #override
  def format(self, record):
    message = self._wrapped_formatter.format(record)
    return self.Colorize(message, record.levelno)

  def Colorize(self, message, log_level):
    try:
      return (''.join(self.color_map[log_level]) + message +
              colorama.Style.RESET_ALL)
    except KeyError:
      return message


class ColorStreamHandler(logging.StreamHandler):
  """Handler that can be used to colorize logging output.

  Example using a specific logger:

    logger = logging.getLogger('my_logger')
    logger.addHandler(ColorStreamHandler())
    logger.info('message')

  Example using the root logger:

    ColorStreamHandler.MakeDefault()
    logging.info('message')

  """
  def __init__(self, force_color=False):
    super().__init__()
    self.force_color = force_color
    self.setFormatter(logging.Formatter())

  @property
  def is_tty(self):
    try:
      isatty = getattr(self.stream, 'isatty')
    except AttributeError:
      return False
    return isatty()

  #override
  def setFormatter(self, fmt):
    if self.force_color or self.is_tty:
      fmt = _ColorFormatter(fmt)
    super().setFormatter(fmt)

  @staticmethod
  def MakeDefault(force_color=False):
    """
     Replaces the default logging handlers with a coloring handler. To use
     a colorizing handler at the same time as others, either register them
     after this call, or add the ColorStreamHandler on the logger using
     Logger.addHandler()

     Args:
       force_color: Set to True to bypass the tty check and always colorize.
     """
    # If the existing handlers aren't removed, messages are duplicated
    logging.getLogger().handlers = []
    logging.getLogger().addHandler(ColorStreamHandler(force_color))


@contextlib.contextmanager
def OverrideColor(level, color):
  """Temporarily override the logging color for a specified level.

  Args:
    level: logging level whose color gets overridden.
    color: tuple of formats to apply to log lines.
  """
  prev_colors = {}
  for handler in logging.getLogger().handlers:
    if isinstance(handler.formatter, _ColorFormatter):
      prev_colors[handler.formatter] = handler.formatter.color_map[level]
      handler.formatter.color_map[level] = color
  try:
    yield
  finally:
    for formatter, prev_color in prev_colors.items():
      formatter.color_map[level] = prev_color


@contextlib.contextmanager
def SuppressLogging(level=logging.ERROR):
  """Momentarilly suppress logging events from all loggers.

  TODO(jbudorick): This is not thread safe. Log events from other threads might
  also inadvertently disappear.

  Example:

    with logging_utils.SuppressLogging():
      # all but CRITICAL logging messages are suppressed
      logging.info('just doing some thing') # not shown
      logging.critical('something really bad happened') # still shown

  Args:
    level: logging events with this or lower levels are suppressed.
  """
  logging.disable(level)
  yield
  logging.disable(logging.NOTSET)