comparison sat/core/log_config.py @ 4037:524856bd7b19

massive refactoring to switch from camelCase to snake_case: historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a pre-PEP8 code, to use the same coding style as in Twisted. However, snake_case is more readable and it's better to follow PEP8 best practices, so it has been decided to move on full snake_case. Because Libervia has a huge codebase, this ended with a ugly mix of camelCase and snake_case. To fix that, this patch does a big refactoring by renaming every function and method (including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case. This is a massive change, and may result in some bugs.
author Goffi <goffi@goffi.org>
date Sat, 08 Apr 2023 13:54:42 +0200
parents 760f563b1243
children
comparison
equal deleted inserted replaced
4036:c4464d7ae97b 4037:524856bd7b19
45 **kwargs, 45 **kwargs,
46 ) 46 )
47 47
48 48
49 class ConfigureBasic(log.ConfigureBase): 49 class ConfigureBasic(log.ConfigureBase):
50 def configureColors(self, colors, force_colors, levels_taints_dict): 50 def configure_colors(self, colors, force_colors, levels_taints_dict):
51 super(ConfigureBasic, self).configureColors( 51 super(ConfigureBasic, self).configure_colors(
52 colors, force_colors, levels_taints_dict 52 colors, force_colors, levels_taints_dict
53 ) 53 )
54 if colors: 54 if colors:
55 import sys 55 import sys
56 56
59 except AttributeError: 59 except AttributeError:
60 isatty = False 60 isatty = False
61 # FIXME: isatty should be tested on each handler, not globaly 61 # FIXME: isatty should be tested on each handler, not globaly
62 if (force_colors or isatty): 62 if (force_colors or isatty):
63 # we need colors 63 # we need colors
64 log.Logger.post_treat = lambda logger, level, message: self.ansiColors( 64 log.Logger.post_treat = lambda logger, level, message: self.ansi_colors(
65 level, message 65 level, message
66 ) 66 )
67 elif force_colors: 67 elif force_colors:
68 raise ValueError("force_colors can't be used if colors is False") 68 raise ValueError("force_colors can't be used if colors is False")
69 69
70 @staticmethod 70 @staticmethod
71 def getProfile(): 71 def get_profile():
72 """Try to find profile value using introspection""" 72 """Try to find profile value using introspection"""
73 import inspect 73 import inspect
74 74
75 stack = inspect.stack() 75 stack = inspect.stack()
76 current_path = stack[0][1] 76 current_path = stack[0][1]
105 105
106 106
107 class ConfigureTwisted(ConfigureBasic): 107 class ConfigureTwisted(ConfigureBasic):
108 LOGGER_CLASS = TwistedLogger 108 LOGGER_CLASS = TwistedLogger
109 109
110 def preTreatment(self): 110 def pre_treatment(self):
111 from twisted import logger 111 from twisted import logger
112 global logger 112 global logger
113 self.level_map = { 113 self.level_map = {
114 C.LOG_LVL_DEBUG: logger.LogLevel.debug, 114 C.LOG_LVL_DEBUG: logger.LogLevel.debug,
115 C.LOG_LVL_INFO: logger.LogLevel.info, 115 C.LOG_LVL_INFO: logger.LogLevel.info,
117 C.LOG_LVL_ERROR: logger.LogLevel.error, 117 C.LOG_LVL_ERROR: logger.LogLevel.error,
118 C.LOG_LVL_CRITICAL: logger.LogLevel.critical, 118 C.LOG_LVL_CRITICAL: logger.LogLevel.critical,
119 } 119 }
120 self.LOGGER_CLASS.level_map = self.level_map 120 self.LOGGER_CLASS.level_map = self.level_map
121 121
122 def configureLevel(self, level): 122 def configure_level(self, level):
123 self.level = self.level_map[level] 123 self.level = self.level_map[level]
124 124
125 def configureOutput(self, output): 125 def configure_output(self, output):
126 import sys 126 import sys
127 from twisted.python import logfile 127 from twisted.python import logfile
128 self.log_publisher = logger.LogPublisher() 128 self.log_publisher = logger.LogPublisher()
129 129
130 if output is None: 130 if output is None:
131 output = C.LOG_OPT_OUTPUT_SEP + C.LOG_OPT_OUTPUT_DEFAULT 131 output = C.LOG_OPT_OUTPUT_SEP + C.LOG_OPT_OUTPUT_DEFAULT
132 self.manageOutputs(output) 132 self.manage_outputs(output)
133 133
134 if C.LOG_OPT_OUTPUT_DEFAULT in log.handlers: 134 if C.LOG_OPT_OUTPUT_DEFAULT in log.handlers:
135 if self.backend_data is None: 135 if self.backend_data is None:
136 raise ValueError( 136 raise ValueError(
137 "You must pass options as backend_data with Twisted backend" 137 "You must pass options as backend_data with Twisted backend"
138 ) 138 )
139 options = self.backend_data 139 options = self.backend_data
140 log_file = logfile.LogFile.fromFullPath(options['logfile']) 140 log_file = logfile.LogFile.fromFullPath(options['logfile'])
141 self.log_publisher.addObserver( 141 self.log_publisher.addObserver(
142 logger.FileLogObserver(log_file, self.textFormatter)) 142 logger.FileLogObserver(log_file, self.text_formatter))
143 # we also want output to stdout if we are in debug or nodaemon mode 143 # we also want output to stdout if we are in debug or nodaemon mode
144 if options.get("nodaemon", False) or options.get("debug", False): 144 if options.get("nodaemon", False) or options.get("debug", False):
145 self.log_publisher.addObserver( 145 self.log_publisher.addObserver(
146 logger.FileLogObserver(sys.stdout, self.textFormatter)) 146 logger.FileLogObserver(sys.stdout, self.text_formatter))
147 147
148 if C.LOG_OPT_OUTPUT_FILE in log.handlers: 148 if C.LOG_OPT_OUTPUT_FILE in log.handlers:
149 149
150 for path in log.handlers[C.LOG_OPT_OUTPUT_FILE]: 150 for path in log.handlers[C.LOG_OPT_OUTPUT_FILE]:
151 log_file = ( 151 log_file = (
152 sys.stdout if path == "-" else logfile.LogFile.fromFullPath(path) 152 sys.stdout if path == "-" else logfile.LogFile.fromFullPath(path)
153 ) 153 )
154 self.log_publisher.addObserver( 154 self.log_publisher.addObserver(
155 logger.FileLogObserver(log_file, self.textFormatter)) 155 logger.FileLogObserver(log_file, self.text_formatter))
156 156
157 if C.LOG_OPT_OUTPUT_MEMORY in log.handlers: 157 if C.LOG_OPT_OUTPUT_MEMORY in log.handlers:
158 raise NotImplementedError( 158 raise NotImplementedError(
159 "Memory observer is not implemented in Twisted backend" 159 "Memory observer is not implemented in Twisted backend"
160 ) 160 )
161 161
162 def configureColors(self, colors, force_colors, levels_taints_dict): 162 def configure_colors(self, colors, force_colors, levels_taints_dict):
163 super(ConfigureTwisted, self).configureColors( 163 super(ConfigureTwisted, self).configure_colors(
164 colors, force_colors, levels_taints_dict 164 colors, force_colors, levels_taints_dict
165 ) 165 )
166 self.LOGGER_CLASS.colors = colors 166 self.LOGGER_CLASS.colors = colors
167 self.LOGGER_CLASS.force_colors = force_colors 167 self.LOGGER_CLASS.force_colors = force_colors
168 if force_colors and not colors: 168 if force_colors and not colors:
169 raise ValueError("colors must be True if force_colors is True") 169 raise ValueError("colors must be True if force_colors is True")
170 170
171 def postTreatment(self): 171 def post_treatment(self):
172 """Install twistedObserver which manage non SàT logs""" 172 """Install twistedObserver which manage non SàT logs"""
173 # from twisted import logger 173 # from twisted import logger
174 import sys 174 import sys
175 filtering_obs = logger.FilteringLogObserver( 175 filtering_obs = logger.FilteringLogObserver(
176 observer=self.log_publisher, 176 observer=self.log_publisher,
178 logger.LogLevelFilterPredicate(self.level), 178 logger.LogLevelFilterPredicate(self.level),
179 ] 179 ]
180 ) 180 )
181 logger.globalLogBeginner.beginLoggingTo([filtering_obs]) 181 logger.globalLogBeginner.beginLoggingTo([filtering_obs])
182 182
183 def textFormatter(self, event): 183 def text_formatter(self, event):
184 if event.get('sat_logged', False): 184 if event.get('sat_logged', False):
185 timestamp = ''.join([logger.formatTime(event.get("log_time", None)), " "]) 185 timestamp = ''.join([logger.formatTime(event.get("log_time", None)), " "])
186 return f"{timestamp}{event.get('log_format', '')}\n" 186 return f"{timestamp}{event.get('log_format', '')}\n"
187 else: 187 else:
188 eventText = logger.eventAsText( 188 eventText = logger.eventAsText(
217 levels_taints_dict, 217 levels_taints_dict,
218 force_colors, 218 force_colors,
219 backend_data, 219 backend_data,
220 ) 220 )
221 221
222 def preTreatment(self): 222 def pre_treatment(self):
223 """We use logging methods directly, instead of using Logger""" 223 """We use logging methods directly, instead of using Logger"""
224 import logging 224 import logging
225 225
226 log.getLogger = logging.getLogger 226 log.getLogger = logging.getLogger
227 log.debug = logging.debug 227 log.debug = logging.debug
228 log.info = logging.info 228 log.info = logging.info
229 log.warning = logging.warning 229 log.warning = logging.warning
230 log.error = logging.error 230 log.error = logging.error
231 log.critical = logging.critical 231 log.critical = logging.critical
232 232
233 def configureLevel(self, level): 233 def configure_level(self, level):
234 if level is None: 234 if level is None:
235 level = C.LOG_LVL_DEBUG 235 level = C.LOG_LVL_DEBUG
236 self.level = level 236 self.level = level
237 237
238 def configureFormat(self, fmt): 238 def configure_format(self, fmt):
239 super(ConfigureStandard, self).configureFormat(fmt) 239 super(ConfigureStandard, self).configure_format(fmt)
240 import logging 240 import logging
241 241
242 class SatFormatter(logging.Formatter): 242 class SatFormatter(logging.Formatter):
243 """Formatter which manage SàT specificities""" 243 """Formatter which manage SàT specificities"""
244 _format = fmt 244 _format = fmt
248 super(SatFormatter, self).__init__(self._format) 248 super(SatFormatter, self).__init__(self._format)
249 self.can_colors = can_colors 249 self.can_colors = can_colors
250 250
251 def format(self, record): 251 def format(self, record):
252 if self._with_profile: 252 if self._with_profile:
253 record.profile = ConfigureStandard.getProfile() 253 record.profile = ConfigureStandard.get_profile()
254 do_color = self.with_colors and (self.can_colors or self.force_colors) 254 do_color = self.with_colors and (self.can_colors or self.force_colors)
255 if ConfigureStandard._color_location: 255 if ConfigureStandard._color_location:
256 # we copy raw formatting strings for color_* 256 # we copy raw formatting strings for color_*
257 # as formatting is handled in ansiColors in this case 257 # as formatting is handled in ansi_colors in this case
258 if do_color: 258 if do_color:
259 record.color_start = log.COLOR_START 259 record.color_start = log.COLOR_START
260 record.color_end = log.COLOR_END 260 record.color_end = log.COLOR_END
261 else: 261 else:
262 record.color_start = record.color_end = "" 262 record.color_start = record.color_end = ""
263 s = super(SatFormatter, self).format(record) 263 s = super(SatFormatter, self).format(record)
264 if do_color: 264 if do_color:
265 s = ConfigureStandard.ansiColors(record.levelname, s) 265 s = ConfigureStandard.ansi_colors(record.levelname, s)
266 return s 266 return s
267 267
268 self.formatterClass = SatFormatter 268 self.formatterClass = SatFormatter
269 269
270 def configureOutput(self, output): 270 def configure_output(self, output):
271 self.manageOutputs(output) 271 self.manage_outputs(output)
272 272
273 def configureLogger(self, logger): 273 def configure_logger(self, logger):
274 self.name_filter = log.FilterName(logger) if logger else None 274 self.name_filter = log.FilterName(logger) if logger else None
275 275
276 def configureColors(self, colors, force_colors, levels_taints_dict): 276 def configure_colors(self, colors, force_colors, levels_taints_dict):
277 super(ConfigureStandard, self).configureColors( 277 super(ConfigureStandard, self).configure_colors(
278 colors, force_colors, levels_taints_dict 278 colors, force_colors, levels_taints_dict
279 ) 279 )
280 self.formatterClass.with_colors = colors 280 self.formatterClass.with_colors = colors
281 self.formatterClass.force_colors = force_colors 281 self.formatterClass.force_colors = force_colors
282 if not colors and force_colors: 282 if not colors and force_colors:
283 raise ValueError("force_colors can't be used if colors is False") 283 raise ValueError("force_colors can't be used if colors is False")
284 284
285 def _addHandler(self, root_logger, hdlr, can_colors=False): 285 def _add_handler(self, root_logger, hdlr, can_colors=False):
286 hdlr.setFormatter(self.formatterClass(can_colors)) 286 hdlr.setFormatter(self.formatterClass(can_colors))
287 root_logger.addHandler(hdlr) 287 root_logger.addHandler(hdlr)
288 root_logger.setLevel(self.level) 288 root_logger.setLevel(self.level)
289 if self.name_filter is not None: 289 if self.name_filter is not None:
290 hdlr.addFilter(self.name_filter) 290 hdlr.addFilter(self.name_filter)
291 291
292 def postTreatment(self): 292 def post_treatment(self):
293 import logging 293 import logging
294 294
295 root_logger = logging.getLogger() 295 root_logger = logging.getLogger()
296 if len(root_logger.handlers) == 0: 296 if len(root_logger.handlers) == 0:
297 for handler, options in list(log.handlers.items()): 297 for handler, options in list(log.handlers.items()):
299 hdlr = logging.StreamHandler() 299 hdlr = logging.StreamHandler()
300 try: 300 try:
301 can_colors = hdlr.stream.isatty() 301 can_colors = hdlr.stream.isatty()
302 except AttributeError: 302 except AttributeError:
303 can_colors = False 303 can_colors = False
304 self._addHandler(root_logger, hdlr, can_colors=can_colors) 304 self._add_handler(root_logger, hdlr, can_colors=can_colors)
305 elif handler == C.LOG_OPT_OUTPUT_MEMORY: 305 elif handler == C.LOG_OPT_OUTPUT_MEMORY:
306 from logging.handlers import BufferingHandler 306 from logging.handlers import BufferingHandler
307 307
308 class SatMemoryHandler(BufferingHandler): 308 class SatMemoryHandler(BufferingHandler):
309 def emit(self, record): 309 def emit(self, record):
313 log.handlers[ 313 log.handlers[
314 handler 314 handler
315 ] = ( 315 ] = (
316 hdlr 316 hdlr
317 ) # we keep a reference to the handler to read the buffer later 317 ) # we keep a reference to the handler to read the buffer later
318 self._addHandler(root_logger, hdlr, can_colors=False) 318 self._add_handler(root_logger, hdlr, can_colors=False)
319 elif handler == C.LOG_OPT_OUTPUT_FILE: 319 elif handler == C.LOG_OPT_OUTPUT_FILE:
320 import os.path 320 import os.path
321 321
322 for path in options: 322 for path in options:
323 hdlr = logging.FileHandler(os.path.expanduser(path)) 323 hdlr = logging.FileHandler(os.path.expanduser(path))
324 self._addHandler(root_logger, hdlr, can_colors=False) 324 self._add_handler(root_logger, hdlr, can_colors=False)
325 else: 325 else:
326 raise ValueError("Unknown handler type") 326 raise ValueError("Unknown handler type")
327 else: 327 else:
328 root_logger.warning("Handlers already set on root logger") 328 root_logger.warning("Handlers already set on root logger")
329 329
330 @staticmethod 330 @staticmethod
331 def memoryGet(size=None): 331 def memory_get(size=None):
332 """Return buffered logs 332 """Return buffered logs
333 333
334 @param size: number of logs to return 334 @param size: number of logs to return
335 """ 335 """
336 mem_handler = log.handlers[C.LOG_OPT_OUTPUT_MEMORY] 336 mem_handler = log.handlers[C.LOG_OPT_OUTPUT_MEMORY]
353 C.LOG_BACKEND_CUSTOM: use a given Logger subclass 353 C.LOG_BACKEND_CUSTOM: use a given Logger subclass
354 """ 354 """
355 return log.configure(backend, **options) 355 return log.configure(backend, **options)
356 356
357 357
358 def _parseOptions(options): 358 def _parse_options(options):
359 """Parse string options as given in conf or environment variable, and return expected python value 359 """Parse string options as given in conf or environment variable, and return expected python value
360 360
361 @param options (dict): options with (key: name, value: string value) 361 @param options (dict): options with (key: name, value: string value)
362 """ 362 """
363 COLORS = C.LOG_OPT_COLORS[0] 363 COLORS = C.LOG_OPT_COLORS[0]
376 if level not in C.LOG_LEVELS: 376 if level not in C.LOG_LEVELS:
377 level = C.LOG_LVL_INFO 377 level = C.LOG_LVL_INFO
378 options[LEVEL] = level 378 options[LEVEL] = level
379 379
380 380
381 def satConfigure(backend=C.LOG_BACKEND_STANDARD, const=None, backend_data=None): 381 def sat_configure(backend=C.LOG_BACKEND_STANDARD, const=None, backend_data=None):
382 """Configure logging system for SàT, can be used by frontends 382 """Configure logging system for SàT, can be used by frontends
383 383
384 logs conf is read in SàT conf, then in environment variables. It must be done before Memory init 384 logs conf is read in SàT conf, then in environment variables. It must be done before Memory init
385 @param backend: backend to use, it can be: 385 @param backend: backend to use, it can be:
386 - C.LOG_BACKEND_BASIC: print based backend 386 - C.LOG_BACKEND_BASIC: print based backend
394 log.C = const 394 log.C = const
395 from sat.tools import config 395 from sat.tools import config
396 import os 396 import os
397 397
398 log_conf = {} 398 log_conf = {}
399 sat_conf = config.parseMainConf() 399 sat_conf = config.parse_main_conf()
400 for opt_name, opt_default in C.LOG_OPTIONS(): 400 for opt_name, opt_default in C.LOG_OPTIONS():
401 try: 401 try:
402 log_conf[opt_name] = os.environ[ 402 log_conf[opt_name] = os.environ[
403 "".join((C.ENV_PREFIX, C.LOG_OPT_PREFIX.upper(), opt_name.upper())) 403 "".join((C.ENV_PREFIX, C.LOG_OPT_PREFIX.upper(), opt_name.upper()))
404 ] 404 ]
405 except KeyError: 405 except KeyError:
406 log_conf[opt_name] = config.getConfig( 406 log_conf[opt_name] = config.config_get(
407 sat_conf, C.LOG_OPT_SECTION, C.LOG_OPT_PREFIX + opt_name, opt_default 407 sat_conf, C.LOG_OPT_SECTION, C.LOG_OPT_PREFIX + opt_name, opt_default
408 ) 408 )
409 409
410 _parseOptions(log_conf) 410 _parse_options(log_conf)
411 configure(backend, backend_data=backend_data, **log_conf) 411 configure(backend, backend_data=backend_data, **log_conf)