Hide keyboard shortcuts

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.""" 

2 

3from __future__ import absolute_import 

4 

5import logging 

6import sys 

7 

8from colorlog.escape_codes import escape_codes, parse_colors 

9 

10__all__ = ('escape_codes', 'default_log_colors', 'ColoredFormatter', 

11 'LevelFormatter', 'TTYColoredFormatter') 

12 

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} 

21 

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} 

28 

29 

30class ColoredRecord(object): 

31 """ 

32 Wraps a LogRecord, adding named escape codes to the internal dict. 

33 

34 The internal dict is used when formatting the message (by the PercentStyle, 

35 StrFormatStyle, and StringTemplateStyle classes). 

36 """ 

37 

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

42 

43 # Keep a reference to the original record so ``__getattr__`` can 

44 # access functions that are not in ``__dict__`` 

45 self.__record = record 

46 

47 def __getattr__(self, name): 

48 return getattr(self.__record, name) 

49 

50 

51class ColoredFormatter(logging.Formatter): 

52 """ 

53 A formatter that allows colors to be placed in the format string. 

54 

55 Intended to help in creating more readable logging output. 

56 """ 

57 

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. 

63 

64 The ``fmt``, ``datefmt`` and ``style`` args are passed on to the 

65 ``logging.Formatter`` constructor. 

66 

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. 

71 

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['%'] 

89 

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) 

100 

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 

105 

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, "")) 

109 

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) 

114 

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) 

120 

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) 

126 

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'] 

131 

132 return message 

133 

134 

135class LevelFormatter(ColoredFormatter): 

136 """An extension of ColoredFormatter that uses per-level format strings.""" 

137 

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. 

143 

144 Supports fmt as a dict. All other args are passed on to the 

145 ``colorlog.ColoredFormatter`` constructor. 

146 

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) 

152 

153 Example: 

154 

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 

174 

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) 

186 

187 if sys.version_info > (2, 7): 

188 message = super(LevelFormatter, self).format(record) 

189 else: 

190 message = ColoredFormatter.format(self, record) 

191 

192 return message 

193 

194 

195class TTYColoredFormatter(ColoredFormatter): 

196 """ 

197 Blanks all color codes if not running under a TTY. 

198 

199 This is useful when you want to be able to pipe colorlog output to a file. 

200 """ 

201 

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

205 

206 # Both `reset` and `isatty` must be true to insert reset codes. 

207 kwargs['reset'] = kwargs.get('reset', True) and self.stream.isatty() 

208 

209 ColoredFormatter.__init__(self, *args, **kwargs) 

210 

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)