comparison src/core/log.py @ 1007:a7d33c7a8277

core (log): refactoring + twisted backend: - configuration is now done with classes, so inheritance is possible - twisted backend now use twisted log methods - log not produced by SàT directly in twisted backend are captured by an observer (twistedObserver) and sent back to SàT logging chain, with the "twisted" logger. - \\default output use normal twistd output, except in debug mode where it add an stdout output in addition to the log file. - when log_colors = True, colors are enabled only where it's possible (tty out), when log_colors=force colors are always enabled. - fixed some bad log.xxx calls
author Goffi <goffi@goffi.org>
date Mon, 05 May 2014 18:58:34 +0200
parents 325fd230c15d
children d70d4fe5c5f8
comparison
equal deleted inserted replaced
1006:325fd230c15d 1007:a7d33c7a8277
25 25
26 _backend = None 26 _backend = None
27 _loggers = {} 27 _loggers = {}
28 _handlers = {} 28 _handlers = {}
29 29
30
30 class Filtered(Exception): 31 class Filtered(Exception):
31 pass 32 pass
32 33
34
33 class Logger(object): 35 class Logger(object):
34 """High level logging class""" 36 """High level logging class"""
35 fmt = None 37 fmt = None # format option as given by user (e.g. SAT_LOG_LOGGER)
36 filter_name = None 38 filter_name = None # filter to call
37 post_treat = None 39 post_treat = None
38 40
39 def __init__(self, name): 41 def __init__(self, name):
40 self._name = name 42 if isinstance(name, Logger):
43 self.copy(name)
44 else:
45 self._name = name
46
47 def copy(self, other):
48 """Copy values from other Logger"""
49 self.fmt = other.fmt
50 self.Filter_name = other.fmt
51 self.post_treat = other.post_treat
52 self._name = other._name
53
54 def out(self, message, level=None):
55 """Actually log the message
56
57 @param message: formatted message
58 """
59 print message
41 60
42 def log(self, level, message): 61 def log(self, level, message):
43 """Print message 62 """Print message
44 63
45 @param level: one of C.LOG_LEVELS 64 @param level: one of C.LOG_LEVELS
46 @param message: message to format and print 65 @param message: message to format and print
47 """ 66 """
48 try: 67 try:
49 formatted = self.format(level, message) 68 formatted = self.format(level, message)
50 if self.post_treat is None: 69 if self.post_treat is None:
51 print formatted 70 self.out(formatted, level)
52 else: 71 else:
53 print self.post_treat(level, formatted) 72 self.out(self.post_treat(level, formatted), level)
54 except Filtered: 73 except Filtered:
55 pass 74 pass
56 75
57 def format(self, level, message): 76 def format(self, level, message):
58 """Format message according to Logger.fmt 77 """Format message according to Logger.fmt
59 78
60 @param level: one of C.LOG_LEVELS 79 @param level: one of C.LOG_LEVELS
61 @param message: message to format 80 @param message: message to format
62 @return: formatted message 81 @return: formatted message
63 82
64 @raise: Filtered the message must not be logged 83 @raise: Filtered when the message must not be logged
65 """ 84 """
66 if self.fmt is None and self.filter_name is None: 85 if self.fmt is None and self.filter_name is None:
67 return message 86 return message
68 record = {'name': self._name, 87 record = {'name': self._name,
69 'message': message, 88 'message': message,
73 if not self.filter_name.dictFilter(record): 92 if not self.filter_name.dictFilter(record):
74 raise Filtered 93 raise Filtered
75 except AttributeError: 94 except AttributeError:
76 if self.filter_name is not None: 95 if self.filter_name is not None:
77 raise ValueError("Bad filter: filters must have a .filter method") 96 raise ValueError("Bad filter: filters must have a .filter method")
78 return self.fmt % record 97 return self.fmt % record if self.fmt is not None else message
79 98
80 def debug(self, msg): 99 def debug(self, msg):
81 self.log(C.LOG_LVL_DEBUG, msg) 100 self.log(C.LOG_LVL_DEBUG, msg)
82 101
83 def info(self, msg): 102 def info(self, msg):
89 def error(self, msg): 108 def error(self, msg):
90 self.log(C.LOG_LVL_ERROR, msg) 109 self.log(C.LOG_LVL_ERROR, msg)
91 110
92 def critical(self, msg): 111 def critical(self, msg):
93 self.log(C.LOG_LVL_CRITICAL, msg) 112 self.log(C.LOG_LVL_CRITICAL, msg)
113
114
115 class TwistedLogger(Logger):
116 colors = True
117 force_colors = False
118
119 def __init__(self, *args, **kwargs):
120 super(TwistedLogger, self).__init__(*args, **kwargs)
121 from twisted.python import log
122 self.twisted_log = log
123
124 def out(self, message, level=None):
125 """Actually log the message
126
127 @param message: formatted message
128 """
129 self.twisted_log.msg(message.encode('utf-8', 'ignore'), sat_logged=True, level=level)
94 130
95 131
96 class FilterName(object): 132 class FilterName(object):
97 """Filter on logger name according to a regex""" 133 """Filter on logger name according to a regex"""
98 134
150 C.ANSI_RESET) 186 C.ANSI_RESET)
151 else: 187 else:
152 out = message 188 out = message
153 return ''.join(out) 189 return ''.join(out)
154 190
155 def _manageOutputs(outputs_raw): 191
156 """ Parse output option in a backend agnostic way, and fill _handlers consequently 192 class Configure(object):
157 193 LOGGER_CLASS = None
158 @param outputs_raw: output option as enterred in environment variable or in configuration 194
159 """ 195 def __init__(self, level=None, fmt=None, output=None, logger=None, colors=False, force_colors=False):
160 if not outputs_raw: 196 """Configure backend
161 return 197 @param level: one of C.LOG_LEVELS
162 outputs = outputs_raw.split(C.LOG_OPT_OUTPUT_SEP) 198 @param fmt: format string, pretty much as in std logging. Accept the following keywords (maybe more depending on backend):
163 global _handlers 199 - "message"
164 if len(outputs) == 1: 200 - "levelname"
165 _handlers[C.LOG_OPT_OUTPUT_FILE] = outputs.pop() 201 - "name" (logger name)
166 202 @param logger: if set, use it as a regular expression to filter on logger name.
167 for output in outputs: 203 Use search to match expression, so ^ or $ can be necessary.
168 if not output: 204 @param colors: if True use ANSI colors to show log levels
169 continue 205 @param force_colors: if True ANSI colors are used even if stdout is not a tty
170 if output[-1] == ')': 206 """
171 # we have options 207 self.preTreatment()
172 opt_begin = output.rfind('(') 208 self.configureLevel(level)
173 options = output[opt_begin+1:-1] 209 self.configureFormat(fmt)
174 output = output[:opt_begin] 210 self.configureOutput(output)
211 self.configureLogger(logger)
212 self.configureColors(colors, force_colors)
213 self.postTreatment()
214 self.updateCurrentLogger()
215
216 def updateCurrentLogger(self):
217 """update existing logger to the class needed for this backend"""
218 if self.LOGGER_CLASS is None:
219 return
220 for name, logger in _loggers.items():
221 _loggers[name] = self.LOGGER_CLASS(logger)
222
223 def preTreatment(self):
224 pass
225
226 def configureLevel(self, level):
227 pass
228
229 def configureFormat(self, fmt):
230 pass
231
232 def configureOutput(self, output):
233 pass
234
235 def configureLogger(self, logger):
236 pass
237
238 def configureColors(self, colors, force_colors):
239 pass
240
241 def postTreatment(self):
242 pass
243
244 def manageOutputs(self, outputs_raw):
245 """ Parse output option in a backend agnostic way, and fill _handlers consequently
246
247 @param outputs_raw: output option as enterred in environment variable or in configuration
248 """
249 if not outputs_raw:
250 return
251 outputs = outputs_raw.split(C.LOG_OPT_OUTPUT_SEP)
252 global _handlers
253 if len(outputs) == 1:
254 _handlers[C.LOG_OPT_OUTPUT_FILE] = [outputs.pop()]
255
256 for output in outputs:
257 if not output:
258 continue
259 if output[-1] == ')':
260 # we have options
261 opt_begin = output.rfind('(')
262 options = output[opt_begin+1:-1]
263 output = output[:opt_begin]
264 else:
265 options = None
266
267 if output not in (C.LOG_OPT_OUTPUT_DEFAULT, C.LOG_OPT_OUTPUT_FILE, C.LOG_OPT_OUTPUT_MEMORY):
268 raise ValueError(u"Invalid output [%s]" % output)
269
270 if output == C.LOG_OPT_OUTPUT_DEFAULT:
271 # no option for defaut handler
272 _handlers[output] = None
273 elif output == C.LOG_OPT_OUTPUT_FILE:
274 if not options:
275 ValueError("%(handler)s output need a path as option" % {'handler': output})
276 _handlers.setdefault(output, []).append(options)
277 options = None # option are parsed, we can empty them
278 elif output == C.LOG_OPT_OUTPUT_MEMORY:
279 # we have memory handler, option can be the len limit or None
280 try:
281 limit = int(options)
282 options = None # option are parsed, we can empty them
283 except (TypeError, ValueError):
284 limit = C.LOG_OPT_OUTPUT_MEMORY_LIMIT
285 _handlers[output] = limit
286
287 if options: # we should not have unparsed options
288 raise ValueError(u"options [%(options)s] are not supported for %(handler)s output" % {'options': options, 'handler': output})
289
290
291 class ConfigureBasic(Configure):
292 def configureLevel(self, level):
293 if level is not None:
294 # we deactivate methods below level
295 level_idx = C.LOG_LEVELS.index(level)
296 def dev_null(self, msg):
297 pass
298 for _level in C.LOG_LEVELS[:level_idx]:
299 setattr(Logger, _level.lower(), dev_null)
300
301 def configureFormat(self, fmt):
302 if fmt is not None:
303 if fmt != '%(message)s': # %(message)s is the same as None
304 Logger.fmt = fmt
305
306 def configureOutput(self, output):
307 if output is not None:
308 if output != C.LOG_OPT_OUTPUT_SEP + C.LOG_OPT_OUTPUT_DEFAULT:
309 # TODO: manage other outputs
310 raise NotImplementedError("Basic backend only manage default output yet")
311
312 def configureLogger(self, logger):
313 if logger:
314 Logger.filter_name = FilterName(logger)
315
316 def configureColors(self, colors, force_colors):
317 if colors:
318 import sys
319 if force_colors or sys.stdout.isatty(): # FIXME: isatty should be tested on each handler, not globaly
320 # we need colors
321 Logger.post_treat = lambda self, level, message: _ansiColors(level, message)
322 elif force_colors:
323 raise ValueError("force_colors can't be used if colors is False")
324
325
326 class ConfigureTwisted(ConfigureBasic):
327 LOGGER_CLASS = TwistedLogger
328
329 def changeObserver(self, observer, can_color=False):
330 """Install a hook on observer to manage SàT specificities
331
332 @param observer: original observer to hook
333 @param can_color: True if observer can display ansi colors
334 """
335 def observer_hook(event):
336 """redirect non SàT log to twisted_logger, and add colors when possible"""
337 if 'sat_logged' in event: # we only want our own logs, other are managed by twistedObserver
338 # we add colors if possible
339 if (can_color and self.LOGGER_CLASS.colors) or self.LOGGER_CLASS.force_colors:
340 message = event.get('message', tuple())
341 level = event.get('level', C.LOG_LVL_INFO)
342 if message:
343 event['message'] = (_ansiColors(level, ''.join(message)),) # must be a tuple
344 observer(event) # we can now call the original observer
345
346 return observer_hook
347
348 def changeFileLogObserver(self, observer):
349 """Install SàT hook for FileLogObserver
350
351 if the output is a tty, we allow colors, else we don't
352 @param observer: original observer to hook
353 """
354 log_obs = observer.__self__
355 log_file = log_obs.write.__self__
356 try:
357 can_color = log_file.isatty()
358 except AttributeError:
359 can_color = False
360 return self.changeObserver(observer, can_color=can_color)
361
362 def installObserverHook(self, observer):
363 """Check observer type and install SàT hook when possible
364
365 @param observer: observer to hook
366 @return: hooked observer or original one
367 """
368 if hasattr(observer, '__self__'):
369 ori = observer
370 if isinstance(observer.__self__, self.log.FileLogObserver):
371 observer = self.changeFileLogObserver(observer)
372 elif isinstance(observer.__self__, self.log.DefaultObserver):
373 observer = self.changeObserver(observer, can_color=True)
374 else:
375 # we use print because log system is not fully initialized
376 print("Unmanaged observer [%s]" % observer)
377 return observer
378 self.observers[ori] = observer
379 return observer
380
381 def preTreatment(self):
382 """initialise needed attributes, and install observers hooks"""
383 self.observers = {}
384 from twisted.python import log
385 self.log = log
386 self.log_publisher = log.msg.__self__
387 def addObserverObserver(self_logpub, other):
388 """Install hook so we know when a new observer is added"""
389 other = self.installObserverHook(other)
390 return self_logpub._originalAddObserver(other)
391 def removeObserverObserver(self_logpub, ori):
392 """removeObserver hook fix
393
394 As we wrap the original observer, the original removeObserver may want to remove the original object instead of the wrapper, this method fix this
395 """
396 if ori in self.observers:
397 self_logpub._originalRemoveObserver(self.observers[ori])
398 else:
399 try:
400 self_logpub._originalRemoveObserver(ori)
401 except ValueError:
402 try:
403 ori in self.cleared_observers
404 except AttributeError:
405 raise ValueError("Unknown observer")
406
407 # we replace addObserver/removeObserver by our own
408 log.LogPublisher._originalAddObserver = log.LogPublisher.addObserver
409 log.LogPublisher._originalRemoveObserver = log.LogPublisher.removeObserver
410 import types # see https://stackoverflow.com/a/4267590 (thx Chris Morgan/aaronasterling)
411 log.addObserver = types.MethodType(addObserverObserver, self.log_publisher, log.LogPublisher)
412 log.removeObserver = types.MethodType(removeObserverObserver, self.log_publisher, log.LogPublisher)
413
414 # we now change existing observers
415 for idx, observer in enumerate(self.log_publisher.observers):
416 self.log_publisher.observers[idx] = self.installObserverHook(observer)
417
418
419 def configureLevel(self, level):
420 self.LOGGER_CLASS.level = level
421 super(ConfigureTwisted, self).configureLevel(level)
422
423 def configureOutput(self, output):
424 import sys
425 if output is None:
426 output = C.LOG_OPT_OUTPUT_SEP + C.LOG_OPT_OUTPUT_DEFAULT
427 self.manageOutputs(output)
428 addObserver = self.log.addObserver
429
430 if C.LOG_OPT_OUTPUT_DEFAULT in _handlers:
431 # default output is already managed, we just add output to stdout if we are in debug mode
432 from twisted.internet import defer
433 if defer.Deferred.debug:
434 addObserver(self.log.FileLogObserver(sys.stdout).emit)
175 else: 435 else:
176 options = None 436 # \\default is not in the output, so we remove current observers
177 437 self.cleared_observers = self.log_publisher.observers
178 if output not in (C.LOG_OPT_OUTPUT_DEFAULT, C.LOG_OPT_OUTPUT_FILE, C.LOG_OPT_OUTPUT_MEMORY): 438 self.observers.clear()
179 raise ValueError(u"Invalid output [%s]" % output) 439 del self.log_publisher.observers[:]
180 440 # and we forbid twistd to add any observer
181 if output == C.LOG_OPT_OUTPUT_DEFAULT: 441 self.log.addObserver = lambda other: None
182 # no option for defaut handler 442
183 _handlers[output] = None 443 if C.LOG_OPT_OUTPUT_FILE in _handlers:
184 elif output == C.LOG_OPT_OUTPUT_FILE: 444 from twisted.python import logfile
185 if not options: 445 for path in _handlers[C.LOG_OPT_OUTPUT_FILE]:
186 ValueError("%(handler)s output need a path as option" % {'handler': output}) 446 log_file = sys.stdout if path == '-' else logfile.LogFile.fromFullPath(path)
187 _handlers[output] = options 447 addObserver(self.log.FileLogObserver(log_file).emit)
188 options = None # option are parsed, we can empty them 448
189 elif output == C.LOG_OPT_OUTPUT_MEMORY: 449 if C.LOG_OPT_OUTPUT_MEMORY in _handlers:
190 # we have memory handler, option can be the len limit or None 450 raise NotImplementedError("Memory observer is not implemented in Twisted backend")
191 try: 451
192 limit = int(options) 452 def configureColors(self, colors, force_colors):
193 options = None # option are parsed, we can empty them 453 self.LOGGER_CLASS.colors = colors
194 except (TypeError, ValueError): 454 self.LOGGER_CLASS.force_colors = force_colors
195 limit = C.LOG_OPT_OUTPUT_MEMORY_LIMIT 455 if force_colors and not colors:
196 _handlers[output] = limit 456 raise ValueError('colors must be True if force_colors is True')
197 457
198 if options: # we should not have unparsed options 458 def postTreatment(self):
199 raise ValueError(u"options [%(options)s] are not supported for %(handler)s output" % {'options': options, 'handler': output}) 459 """Install twistedObserver which manage non SàT logs"""
200 460 def twistedObserver(event):
201 def _configureStdLogging(logging, level=None, fmt=C.LOG_OPT_FORMAT[1], output=C.LOG_OPT_OUTPUT[1], logger=None, colors=False, force_colors=False): 461 """Observer which redirect log message not produced by SàT to SàT logging system"""
202 """Configure standard logging module 462 if not 'sat_logged' in event:
203 463 # this log was not produced by SàT
204 @param logging: standard logging module 464 from twisted.python import log
205 @param level: one of C.LOG_LEVELS 465 text = log.textFromEventDict(event)
206 @param fmt: format string, pretty much as in std logging. Accept the following keywords (maybe more depending on backend): 466 if text is None:
207 - "message" 467 return
208 - "levelname" 468 twisted_logger = getLogger(C.LOG_TWISTED_LOGGER)
209 - "name" (logger name) 469 log_method = twisted_logger.error if event.get('isError', False) else twisted_logger.info
210 @param logger: if set, use it as a regular expression to filter on logger name. 470 log_method(text.decode('utf-8'))
211 Use search to match expression, so ^ or $ can be necessary. 471
212 @param colors: if True use ANSI colors to show log levels 472 self.log_publisher._originalAddObserver(twistedObserver)
213 @param force_colors: if True ANSI colors are used even if stdout is not a tty 473
214 """ 474
215 format_ = fmt 475 class ConfigureStandard(Configure):
216 if level is None: 476
217 level = C.LOG_LVL_DEBUG 477 def __init__(self, level=None, fmt=C.LOG_OPT_FORMAT[1], output=C.LOG_OPT_OUTPUT[1], logger=None, colors=False, force_colors=False):
218 import sys 478 super(ConfigureStandard, self).__init__(level, fmt, output, logger, colors, force_colors)
219 with_color = colors & (sys.stdout.isatty() or force_colors) 479
220 if not colors and force_colors: 480 def preTreatment(self):
221 raise ValueError("force_colors can't be used if colors is False") 481 """We use logging methods directly, instead of using Logger"""
222 482 global getLogger
223 class SatFormatter(logging.Formatter): 483 global debug
224 u"""Formatter which manage SàT specificities""" 484 global info
225 485 global warning
226 def __init__(self, fmt=None, datefmt=None): 486 global error
227 super(SatFormatter, self).__init__(fmt, datefmt) 487 global critical
228 488 import logging
229 def format(self, record): 489 getLogger = logging.getLogger
230 s = super(SatFormatter, self).format(record) 490 debug = logging.debug
231 if with_color: 491 info = logging.info
232 s = _ansiColors(record.levelname, s) 492 warning = logging.warning
233 return s 493 error = logging.error
234 494 critical = logging.critical
235 root_logger = logging.getLogger() 495
236 if len(root_logger.handlers) == 0: 496 def configureLevel(self, level):
237 _manageOutputs(output) 497 if level is None:
238 formatter = SatFormatter(format_) 498 level = C.LOG_LVL_DEBUG
239 name_filter = FilterName(logger) if logger else None 499 self.level = level
240 for handler, options in _handlers.items(): 500
241 if handler == C.LOG_OPT_OUTPUT_DEFAULT: 501 def configureFormat(self, fmt):
242 hdlr = logging.StreamHandler() 502 import logging
243 elif handler == C.LOG_OPT_OUTPUT_MEMORY: 503 format_ = fmt
244 import logging.handlers 504
245 hdlr = logging.handlers.BufferingHandler(options) 505 class SatFormatter(logging.Formatter):
246 _handlers[handler] = hdlr # we keep a reference to the handler to read the buffer later 506 u"""Formatter which manage SàT specificities"""
247 elif handler == C.LOG_OPT_OUTPUT_FILE: 507
248 import os.path 508 def __init__(self, fmt=None, datefmt=None):
249 hdlr = logging.FileHandler(os.path.expanduser(options)) 509 super(SatFormatter, self).__init__(fmt, datefmt)
250 else: 510
251 raise ValueError("Unknown handler type") 511 def format(self, record):
252 hdlr.setFormatter(formatter) 512 s = super(SatFormatter, self).format(record)
253 root_logger.addHandler(hdlr) 513 if self.with_color:
254 root_logger.setLevel(level) 514 s = _ansiColors(record.levelname, s)
255 if name_filter is not None: 515 return s
256 hdlr.addFilter(name_filter) 516
257 else: 517 self.formatter = SatFormatter(format_)
258 root_logger.warning(u"Handlers already set on root logger") 518
259 519 def configureOutput(self, output):
260 def _configureBasic(level=None, fmt=None, output=None, logger=None, colors=False, force_colors=False): 520 self.manageOutputs(output)
261 """Configure basic backend 521
262 @param level: same as _configureStdLogging.level 522 def configureLogger(self, logger):
263 @param fmt: same as _configureStdLogging.fmt 523 self.name_filter = FilterName(logger) if logger else None
264 @param output: not implemented yet TODO 524
265 @param logger: same as _configureStdLogging.logger 525 def configureColors(self, colors, force_colors):
266 @param colors: same as _configureStdLogging.colors
267 @param force_colors: same as _configureStdLogging.force_colors
268 """
269 if level is not None:
270 # we deactivate methods below level
271 level_idx = C.LOG_LEVELS.index(level)
272 def dev_null(self, msg):
273 pass
274 for _level in C.LOG_LEVELS[:level_idx]:
275 setattr(Logger, _level.lower(), dev_null)
276 if fmt is not None:
277 if fmt != '%(message)s': # %(message)s is the same as None
278 Logger.fmt = fmt
279 if output is not None:
280 if output != C.LOG_OPT_OUTPUT_SEP + C.LOG_OPT_OUTPUT_DEFAULT:
281 # TODO: manage other outputs
282 raise NotImplementedError("Basic backend only manage default output yet")
283
284 if logger:
285 Logger.filter_name = FilterName(logger)
286
287 if colors:
288 import sys 526 import sys
289 if force_colors or sys.stdout.isatty(): 527 self.with_color = colors & (sys.stdout.isatty() or force_colors)
290 # we need colors 528 if not colors and force_colors:
291 Logger.post_treat = lambda self, level, message: _ansiColors(level, message) 529 raise ValueError("force_colors can't be used if colors is False")
292 elif force_colors: 530
293 raise ValueError("force_colors can't be used if colors is False") 531 def _addHandler(self, root_logger, hdlr):
532 hdlr.setFormatter(self.formatter)
533 root_logger.addHandler(hdlr)
534 root_logger.setLevel(self.level)
535 if self.name_filter is not None:
536 hdlr.addFilter(self.name_filter)
537
538 def postTreatment(self):
539 import logging
540 root_logger = logging.getLogger()
541 if len(root_logger.handlers) == 0:
542 for handler, options in _handlers.items():
543 if handler == C.LOG_OPT_OUTPUT_DEFAULT:
544 hdlr = logging.StreamHandler()
545 self._addHandler(root_logger, hdlr)
546 elif handler == C.LOG_OPT_OUTPUT_MEMORY:
547 import logging.handlers
548 hdlr = logging.handlers.BufferingHandler(options)
549 _handlers[handler] = hdlr # we keep a reference to the handler to read the buffer later
550 self._addHandler(root_logger, hdlr)
551 elif handler == C.LOG_OPT_OUTPUT_FILE:
552 import os.path
553 for path in options:
554 hdlr = logging.FileHandler(os.path.expanduser(path))
555 self._addHandler(root_logger, hdlr)
556 else:
557 raise ValueError("Unknown handler type")
558 else:
559 root_logger.warning(u"Handlers already set on root logger")
560
294 561
295 def configure(backend=C.LOG_BACKEND_STANDARD, **options): 562 def configure(backend=C.LOG_BACKEND_STANDARD, **options):
296 """Configure logging behaviour 563 """Configure logging behaviour
297 @param backend: can be: 564 @param backend: can be:
298 C.LOG_BACKEND_STANDARD: use standard logging module 565 C.LOG_BACKEND_STANDARD: use standard logging module
302 global _backend 569 global _backend
303 if _backend is not None: 570 if _backend is not None:
304 raise exceptions.InternalError("Logging can only be configured once") 571 raise exceptions.InternalError("Logging can only be configured once")
305 _backend = backend 572 _backend = backend
306 573
307 if backend in (C.LOG_BACKEND_TWISTED, C.LOG_BACKEND_STANDARD): 574 if backend == C.LOG_BACKEND_BASIC:
308 if backend == C.LOG_BACKEND_TWISTED: 575 ConfigureBasic(**options)
309 from twisted.python import log 576
310 observer = log.PythonLoggingObserver() 577 elif backend == C.LOG_BACKEND_TWISTED:
311 observer.start() 578 ConfigureTwisted(**options)
312 global getLogger 579
313 global debug 580 elif backend == C.LOG_BACKEND_STANDARD:
314 global info 581 ConfigureStandard(**options)
315 global warning
316 global error
317 global critical
318 import logging
319 _configureStdLogging(logging, **options)
320 getLogger = logging.getLogger
321 debug = logging.debug
322 info = logging.info
323 warning = logging.warning
324 error = logging.error
325 critical = logging.critical
326
327 elif backend == C.LOG_BACKEND_BASIC:
328 _configureBasic(**options)
329 582
330 else: 583 else:
331 raise ValueError("unknown backend") 584 raise ValueError("unknown backend")
332 585
333 def _parseOptions(options): 586 def _parseOptions(options):
373 626
374 _parseOptions(log_conf) 627 _parseOptions(log_conf)
375 configure(backend, **log_conf) 628 configure(backend, **log_conf)
376 629
377 def getLogger(name=C.LOG_BASE_LOGGER): 630 def getLogger(name=C.LOG_BASE_LOGGER):
378 return _loggers.setdefault(name, Logger(name)) 631 if _backend in (None, C.LOG_BACKEND_BASIC):
632 logger_class = Logger
633 elif _backend == C.LOG_BACKEND_TWISTED:
634 logger_class = TwistedLogger
635 else:
636 raise ValueError("This method should not be called with backend [%s]" % _backend)
637 return _loggers.setdefault(name, logger_class(name))
379 638
380 _root_logger = getLogger() 639 _root_logger = getLogger()
381 640
382 def debug(msg): 641 def debug(msg):
383 _root_logger.debug(msg) 642 _root_logger.debug(msg)