Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/colorlog/colorlog.py : 28%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""The ColoredFormatter class."""
3from __future__ import absolute_import
5import logging
6import sys
8from colorlog.escape_codes import escape_codes, parse_colors
10__all__ = ('escape_codes', 'default_log_colors', 'ColoredFormatter',
11 'LevelFormatter', 'TTYColoredFormatter')
13# The default colors to use for the debug levels
14default_log_colors = {
15 'DEBUG': 'white',
16 'INFO': 'green',
17 'WARNING': 'yellow',
18 'ERROR': 'red',
19 'CRITICAL': 'bold_red',
20}
22# The default format to use for each style
23default_formats = {
24 '%': '%(log_color)s%(levelname)s:%(name)s:%(message)s',
25 '{': '{log_color}{levelname}:{name}:{message}',
26 '$': '${log_color}${levelname}:${name}:${message}'
27}
30class ColoredRecord(object):
31 """
32 Wraps a LogRecord, adding named escape codes to the internal dict.
34 The internal dict is used when formatting the message (by the PercentStyle,
35 StrFormatStyle, and StringTemplateStyle classes).
36 """
38 def __init__(self, record):
39 """Add attributes from the escape_codes dict and the record."""
40 self.__dict__.update(escape_codes)
41 self.__dict__.update(record.__dict__)
43 # Keep a reference to the original record so ``__getattr__`` can
44 # access functions that are not in ``__dict__``
45 self.__record = record
47 def __getattr__(self, name):
48 return getattr(self.__record, name)
51class ColoredFormatter(logging.Formatter):
52 """
53 A formatter that allows colors to be placed in the format string.
55 Intended to help in creating more readable logging output.
56 """
58 def __init__(self, fmt=None, datefmt=None, style='%',
59 log_colors=None, reset=True,
60 secondary_log_colors=None):
61 """
62 Set the format and colors the ColoredFormatter will use.
64 The ``fmt``, ``datefmt`` and ``style`` args are passed on to the
65 ``logging.Formatter`` constructor.
67 The ``secondary_log_colors`` argument can be used to create additional
68 ``log_color`` attributes. Each key in the dictionary will set
69 ``{key}_log_color``, using the value to select from a different
70 ``log_colors`` set.
72 :Parameters:
73 - fmt (str): The format string to use
74 - datefmt (str): A format string for the date
75 - log_colors (dict):
76 A mapping of log level names to color names
77 - reset (bool):
78 Implicitly append a color reset to all records unless False
79 - style ('%' or '{' or '$'):
80 The format style to use. (*No meaning prior to Python 3.2.*)
81 - secondary_log_colors (dict):
82 Map secondary ``log_color`` attributes. (*New in version 2.6.*)
83 """
84 if fmt is None:
85 if sys.version_info > (3, 2):
86 fmt = default_formats[style]
87 else:
88 fmt = default_formats['%']
90 if sys.version_info > (3, 8) and isinstance(self, LevelFormatter) \
91 and isinstance(fmt, dict):
92 super(ColoredFormatter, self).__init__(
93 fmt, datefmt, style, validate=False)
94 elif sys.version_info > (3, 2):
95 super(ColoredFormatter, self).__init__(fmt, datefmt, style)
96 elif sys.version_info > (2, 7):
97 super(ColoredFormatter, self).__init__(fmt, datefmt)
98 else:
99 logging.Formatter.__init__(self, fmt, datefmt)
101 self.log_colors = (
102 log_colors if log_colors is not None else default_log_colors)
103 self.secondary_log_colors = secondary_log_colors
104 self.reset = reset
106 def color(self, log_colors, level_name):
107 """Return escape codes from a ``log_colors`` dict."""
108 return parse_colors(log_colors.get(level_name, ""))
110 def format(self, record):
111 """Format a message from a record object."""
112 record = ColoredRecord(record)
113 record.log_color = self.color(self.log_colors, record.levelname)
115 # Set secondary log colors
116 if self.secondary_log_colors:
117 for name, log_colors in self.secondary_log_colors.items():
118 color = self.color(log_colors, record.levelname)
119 setattr(record, name + '_log_color', color)
121 # Format the message
122 if sys.version_info > (2, 7):
123 message = super(ColoredFormatter, self).format(record)
124 else:
125 message = logging.Formatter.format(self, record)
127 # Add a reset code to the end of the message
128 # (if it wasn't explicitly added in format str)
129 if self.reset and not message.endswith(escape_codes['reset']):
130 message += escape_codes['reset']
132 return message
135class LevelFormatter(ColoredFormatter):
136 """An extension of ColoredFormatter that uses per-level format strings."""
138 def __init__(self, fmt=None, datefmt=None, style='%',
139 log_colors=None, reset=True,
140 secondary_log_colors=None):
141 """
142 Set the per-loglevel format that will be used.
144 Supports fmt as a dict. All other args are passed on to the
145 ``colorlog.ColoredFormatter`` constructor.
147 :Parameters:
148 - fmt (dict):
149 A mapping of log levels (represented as strings, e.g. 'WARNING') to
150 different formatters. (*New in version 2.7.0)
151 (All other parameters are the same as in colorlog.ColoredFormatter)
153 Example:
155 formatter = colorlog.LevelFormatter(fmt={
156 'DEBUG':'%(log_color)s%(msg)s (%(module)s:%(lineno)d)',
157 'INFO': '%(log_color)s%(msg)s',
158 'WARNING': '%(log_color)sWARN: %(msg)s (%(module)s:%(lineno)d)',
159 'ERROR': '%(log_color)sERROR: %(msg)s (%(module)s:%(lineno)d)',
160 'CRITICAL': '%(log_color)sCRIT: %(msg)s (%(module)s:%(lineno)d)',
161 })
162 """
163 if sys.version_info > (2, 7):
164 super(LevelFormatter, self).__init__(
165 fmt=fmt, datefmt=datefmt, style=style, log_colors=log_colors,
166 reset=reset, secondary_log_colors=secondary_log_colors)
167 else:
168 ColoredFormatter.__init__(
169 self, fmt=fmt, datefmt=datefmt, style=style,
170 log_colors=log_colors, reset=reset,
171 secondary_log_colors=secondary_log_colors)
172 self.style = style
173 self.fmt = fmt
175 def format(self, record):
176 """Customize the message format based on the log level."""
177 if isinstance(self.fmt, dict):
178 self._fmt = self.fmt[record.levelname]
179 if sys.version_info > (3, 2):
180 # Update self._style because we've changed self._fmt
181 # (code based on stdlib's logging.Formatter.__init__())
182 if self.style not in logging._STYLES:
183 raise ValueError('Style must be one of: %s' % ','.join(
184 logging._STYLES.keys()))
185 self._style = logging._STYLES[self.style][0](self._fmt)
187 if sys.version_info > (2, 7):
188 message = super(LevelFormatter, self).format(record)
189 else:
190 message = ColoredFormatter.format(self, record)
192 return message
195class TTYColoredFormatter(ColoredFormatter):
196 """
197 Blanks all color codes if not running under a TTY.
199 This is useful when you want to be able to pipe colorlog output to a file.
200 """
202 def __init__(self, *args, **kwargs):
203 """Overwrite the `reset` argument to False if stream is not a TTY."""
204 self.stream = kwargs.pop('stream')
206 # Both `reset` and `isatty` must be true to insert reset codes.
207 kwargs['reset'] = kwargs.get('reset', True) and self.stream.isatty()
209 ColoredFormatter.__init__(self, *args, **kwargs)
211 def color(self, log_colors, level_name):
212 """Only returns colors if STDOUT is a TTY."""
213 if not self.stream.isatty():
214 log_colors = {}
215 return ColoredFormatter.color(self, log_colors, level_name)