# 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)