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)