Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/colorlog/formatter.py: 54%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""The ColoredFormatter class."""
3import logging
4import os
5import sys
6import typing
8import colorlog.escape_codes
10__all__ = (
11 "default_log_colors",
12 "ColoredFormatter",
13 "LevelFormatter",
14 "TTYColoredFormatter",
15)
17# Type aliases used in function signatures.
18EscapeCodes = typing.Mapping[str, str]
19LogColors = typing.Mapping[str, str]
20SecondaryLogColors = typing.Mapping[str, LogColors]
21if sys.version_info >= (3, 8):
22 _FormatStyle = typing.Literal["%", "{", "$"]
23else:
24 _FormatStyle = str
26# The default colors to use for the debug levels
27default_log_colors = {
28 "DEBUG": "white",
29 "INFO": "green",
30 "WARNING": "yellow",
31 "ERROR": "red",
32 "CRITICAL": "bold_red",
33}
35# The default format to use for each style
36default_formats = {
37 "%": "%(log_color)s%(levelname)s:%(name)s:%(message)s",
38 "{": "{log_color}{levelname}:{name}:{message}",
39 "$": "${log_color}${levelname}:${name}:${message}",
40}
43class ColoredRecord:
44 """
45 Wraps a LogRecord, adding escape codes to the internal dict.
47 The internal dict is used when formatting the message (by the PercentStyle,
48 StrFormatStyle, and StringTemplateStyle classes).
49 """
51 def __init__(self, record: logging.LogRecord, escapes: EscapeCodes) -> None:
52 self.__dict__.update(record.__dict__)
53 self.__dict__.update(escapes)
56class ColoredFormatter(logging.Formatter):
57 """
58 A formatter that allows colors to be placed in the format string.
60 Intended to help in creating more readable logging output.
61 """
63 def __init__(
64 self,
65 fmt: typing.Optional[str] = None,
66 datefmt: typing.Optional[str] = None,
67 style: _FormatStyle = "%",
68 log_colors: typing.Optional[LogColors] = None,
69 reset: bool = True,
70 secondary_log_colors: typing.Optional[SecondaryLogColors] = None,
71 validate: bool = True,
72 stream: typing.Optional[typing.IO] = None,
73 no_color: bool = False,
74 force_color: bool = False,
75 defaults: typing.Optional[typing.Mapping[str, typing.Any]] = None,
76 ) -> None:
77 """
78 Set the format and colors the ColoredFormatter will use.
80 The ``fmt``, ``datefmt``, ``style``, and ``default`` args are passed on to the
81 ``logging.Formatter`` constructor.
83 The ``secondary_log_colors`` argument can be used to create additional
84 ``log_color`` attributes. Each key in the dictionary will set
85 ``{key}_log_color``, using the value to select from a different
86 ``log_colors`` set.
88 :Parameters:
89 - fmt (str): The format string to use.
90 - datefmt (str): A format string for the date.
91 - log_colors (dict):
92 A mapping of log level names to color names.
93 - reset (bool):
94 Implicitly append a color reset to all records unless False.
95 - style ('%' or '{' or '$'):
96 The format style to use.
97 - secondary_log_colors (dict):
98 Map secondary ``log_color`` attributes. (*New in version 2.6.*)
99 - validate (bool)
100 Validate the format string.
101 - stream (typing.IO)
102 The stream formatted messages will be printed to. Used to toggle colour
103 on non-TTY outputs. Optional.
104 - no_color (bool):
105 Disable color output.
106 - force_color (bool):
107 Enable color output. Takes precedence over `no_color`.
108 """
110 # Select a default format if `fmt` is not provided.
111 fmt = default_formats[style] if fmt is None else fmt
113 if sys.version_info >= (3, 10):
114 super().__init__(fmt, datefmt, style, validate, defaults=defaults)
115 elif sys.version_info >= (3, 8):
116 super().__init__(fmt, datefmt, style, validate)
117 else:
118 super().__init__(fmt, datefmt, style)
120 self.log_colors = log_colors if log_colors is not None else default_log_colors
121 self.secondary_log_colors = (
122 secondary_log_colors if secondary_log_colors is not None else {}
123 )
124 self.reset = reset
125 self.stream = stream
126 self.no_color = no_color
127 self.force_color = force_color
129 def formatMessage(self, record: logging.LogRecord) -> str:
130 """Format a message from a record object."""
131 escapes = self._escape_code_map(record.levelname)
132 wrapper = ColoredRecord(record, escapes)
133 message = super().formatMessage(wrapper) # type: ignore
134 message = self._append_reset(message, escapes)
135 return message
137 def _escape_code_map(self, item: str) -> EscapeCodes:
138 """
139 Build a map of keys to escape codes for use in message formatting.
141 If _blank_escape_codes() returns True, all values will be an empty string.
142 """
143 codes = {**colorlog.escape_codes.escape_codes}
144 codes.setdefault("log_color", self._get_escape_code(self.log_colors, item))
145 for name, colors in self.secondary_log_colors.items():
146 codes.setdefault("%s_log_color" % name, self._get_escape_code(colors, item))
147 if self._blank_escape_codes():
148 codes = {key: "" for key in codes.keys()}
149 return codes
151 def _blank_escape_codes(self):
152 """Return True if we should be prevented from printing escape codes."""
153 if self.force_color or "FORCE_COLOR" in os.environ:
154 return False
156 if self.no_color or "NO_COLOR" in os.environ:
157 return True
159 if self.stream is not None and not self.stream.isatty():
160 return True
162 return False
164 @staticmethod
165 def _get_escape_code(log_colors: LogColors, item: str) -> str:
166 """Extract a color sequence from a mapping, and return escape codes."""
167 return colorlog.escape_codes.parse_colors(log_colors.get(item, ""))
169 def _append_reset(self, message: str, escapes: EscapeCodes) -> str:
170 """Add a reset code to the end of the message, if it's not already there."""
171 reset_escape_code = escapes["reset"]
173 if self.reset and not message.endswith(reset_escape_code):
174 message += reset_escape_code
176 return message
179class LevelFormatter:
180 """An extension of ColoredFormatter that uses per-level format strings."""
182 def __init__(self, fmt: typing.Mapping[str, str], **kwargs: typing.Any) -> None:
183 """
184 Configure a ColoredFormatter with its own format string for each log level.
186 Supports fmt as a dict. All other args are passed on to the
187 ``colorlog.ColoredFormatter`` constructor.
189 :Parameters:
190 - fmt (dict):
191 A mapping of log levels (represented as strings, e.g. 'WARNING') to
192 format strings. (*New in version 2.7.0)
193 (All other parameters are the same as in colorlog.ColoredFormatter)
195 Example:
197 formatter = colorlog.LevelFormatter(
198 fmt={
199 "DEBUG": "%(log_color)s%(message)s (%(module)s:%(lineno)d)",
200 "INFO": "%(log_color)s%(message)s",
201 "WARNING": "%(log_color)sWRN: %(message)s (%(module)s:%(lineno)d)",
202 "ERROR": "%(log_color)sERR: %(message)s (%(module)s:%(lineno)d)",
203 "CRITICAL": "%(log_color)sCRT: %(message)s (%(module)s:%(lineno)d)",
204 }
205 )
206 """
207 self.formatters = {
208 level: ColoredFormatter(fmt=f, **kwargs) for level, f in fmt.items()
209 }
211 def format(self, record: logging.LogRecord) -> str:
212 return self.formatters[record.levelname].format(record)
215# Provided for backwards compatibility. The features provided by this subclass are now
216# included directly in the `ColoredFormatter` class.
217TTYColoredFormatter = ColoredFormatter