Package flumotion :: Package extern :: Package log :: Module log
[hide private]

Source Code for Module flumotion.extern.log.log

  1  # -*- Mode: Python; test-case-name: test_log -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  # This file is released under the standard PSF license. 
  5   
  6  """ 
  7  Logging module. 
  8   
  9  Five levels of log information are defined. 
 10  These are, in order of decreasing verbosity: log, debug, info, warning, error. 
 11   
 12  This module provides a Loggable class for objects, as well as various 
 13  convenience methods for logging in general, and for logging with Twisted 
 14  and failures in particular. 
 15   
 16  Maintainer: U{Thomas Vander Stichele <thomas at apestaart dot org>} 
 17  """ 
 18   
 19  import errno 
 20  import sys 
 21  import os 
 22  import fnmatch 
 23  import time 
 24  import types 
 25  import traceback 
 26   
 27  # environment variables controlling levels for each category 
 28  _DEBUG = "*:1" 
 29  # name of the environment variable controlling our logging 
 30  _ENV_VAR_NAME = None 
 31  # package names we should scrub filenames for 
 32  _PACKAGE_SCRUB_LIST = [] 
 33   
 34  # dynamic dictionary of categories already seen and their level 
 35  _categories = {} 
 36   
 37  # log handlers registered 
 38  _log_handlers = [] 
 39  _log_handlers_limited = [] 
 40   
 41  _initialized = False 
 42   
 43  _stdout = None 
 44  _stderr = None 
 45  _old_hup_handler = None 
 46   
 47   
 48  # public log levels 
 49  (ERROR, 
 50   WARN, 
 51   INFO, 
 52   DEBUG, 
 53   LOG) = range(1, 6) 
 54   
 55  COLORS = {ERROR: 'RED', 
 56            WARN: 'YELLOW', 
 57            INFO: 'GREEN', 
 58            DEBUG: 'BLUE', 
 59            LOG: 'CYAN' 
 60            } 
 61   
 62  _FORMATTED_LEVELS = [] 
 63   
64 -def getLevelName(level):
65 """ 66 Return the name of a log level. 67 """ 68 assert isinstance(level, int) and level > 0 and level < 6, \ 69 "Bad debug level" 70 return ('ERROR', 'WARN', 'INFO', 'DEBUG', 'LOG')[level - 1]
71
72 -def getFormattedLevelName(level):
73 assert isinstance(level, int) and level > 0 and level < 6, \ 74 "Bad debug level" 75 return _FORMATTED_LEVELS[level - 1]
76
77 -def registerCategory(category):
78 """ 79 Register a given category in the debug system. 80 A level will be assigned to it based on previous calls to setDebug. 81 """ 82 # parse what level it is set to based on _DEBUG 83 # example: *:2,admin:4 84 global _DEBUG 85 global _levels 86 global _categories 87 88 level = 0 89 chunks = _DEBUG.split(',') 90 for chunk in chunks: 91 if not chunk: 92 continue 93 if ':' in chunk: 94 spec, value = chunk.split(':') 95 else: 96 spec = '*' 97 value = chunk 98 99 # our glob is unix filename style globbing, so cheat with fnmatch 100 # fnmatch.fnmatch didn't work for this, so don't use it 101 if category in fnmatch.filter((category, ), spec): 102 # we have a match, so set level based on string or int 103 if not value: 104 continue 105 try: 106 level = int(value) 107 except ValueError: # e.g. *; we default to most 108 level = 5 109 # store it 110 _categories[category] = level
111
112 -def getCategoryLevel(category):
113 """ 114 @param category: string 115 116 Get the debug level at which this category is being logged, adding it 117 if it wasn't registered yet. 118 """ 119 global _categories 120 if not _categories.has_key(category): 121 registerCategory(category) 122 return _categories[category]
123
124 -def setLogSettings(state):
125 """Update the current log settings. 126 This can restore an old saved log settings object returned by 127 getLogSettings 128 @param state: the settings to set 129 """ 130 131 global _DEBUG 132 global _log_handlers 133 global _log_handlers_limited 134 135 (_DEBUG, 136 _categories, 137 _log_handlers, 138 _log_handlers_limited) = state 139 140 for category in _categories: 141 registerCategory(category)
142
143 -def getLogSettings():
144 """Fetches the current log settings. 145 The returned object can be sent to setLogSettings to restore the 146 returned settings 147 @returns: the current settings 148 """ 149 return (_DEBUG, 150 _categories, 151 _log_handlers, 152 _log_handlers_limited)
153
154 -def _canShortcutLogging(category, level):
155 if _log_handlers: 156 # we have some loggers operating without filters, have to do 157 # everything 158 return False 159 else: 160 return level > getCategoryLevel(category)
161
162 -def scrubFilename(filename):
163 ''' 164 Scrub the filename to a relative path for all packages in our scrub list. 165 ''' 166 global _PACKAGE_SCRUB_LIST 167 for package in _PACKAGE_SCRUB_LIST: 168 i = filename.rfind(package) 169 if i > -1: 170 return filename[i:] 171 172 return filename
173
174 -def getFileLine(where=-1):
175 """ 176 Return the filename and line number for the given location. 177 178 If where is a negative integer, look for the code entry in the current 179 stack that is the given number of frames above this module. 180 If where is a function, look for the code entry of the function. 181 182 @param where: how many frames to go back up, or function 183 @type where: int (negative) or function 184 185 @return: tuple of (file, line) 186 @rtype: tuple of (str, int) 187 """ 188 co = None 189 lineno = None 190 191 if isinstance(where, types.FunctionType): 192 co = where.func_code 193 lineno = co.co_firstlineno 194 elif isinstance(where, types.MethodType): 195 co = where.im_func.func_code 196 lineno = co.co_firstlineno 197 else: 198 stackFrame = sys._getframe() 199 while stackFrame: 200 co = stackFrame.f_code 201 if not co.co_filename.endswith('log.py'): 202 # wind up the stack according to frame 203 while where < -1: 204 stackFrame = stackFrame.f_back 205 where += 1 206 co = stackFrame.f_code 207 lineno = stackFrame.f_lineno 208 break 209 stackFrame = stackFrame.f_back 210 211 if not co: 212 return "<unknown file>", 0 213 214 return scrubFilename(co.co_filename), lineno
215
216 -def ellipsize(o):
217 """ 218 Ellipsize the representation of the given object. 219 """ 220 r = repr(o) 221 if len(r) < 800: 222 return r 223 224 r = r[:60] + ' ... ' + r[-15:] 225 return r
226
227 -def getFormatArgs(startFormat, startArgs, endFormat, endArgs, args, kwargs):
228 """ 229 Helper function to create a format and args to use for logging. 230 This avoids needlessly interpolating variables. 231 """ 232 debugArgs = startArgs[:] 233 for a in args: 234 debugArgs.append(ellipsize(a)) 235 236 for items in kwargs.items(): 237 debugArgs.extend(items) 238 debugArgs.extend(endArgs) 239 format = startFormat \ 240 + ', '.join(('%s', ) * len(args)) \ 241 + (kwargs and ', ' or '') \ 242 + ', '.join(('%s=%r', ) * len(kwargs)) \ 243 + endFormat 244 return format, debugArgs
245
246 -def doLog(level, object, category, format, args, where=-1, 247 filePath=None, line=None):
248 """ 249 @param where: what to log file and line number for; 250 -1 for one frame above log.py; -2 and down for higher up; 251 a function for a (future) code object 252 @type where: int or callable 253 @param filePath: file to show the message as coming from, if caller 254 knows best 255 @type filePath: str 256 @param line: line to show the message as coming from, if caller 257 knows best 258 @type line: int 259 260 @return: dict of calculated variables, if they needed calculating. 261 currently contains file and line; this prevents us from 262 doing this work in the caller when it isn't needed because 263 of the debug level 264 """ 265 ret = {} 266 267 if args: 268 message = format % args 269 else: 270 message = format 271 272 # first all the unlimited ones 273 if _log_handlers: 274 if filePath is None and line is None: 275 (filePath, line) = getFileLine(where=where) 276 ret['filePath'] = filePath 277 ret['line'] = line 278 for handler in _log_handlers: 279 try: 280 handler(level, object, category, file, line, message) 281 except TypeError: 282 raise SystemError, "handler %r raised a TypeError" % handler 283 284 if level > getCategoryLevel(category): 285 return ret 286 287 for handler in _log_handlers_limited: 288 # set this a second time, just in case there weren't unlimited 289 # loggers there before 290 if filePath is None and line is None: 291 (filePath, line) = getFileLine(where=where) 292 ret['filePath'] = filePath 293 ret['line'] = line 294 try: 295 handler(level, object, category, filePath, line, message) 296 except TypeError: 297 raise SystemError, "handler %r raised a TypeError" % handler 298 299 return ret
300
301 -def errorObject(object, cat, format, *args):
302 """ 303 Log a fatal error message in the given category. 304 This will also raise a L{SystemExit}. 305 """ 306 doLog(ERROR, object, cat, format, args) 307 308 # we do the import here because having it globally causes weird import 309 # errors if our gstreactor also imports .log, which brings in errors 310 # and pb stuff 311 if args: 312 raise SystemExit(format % args) 313 else: 314 raise SystemExit(format)
315
316 -def warningObject(object, cat, format, *args):
317 """ 318 Log a warning message in the given category. 319 This is used for non-fatal problems. 320 """ 321 doLog(WARN, object, cat, format, args)
322
323 -def infoObject(object, cat, format, *args):
324 """ 325 Log an informational message in the given category. 326 """ 327 doLog(INFO, object, cat, format, args)
328
329 -def debugObject(object, cat, format, *args):
330 """ 331 Log a debug message in the given category. 332 """ 333 doLog(DEBUG, object, cat, format, args)
334
335 -def logObject(object, cat, format, *args):
336 """ 337 Log a log message. Used for debugging recurring events. 338 """ 339 doLog(LOG, object, cat, format, args)
340
341 -def safeprintf(file, format, *args):
342 """Write to a file object, ignoring errors. 343 """ 344 try: 345 if args: 346 file.write(format % args) 347 else: 348 file.write(format) 349 except IOError, e: 350 if e.errno == errno.EPIPE: 351 # if our output is closed, exit; e.g. when logging over an 352 # ssh connection and the ssh connection is closed 353 os._exit(os.EX_OSERR)
354 # otherwise ignore it, there's nothing you can do 355
356 -def stderrHandler(level, object, category, file, line, message):
357 """ 358 A log handler that writes to stderr. 359 360 @type level: string 361 @type object: string (or None) 362 @type category: string 363 @type message: string 364 """ 365 366 o = "" 367 if object: 368 o = '"' + object + '"' 369 370 where = "(%s:%d)" % (file, line) 371 372 # level pid object cat time 373 # 5 + 1 + 7 + 1 + 32 + 1 + 17 + 1 + 15 == 80 374 safeprintf(sys.stderr, '%s [%5d] %-32s %-17s %-15s ', 375 getFormattedLevelName(level), os.getpid(), o, category, 376 time.strftime("%b %d %H:%M:%S")) 377 safeprintf(sys.stderr, '%-4s %s %s\n', "", message, where) 378 379 sys.stderr.flush()
380
381 -def _preformatLevels(noColorEnvVarName):
382 levels = ('ERROR', 'WARN', 'INFO', 'DEBUG', 'LOG') 383 format = '%-5s' 384 385 try: 386 import termcolor 387 except ImportError: 388 # we don't need to catch this if termcolor is in same package as 389 # log.py 390 termcolor = None 391 392 if (noColorEnvVarName is not None 393 and termcolor is not None 394 and (noColorEnvVarName not in os.environ 395 or not os.environ[noColorEnvVarName])): 396 397 t = termcolor.TerminalController() 398 formatter = lambda level: ''.join((t.BOLD, getattr(t, COLORS[level]), 399 format % (levels[level-1],), t.NORMAL)) 400 else: 401 formatter = lambda level: format % (levels[level-1],) 402 403 for level in ERROR, WARN, INFO, DEBUG, LOG: 404 _FORMATTED_LEVELS.append(formatter(level))
405 406 ### "public" useful API 407 408 # setup functions
409 -def init(envVarName, enableColorOutput=False):
410 """ 411 Initialize the logging system and parse the environment variable 412 of the given name. 413 Needs to be called before starting the actual application. 414 """ 415 global _initialized 416 417 if _initialized: 418 return 419 420 global _ENV_VAR_NAME 421 _ENV_VAR_NAME = envVarName 422 423 if enableColorOutput: 424 _preformatLevels(envVarName + "_NO_COLOR") 425 else: 426 _preformatLevels(None) 427 428 if os.environ.has_key(envVarName): 429 # install a log handler that uses the value of the environment var 430 setDebug(os.environ[envVarName]) 431 addLimitedLogHandler(stderrHandler) 432 433 _initialized = True
434
435 -def setDebug(string):
436 """Set the DEBUG string. This controls the log output.""" 437 global _DEBUG 438 global _ENV_VAR_NAME 439 global _categories 440 441 _DEBUG = string 442 debug('log', "%s set to %s" % (_ENV_VAR_NAME, _DEBUG)) 443 444 # reparse all already registered category levels 445 for category in _categories: 446 registerCategory(category)
447
448 -def getDebug():
449 """ 450 Returns the currently active DEBUG string. 451 @rtype: str 452 """ 453 global _DEBUG 454 return _DEBUG
455
456 -def setPackageScrubList(*packages):
457 """ 458 Set the package names to scrub from filenames. 459 Filenames from these paths in log messages will be scrubbed to their 460 relative file path instead of the full absolute path. 461 462 @type packages: list of str 463 """ 464 global _PACKAGE_SCRUB_LIST 465 _PACKAGE_SCRUB_LIST = packages
466
467 -def reset():
468 """ 469 Resets the logging system, removing all log handlers. 470 """ 471 global _log_handlers, _log_handlers_limited, _initialized 472 473 _log_handlers = [] 474 _log_handlers_limited = [] 475 _initialized = False
476
477 -def addLogHandler(func):
478 """ 479 Add a custom log handler. 480 481 @param func: a function object with prototype (level, object, category, 482 message) where level is either ERROR, WARN, INFO, DEBUG, or 483 LOG, and the rest of the arguments are strings or None. Use 484 getLevelName(level) to get a printable name for the log level. 485 @type func: a callable function 486 487 @raises TypeError: if func is not a callable 488 """ 489 490 if not callable(func): 491 raise TypeError, "func must be callable" 492 493 if func not in _log_handlers: 494 _log_handlers.append(func)
495
496 -def addLimitedLogHandler(func):
497 """ 498 Add a custom log handler. 499 500 @param func: a function object with prototype (level, object, category, 501 message) where level is either ERROR, WARN, INFO, DEBUG, or 502 LOG, and the rest of the arguments are strings or None. Use 503 getLevelName(level) to get a printable name for the log level. 504 @type func: a callable function 505 506 @raises TypeError: TypeError if func is not a callable 507 """ 508 if not callable(func): 509 raise TypeError, "func must be callable" 510 511 if func not in _log_handlers_limited: 512 _log_handlers_limited.append(func)
513
514 -def removeLogHandler(func):
515 """ 516 Remove a registered log handler. 517 518 @param func: a function object with prototype (level, object, category, 519 message) where level is either ERROR, WARN, INFO, DEBUG, or 520 LOG, and the rest of the arguments are strings or None. Use 521 getLevelName(level) to get a printable name for the log level. 522 @type func: a callable function 523 524 @raises ValueError: if func is not registered 525 """ 526 _log_handlers.remove(func)
527 528
529 -def removeLimitedLogHandler(func):
530 """ 531 Remove a registered limited log handler. 532 533 @param func: a function object with prototype (level, object, category, 534 message) where level is either ERROR, WARN, INFO, DEBUG, or 535 LOG, and the rest of the arguments are strings or None. Use 536 getLevelName(level) to get a printable name for the log level. 537 @type func: a callable function 538 539 @raises ValueError: if func is not registered 540 """ 541 _log_handlers_limited.remove(func)
542 543 # public log functions
544 -def error(cat, format, *args):
545 errorObject(None, cat, format, *args)
546
547 -def warning(cat, format, *args):
548 warningObject(None, cat, format, *args)
549
550 -def info(cat, format, *args):
551 infoObject(None, cat, format, *args)
552
553 -def debug(cat, format, *args):
554 debugObject(None, cat, format, *args)
555
556 -def log(cat, format, *args):
557 logObject(None, cat, format, *args)
558 559 # public utility functions
560 -def getExceptionMessage(exception, frame=-1, filename=None):
561 """ 562 Return a short message based on an exception, useful for debugging. 563 Tries to find where the exception was triggered. 564 """ 565 stack = traceback.extract_tb(sys.exc_info()[2]) 566 if filename: 567 stack = [f for f in stack if f[0].find(filename) > -1] 568 #import code; code.interact(local=locals()) 569 (filename, line, func, text) = stack[frame] 570 filename = scrubFilename(filename) 571 exc = exception.__class__.__name__ 572 msg = "" 573 # a shortcut to extract a useful message out of most exceptions 574 # for now 575 if str(exception): 576 msg = ": %s" % str(exception) 577 return "exception %(exc)s at %(filename)s:%(line)s: %(func)s()%(msg)s" \ 578 % locals()
579
580 -def reopenOutputFiles():
581 """ 582 Reopens the stdout and stderr output files, as set by 583 L{outputToFiles}. 584 """ 585 if not _stdout and not _stderr: 586 debug('log', 'told to reopen log files, but log files not set') 587 return 588 589 def reopen(name, fileno, *args): 590 oldmask = os.umask(0026) 591 try: 592 f = open(name, 'a+', *args) 593 finally: 594 os.umask(oldmask) 595 596 os.dup2(f.fileno(), fileno)
597 598 if _stdout: 599 reopen(_stdout, sys.stdout.fileno()) 600 601 if _stderr: 602 reopen(_stderr, sys.stderr.fileno(), 0) 603 debug('log', 'opened log %r', _stderr) 604
605 -def outputToFiles(stdout=None, stderr=None):
606 """ 607 Redirect stdout and stderr to named files. 608 609 Records the file names so that a future call to reopenOutputFiles() 610 can open the same files. Installs a SIGHUP handler that will reopen 611 the output files. 612 613 Note that stderr is opened unbuffered, so if it shares a file with 614 stdout then interleaved output may not appear in the order that you 615 expect. 616 """ 617 global _stdout, _stderr, _old_hup_handler 618 _stdout, _stderr = stdout, stderr 619 reopenOutputFiles() 620 621 def sighup(signum, frame): 622 info('log', "Received SIGHUP, reopening logs") 623 reopenOutputFiles() 624 if _old_hup_handler: 625 info('log', "Calling old SIGHUP hander") 626 _old_hup_handler(signum, frame)
627 628 debug('log', 'installing SIGHUP handler') 629 import signal 630 handler = signal.signal(signal.SIGHUP, sighup) 631 if handler == signal.SIG_DFL or handler == signal.SIG_IGN: 632 _old_hup_handler = None 633 else: 634 _old_hup_handler = handler 635 636 637 # base class for loggable objects
638 -class Loggable:
639 """ 640 Base class for objects that want to be able to log messages with 641 different level of severity. The levels are, in order from least 642 to most: log, debug, info, warning, error. 643 644 @cvar logCategory: Implementors can provide a category to log their 645 messages under. 646 """ 647 648 logCategory = 'default' 649
650 - def error(self, *args):
651 """Log an error. By default this will also raise an exception.""" 652 if _canShortcutLogging(self.logCategory, ERROR): 653 return 654 errorObject(self.logObjectName(), self.logCategory, 655 *self.logFunction(*args))
656
657 - def warning(self, *args):
658 """Log a warning. Used for non-fatal problems.""" 659 if _canShortcutLogging(self.logCategory, WARN): 660 return 661 warningObject(self.logObjectName(), self.logCategory, 662 *self.logFunction(*args))
663
664 - def info(self, *args):
665 """Log an informational message. Used for normal operation.""" 666 if _canShortcutLogging(self.logCategory, INFO): 667 return 668 infoObject(self.logObjectName(), self.logCategory, 669 *self.logFunction(*args))
670
671 - def debug(self, *args):
672 """Log a debug message. Used for debugging.""" 673 if _canShortcutLogging(self.logCategory, DEBUG): 674 return 675 debugObject(self.logObjectName(), self.logCategory, 676 *self.logFunction(*args))
677
678 - def log(self, *args):
679 """Log a log message. Used for debugging recurring events.""" 680 if _canShortcutLogging(self.logCategory, LOG): 681 return 682 logObject(self.logObjectName(), self.logCategory, 683 *self.logFunction(*args))
684
685 - def doLog(self, level, where, format, *args, **kwargs):
686 """ 687 Log a message at the given level, with the possibility of going 688 higher up in the stack. 689 690 @param level: log level 691 @type level: int 692 @param where: how many frames to go back from the last log frame; 693 or a function (to log for a future call) 694 @type where: int (negative), or function 695 696 @param kwargs: a dict of pre-calculated values from a previous 697 doLog call 698 699 @return: a dict of calculated variables, to be reused in a 700 call to doLog that should show the same location 701 @rtype: dict 702 """ 703 if _canShortcutLogging(self.logCategory, level): 704 return {} 705 args = self.logFunction(*args) 706 return doLog(level, self.logObjectName(), self.logCategory, 707 format, args, where=where, **kwargs)
708
709 - def warningFailure(self, failure, swallow=True):
710 """ 711 Log a warning about a Twisted Failure. Useful as an errback handler: 712 d.addErrback(self.warningFailure) 713 714 @param swallow: whether to swallow the failure or not 715 @type swallow: bool 716 """ 717 if _canShortcutLogging(self.logCategory, WARN): 718 if swallow: 719 return 720 return failure 721 warningObject(self.logObjectName(), self.logCategory, 722 *self.logFunction(getFailureMessage(failure))) 723 if not swallow: 724 return failure
725
726 - def logFunction(self, *args):
727 """Overridable log function. Default just returns passed message.""" 728 return args
729
730 - def logObjectName(self):
731 """Overridable object name function.""" 732 # cheat pychecker 733 for name in ['logName', 'name']: 734 if hasattr(self, name): 735 return getattr(self, name) 736 737 return None
738 739 # Twisted helper stuff 740 741 # private stuff 742 _initializedTwisted = False 743 744 # make a singleton 745 __theTwistedLogObserver = None 746
747 -def _getTheTwistedLogObserver():
748 # used internally and in test 749 global __theTwistedLogObserver 750 751 if not __theTwistedLogObserver: 752 __theTwistedLogObserver = TwistedLogObserver() 753 754 return __theTwistedLogObserver
755 756 757 # public helper methods
758 -def getFailureMessage(failure):
759 """ 760 Return a short message based on L{twisted.python.failure.Failure}. 761 Tries to find where the exception was triggered. 762 """ 763 exc = str(failure.type) 764 msg = failure.getErrorMessage() 765 if len(failure.frames) == 0: 766 return "failure %(exc)s: %(msg)s" % locals() 767 768 (func, filename, line, some, other) = failure.frames[-1] 769 filename = scrubFilename(filename) 770 return "failure %(exc)s at %(filename)s:%(line)s: %(func)s(): %(msg)s" \ 771 % locals()
772
773 -def warningFailure(failure, swallow=True):
774 """ 775 Log a warning about a Failure. Useful as an errback handler: 776 d.addErrback(warningFailure) 777 778 @param swallow: whether to swallow the failure or not 779 @type swallow: bool 780 """ 781 warning('', getFailureMessage(failure)) 782 if not swallow: 783 return failure
784
785 -def logTwisted():
786 """ 787 Integrate twisted's logger with our logger. 788 789 This is done in a separate method because calling this imports and sets 790 up a reactor. Since we want basic logging working before choosing a 791 reactor, we need to separate these. 792 """ 793 global _initializedTwisted 794 795 if _initializedTwisted: 796 return 797 798 debug('log', 'Integrating twisted logger') 799 800 # integrate twisted's logging with us 801 from twisted.python import log as tlog 802 803 # this call imports the reactor 804 # that is why we do this in a separate method 805 from twisted.spread import pb 806 807 # we don't want logs for pb.Error types since they 808 # are specifically raised to be handled on the other side 809 observer = _getTheTwistedLogObserver() 810 observer.ignoreErrors([pb.Error,]) 811 tlog.startLoggingWithObserver(observer.emit, False) 812 813 _initializedTwisted = True
814 815 816 # we need an object as the observer because startLoggingWithObserver 817 # expects a bound method
818 -class TwistedLogObserver(Loggable):
819 """ 820 Twisted log observer that integrates with our logging. 821 """ 822 logCategory = "logobserver" 823
824 - def __init__(self):
825 self._ignoreErrors = [] # Failure types
826
827 - def emit(self, eventDict):
828 method = log # by default, lowest level 829 edm = eventDict['message'] 830 if not edm: 831 if eventDict['isError'] and eventDict.has_key('failure'): 832 f = eventDict['failure'] 833 for failureType in self._ignoreErrors: 834 r = f.check(failureType) 835 if r: 836 self.debug("Failure of type %r, ignoring" % failureType) 837 return 838 839 self.log("Failure %r" % f) 840 841 method = debug # tracebacks from errors at debug level 842 msg = "A twisted traceback occurred." 843 if getCategoryLevel("twisted") < WARN: 844 msg += " Run with debug level >= 2 to see the traceback." 845 # and an additional warning 846 warning('twisted', msg) 847 text = f.getTraceback() 848 safeprintf(sys.stderr, "\nTwisted traceback:\n") 849 safeprintf(sys.stderr, text + '\n') 850 elif eventDict.has_key('format'): 851 text = eventDict['format'] % eventDict 852 else: 853 # we don't know how to log this 854 return 855 else: 856 text = ' '.join(map(str, edm)) 857 858 fmtDict = { 'system': eventDict['system'], 859 'text': text.replace("\n", "\n\t") 860 } 861 msgStr = " [%(system)s] %(text)s\n" % fmtDict 862 # because msgstr can contain %, as in a backtrace, make sure we 863 # don't try to splice it 864 method('twisted', msgStr)
865
866 - def ignoreErrors(self, *types):
867 for failureType in types: 868 self._ignoreErrors.append(failureType)
869
870 - def clearIgnores(self):
871 self._ignoreErrors = []
872