Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | # Copyright 2001-2005 by Vinay Sajip. All Rights Reserved. |
2 | # | |
3 | # Permission to use, copy, modify, and distribute this software and its | |
4 | # documentation for any purpose and without fee is hereby granted, | |
5 | # provided that the above copyright notice appear in all copies and that | |
6 | # both that copyright notice and this permission notice appear in | |
7 | # supporting documentation, and that the name of Vinay Sajip | |
8 | # not be used in advertising or publicity pertaining to distribution | |
9 | # of the software without specific, written prior permission. | |
10 | # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING | |
11 | # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL | |
12 | # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR | |
13 | # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |
14 | # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT | |
15 | # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | ||
17 | """ | |
18 | Logging package for Python. Based on PEP 282 and comments thereto in | |
19 | comp.lang.python, and influenced by Apache's log4j system. | |
20 | ||
21 | Should work under Python versions >= 1.5.2, except that source line | |
22 | information is not available unless 'sys._getframe()' is. | |
23 | ||
24 | Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. | |
25 | ||
26 | To use, simply 'import logging' and log away! | |
27 | """ | |
28 | ||
29 | import sys, os, types, time, string, cStringIO, traceback | |
30 | ||
31 | try: | |
32 | import codecs | |
33 | except ImportError: | |
34 | codecs = None | |
35 | ||
36 | try: | |
37 | import thread | |
38 | import threading | |
39 | except ImportError: | |
40 | thread = None | |
41 | ||
42 | __author__ = "Vinay Sajip <vinay_sajip@red-dove.com>" | |
43 | __status__ = "beta" | |
44 | __version__ = "0.4.9.6" | |
45 | __date__ = "27 March 2005" | |
46 | ||
47 | #--------------------------------------------------------------------------- | |
48 | # Miscellaneous module data | |
49 | #--------------------------------------------------------------------------- | |
50 | ||
51 | # | |
52 | # _srcfile is used when walking the stack to check when we've got the first | |
53 | # caller stack frame. | |
54 | # | |
55 | if hasattr(sys, 'frozen'): #support for py2exe | |
56 | _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:]) | |
57 | elif string.lower(__file__[-4:]) in ['.pyc', '.pyo']: | |
58 | _srcfile = __file__[:-4] + '.py' | |
59 | else: | |
60 | _srcfile = __file__ | |
61 | _srcfile = os.path.normcase(_srcfile) | |
62 | ||
63 | # next bit filched from 1.5.2's inspect.py | |
64 | def currentframe(): | |
65 | """Return the frame object for the caller's stack frame.""" | |
66 | try: | |
67 | raise 'catch me' | |
68 | except: | |
69 | return sys.exc_traceback.tb_frame.f_back | |
70 | ||
71 | if hasattr(sys, '_getframe'): currentframe = sys._getframe | |
72 | # done filching | |
73 | ||
74 | # _srcfile is only used in conjunction with sys._getframe(). | |
75 | # To provide compatibility with older versions of Python, set _srcfile | |
76 | # to None if _getframe() is not available; this value will prevent | |
77 | # findCaller() from being called. | |
78 | #if not hasattr(sys, "_getframe"): | |
79 | # _srcfile = None | |
80 | ||
81 | # | |
82 | #_startTime is used as the base when calculating the relative time of events | |
83 | # | |
84 | _startTime = time.time() | |
85 | ||
86 | # | |
87 | #raiseExceptions is used to see if exceptions during handling should be | |
88 | #propagated | |
89 | # | |
90 | raiseExceptions = 1 | |
91 | ||
92 | #--------------------------------------------------------------------------- | |
93 | # Level related stuff | |
94 | #--------------------------------------------------------------------------- | |
95 | # | |
96 | # Default levels and level names, these can be replaced with any positive set | |
97 | # of values having corresponding names. There is a pseudo-level, NOTSET, which | |
98 | # is only really there as a lower limit for user-defined levels. Handlers and | |
99 | # loggers are initialized with NOTSET so that they will log all messages, even | |
100 | # at user-defined levels. | |
101 | # | |
102 | ||
103 | CRITICAL = 50 | |
104 | FATAL = CRITICAL | |
105 | ERROR = 40 | |
106 | WARNING = 30 | |
107 | WARN = WARNING | |
108 | INFO = 20 | |
109 | DEBUG = 10 | |
110 | NOTSET = 0 | |
111 | ||
112 | _levelNames = { | |
113 | CRITICAL : 'CRITICAL', | |
114 | ERROR : 'ERROR', | |
115 | WARNING : 'WARNING', | |
116 | INFO : 'INFO', | |
117 | DEBUG : 'DEBUG', | |
118 | NOTSET : 'NOTSET', | |
119 | 'CRITICAL' : CRITICAL, | |
120 | 'ERROR' : ERROR, | |
121 | 'WARN' : WARNING, | |
122 | 'WARNING' : WARNING, | |
123 | 'INFO' : INFO, | |
124 | 'DEBUG' : DEBUG, | |
125 | 'NOTSET' : NOTSET, | |
126 | } | |
127 | ||
128 | def getLevelName(level): | |
129 | """ | |
130 | Return the textual representation of logging level 'level'. | |
131 | ||
132 | If the level is one of the predefined levels (CRITICAL, ERROR, WARNING, | |
133 | INFO, DEBUG) then you get the corresponding string. If you have | |
134 | associated levels with names using addLevelName then the name you have | |
135 | associated with 'level' is returned. | |
136 | ||
137 | If a numeric value corresponding to one of the defined levels is passed | |
138 | in, the corresponding string representation is returned. | |
139 | ||
140 | Otherwise, the string "Level %s" % level is returned. | |
141 | """ | |
142 | return _levelNames.get(level, ("Level %s" % level)) | |
143 | ||
144 | def addLevelName(level, levelName): | |
145 | """ | |
146 | Associate 'levelName' with 'level'. | |
147 | ||
148 | This is used when converting levels to text during message formatting. | |
149 | """ | |
150 | _acquireLock() | |
151 | try: #unlikely to cause an exception, but you never know... | |
152 | _levelNames[level] = levelName | |
153 | _levelNames[levelName] = level | |
154 | finally: | |
155 | _releaseLock() | |
156 | ||
157 | #--------------------------------------------------------------------------- | |
158 | # Thread-related stuff | |
159 | #--------------------------------------------------------------------------- | |
160 | ||
161 | # | |
162 | #_lock is used to serialize access to shared data structures in this module. | |
163 | #This needs to be an RLock because fileConfig() creates Handlers and so | |
164 | #might arbitrary user threads. Since Handler.__init__() updates the shared | |
165 | #dictionary _handlers, it needs to acquire the lock. But if configuring, | |
166 | #the lock would already have been acquired - so we need an RLock. | |
167 | #The same argument applies to Loggers and Manager.loggerDict. | |
168 | # | |
169 | _lock = None | |
170 | ||
171 | def _acquireLock(): | |
172 | """ | |
173 | Acquire the module-level lock for serializing access to shared data. | |
174 | ||
175 | This should be released with _releaseLock(). | |
176 | """ | |
177 | global _lock | |
178 | if (not _lock) and thread: | |
179 | _lock = threading.RLock() | |
180 | if _lock: | |
181 | _lock.acquire() | |
182 | ||
183 | def _releaseLock(): | |
184 | """ | |
185 | Release the module-level lock acquired by calling _acquireLock(). | |
186 | """ | |
187 | if _lock: | |
188 | _lock.release() | |
189 | ||
190 | #--------------------------------------------------------------------------- | |
191 | # The logging record | |
192 | #--------------------------------------------------------------------------- | |
193 | ||
194 | class LogRecord: | |
195 | """ | |
196 | A LogRecord instance represents an event being logged. | |
197 | ||
198 | LogRecord instances are created every time something is logged. They | |
199 | contain all the information pertinent to the event being logged. The | |
200 | main information passed in is in msg and args, which are combined | |
201 | using str(msg) % args to create the message field of the record. The | |
202 | record also includes information such as when the record was created, | |
203 | the source line where the logging call was made, and any exception | |
204 | information to be logged. | |
205 | """ | |
206 | def __init__(self, name, level, pathname, lineno, msg, args, exc_info): | |
207 | """ | |
208 | Initialize a logging record with interesting information. | |
209 | """ | |
210 | ct = time.time() | |
211 | self.name = name | |
212 | self.msg = msg | |
213 | # | |
214 | # The following statement allows passing of a dictionary as a sole | |
215 | # argument, so that you can do something like | |
216 | # logging.debug("a %(a)d b %(b)s", {'a':1, 'b':2}) | |
217 | # Suggested by Stefan Behnel. | |
218 | # Note that without the test for args[0], we get a problem because | |
219 | # during formatting, we test to see if the arg is present using | |
220 | # 'if self.args:'. If the event being logged is e.g. 'Value is %d' | |
221 | # and if the passed arg fails 'if self.args:' then no formatting | |
222 | # is done. For example, logger.warn('Value is %d', 0) would log | |
223 | # 'Value is %d' instead of 'Value is 0'. | |
224 | # For the use case of passing a dictionary, this should not be a | |
225 | # problem. | |
226 | if args and (len(args) == 1) and args[0] and (type(args[0]) == types.DictType): | |
227 | args = args[0] | |
228 | self.args = args | |
229 | self.levelname = getLevelName(level) | |
230 | self.levelno = level | |
231 | self.pathname = pathname | |
232 | try: | |
233 | self.filename = os.path.basename(pathname) | |
234 | self.module = os.path.splitext(self.filename)[0] | |
235 | except: | |
236 | self.filename = pathname | |
237 | self.module = "Unknown module" | |
238 | self.exc_info = exc_info | |
239 | self.exc_text = None # used to cache the traceback text | |
240 | self.lineno = lineno | |
241 | self.created = ct | |
242 | self.msecs = (ct - long(ct)) * 1000 | |
243 | self.relativeCreated = (self.created - _startTime) * 1000 | |
244 | if thread: | |
245 | self.thread = thread.get_ident() | |
246 | self.threadName = threading.currentThread().getName() | |
247 | else: | |
248 | self.thread = None | |
249 | self.threadName = None | |
250 | if hasattr(os, 'getpid'): | |
251 | self.process = os.getpid() | |
252 | else: | |
253 | self.process = None | |
254 | ||
255 | def __str__(self): | |
256 | return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno, | |
257 | self.pathname, self.lineno, self.msg) | |
258 | ||
259 | def getMessage(self): | |
260 | """ | |
261 | Return the message for this LogRecord. | |
262 | ||
263 | Return the message for this LogRecord after merging any user-supplied | |
264 | arguments with the message. | |
265 | """ | |
266 | if not hasattr(types, "UnicodeType"): #if no unicode support... | |
267 | msg = str(self.msg) | |
268 | else: | |
269 | try: | |
270 | msg = str(self.msg) | |
271 | except UnicodeError: | |
272 | msg = self.msg #Defer encoding till later | |
273 | if self.args: | |
274 | msg = msg % self.args | |
275 | return msg | |
276 | ||
277 | def makeLogRecord(dict): | |
278 | """ | |
279 | Make a LogRecord whose attributes are defined by the specified dictionary, | |
280 | This function is useful for converting a logging event received over | |
281 | a socket connection (which is sent as a dictionary) into a LogRecord | |
282 | instance. | |
283 | """ | |
284 | rv = LogRecord(None, None, "", 0, "", (), None) | |
285 | rv.__dict__.update(dict) | |
286 | return rv | |
287 | ||
288 | #--------------------------------------------------------------------------- | |
289 | # Formatter classes and functions | |
290 | #--------------------------------------------------------------------------- | |
291 | ||
292 | class Formatter: | |
293 | """ | |
294 | Formatter instances are used to convert a LogRecord to text. | |
295 | ||
296 | Formatters need to know how a LogRecord is constructed. They are | |
297 | responsible for converting a LogRecord to (usually) a string which can | |
298 | be interpreted by either a human or an external system. The base Formatter | |
299 | allows a formatting string to be specified. If none is supplied, the | |
300 | default value of "%s(message)\\n" is used. | |
301 | ||
302 | The Formatter can be initialized with a format string which makes use of | |
303 | knowledge of the LogRecord attributes - e.g. the default value mentioned | |
304 | above makes use of the fact that the user's message and arguments are pre- | |
305 | formatted into a LogRecord's message attribute. Currently, the useful | |
306 | attributes in a LogRecord are described by: | |
307 | ||
308 | %(name)s Name of the logger (logging channel) | |
309 | %(levelno)s Numeric logging level for the message (DEBUG, INFO, | |
310 | WARNING, ERROR, CRITICAL) | |
311 | %(levelname)s Text logging level for the message ("DEBUG", "INFO", | |
312 | "WARNING", "ERROR", "CRITICAL") | |
313 | %(pathname)s Full pathname of the source file where the logging | |
314 | call was issued (if available) | |
315 | %(filename)s Filename portion of pathname | |
316 | %(module)s Module (name portion of filename) | |
317 | %(lineno)d Source line number where the logging call was issued | |
318 | (if available) | |
319 | %(created)f Time when the LogRecord was created (time.time() | |
320 | return value) | |
321 | %(asctime)s Textual time when the LogRecord was created | |
322 | %(msecs)d Millisecond portion of the creation time | |
323 | %(relativeCreated)d Time in milliseconds when the LogRecord was created, | |
324 | relative to the time the logging module was loaded | |
325 | (typically at application startup time) | |
326 | %(thread)d Thread ID (if available) | |
327 | %(threadName)s Thread name (if available) | |
328 | %(process)d Process ID (if available) | |
329 | %(message)s The result of record.getMessage(), computed just as | |
330 | the record is emitted | |
331 | """ | |
332 | ||
333 | converter = time.localtime | |
334 | ||
335 | def __init__(self, fmt=None, datefmt=None): | |
336 | """ | |
337 | Initialize the formatter with specified format strings. | |
338 | ||
339 | Initialize the formatter either with the specified format string, or a | |
340 | default as described above. Allow for specialized date formatting with | |
341 | the optional datefmt argument (if omitted, you get the ISO8601 format). | |
342 | """ | |
343 | if fmt: | |
344 | self._fmt = fmt | |
345 | else: | |
346 | self._fmt = "%(message)s" | |
347 | self.datefmt = datefmt | |
348 | ||
349 | def formatTime(self, record, datefmt=None): | |
350 | """ | |
351 | Return the creation time of the specified LogRecord as formatted text. | |
352 | ||
353 | This method should be called from format() by a formatter which | |
354 | wants to make use of a formatted time. This method can be overridden | |
355 | in formatters to provide for any specific requirement, but the | |
356 | basic behaviour is as follows: if datefmt (a string) is specified, | |
357 | it is used with time.strftime() to format the creation time of the | |
358 | record. Otherwise, the ISO8601 format is used. The resulting | |
359 | string is returned. This function uses a user-configurable function | |
360 | to convert the creation time to a tuple. By default, time.localtime() | |
361 | is used; to change this for a particular formatter instance, set the | |
362 | 'converter' attribute to a function with the same signature as | |
363 | time.localtime() or time.gmtime(). To change it for all formatters, | |
364 | for example if you want all logging times to be shown in GMT, | |
365 | set the 'converter' attribute in the Formatter class. | |
366 | """ | |
367 | ct = self.converter(record.created) | |
368 | if datefmt: | |
369 | s = time.strftime(datefmt, ct) | |
370 | else: | |
371 | t = time.strftime("%Y-%m-%d %H:%M:%S", ct) | |
372 | s = "%s,%03d" % (t, record.msecs) | |
373 | return s | |
374 | ||
375 | def formatException(self, ei): | |
376 | """ | |
377 | Format and return the specified exception information as a string. | |
378 | ||
379 | This default implementation just uses | |
380 | traceback.print_exception() | |
381 | """ | |
382 | sio = cStringIO.StringIO() | |
383 | traceback.print_exception(ei[0], ei[1], ei[2], None, sio) | |
384 | s = sio.getvalue() | |
385 | sio.close() | |
386 | if s[-1] == "\n": | |
387 | s = s[:-1] | |
388 | return s | |
389 | ||
390 | def format(self, record): | |
391 | """ | |
392 | Format the specified record as text. | |
393 | ||
394 | The record's attribute dictionary is used as the operand to a | |
395 | string formatting operation which yields the returned string. | |
396 | Before formatting the dictionary, a couple of preparatory steps | |
397 | are carried out. The message attribute of the record is computed | |
398 | using LogRecord.getMessage(). If the formatting string contains | |
399 | "%(asctime)", formatTime() is called to format the event time. | |
400 | If there is exception information, it is formatted using | |
401 | formatException() and appended to the message. | |
402 | """ | |
403 | record.message = record.getMessage() | |
404 | if string.find(self._fmt,"%(asctime)") >= 0: | |
405 | record.asctime = self.formatTime(record, self.datefmt) | |
406 | s = self._fmt % record.__dict__ | |
407 | if record.exc_info: | |
408 | # Cache the traceback text to avoid converting it multiple times | |
409 | # (it's constant anyway) | |
410 | if not record.exc_text: | |
411 | record.exc_text = self.formatException(record.exc_info) | |
412 | if record.exc_text: | |
413 | if s[-1] != "\n": | |
414 | s = s + "\n" | |
415 | s = s + record.exc_text | |
416 | return s | |
417 | ||
418 | # | |
419 | # The default formatter to use when no other is specified | |
420 | # | |
421 | _defaultFormatter = Formatter() | |
422 | ||
423 | class BufferingFormatter: | |
424 | """ | |
425 | A formatter suitable for formatting a number of records. | |
426 | """ | |
427 | def __init__(self, linefmt=None): | |
428 | """ | |
429 | Optionally specify a formatter which will be used to format each | |
430 | individual record. | |
431 | """ | |
432 | if linefmt: | |
433 | self.linefmt = linefmt | |
434 | else: | |
435 | self.linefmt = _defaultFormatter | |
436 | ||
437 | def formatHeader(self, records): | |
438 | """ | |
439 | Return the header string for the specified records. | |
440 | """ | |
441 | return "" | |
442 | ||
443 | def formatFooter(self, records): | |
444 | """ | |
445 | Return the footer string for the specified records. | |
446 | """ | |
447 | return "" | |
448 | ||
449 | def format(self, records): | |
450 | """ | |
451 | Format the specified records and return the result as a string. | |
452 | """ | |
453 | rv = "" | |
454 | if len(records) > 0: | |
455 | rv = rv + self.formatHeader(records) | |
456 | for record in records: | |
457 | rv = rv + self.linefmt.format(record) | |
458 | rv = rv + self.formatFooter(records) | |
459 | return rv | |
460 | ||
461 | #--------------------------------------------------------------------------- | |
462 | # Filter classes and functions | |
463 | #--------------------------------------------------------------------------- | |
464 | ||
465 | class Filter: | |
466 | """ | |
467 | Filter instances are used to perform arbitrary filtering of LogRecords. | |
468 | ||
469 | Loggers and Handlers can optionally use Filter instances to filter | |
470 | records as desired. The base filter class only allows events which are | |
471 | below a certain point in the logger hierarchy. For example, a filter | |
472 | initialized with "A.B" will allow events logged by loggers "A.B", | |
473 | "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If | |
474 | initialized with the empty string, all events are passed. | |
475 | """ | |
476 | def __init__(self, name=''): | |
477 | """ | |
478 | Initialize a filter. | |
479 | ||
480 | Initialize with the name of the logger which, together with its | |
481 | children, will have its events allowed through the filter. If no | |
482 | name is specified, allow every event. | |
483 | """ | |
484 | self.name = name | |
485 | self.nlen = len(name) | |
486 | ||
487 | def filter(self, record): | |
488 | """ | |
489 | Determine if the specified record is to be logged. | |
490 | ||
491 | Is the specified record to be logged? Returns 0 for no, nonzero for | |
492 | yes. If deemed appropriate, the record may be modified in-place. | |
493 | """ | |
494 | if self.nlen == 0: | |
495 | return 1 | |
496 | elif self.name == record.name: | |
497 | return 1 | |
498 | elif string.find(record.name, self.name, 0, self.nlen) != 0: | |
499 | return 0 | |
500 | return (record.name[self.nlen] == ".") | |
501 | ||
502 | class Filterer: | |
503 | """ | |
504 | A base class for loggers and handlers which allows them to share | |
505 | common code. | |
506 | """ | |
507 | def __init__(self): | |
508 | """ | |
509 | Initialize the list of filters to be an empty list. | |
510 | """ | |
511 | self.filters = [] | |
512 | ||
513 | def addFilter(self, filter): | |
514 | """ | |
515 | Add the specified filter to this handler. | |
516 | """ | |
517 | if not (filter in self.filters): | |
518 | self.filters.append(filter) | |
519 | ||
520 | def removeFilter(self, filter): | |
521 | """ | |
522 | Remove the specified filter from this handler. | |
523 | """ | |
524 | if filter in self.filters: | |
525 | self.filters.remove(filter) | |
526 | ||
527 | def filter(self, record): | |
528 | """ | |
529 | Determine if a record is loggable by consulting all the filters. | |
530 | ||
531 | The default is to allow the record to be logged; any filter can veto | |
532 | this and the record is then dropped. Returns a zero value if a record | |
533 | is to be dropped, else non-zero. | |
534 | """ | |
535 | rv = 1 | |
536 | for f in self.filters: | |
537 | if not f.filter(record): | |
538 | rv = 0 | |
539 | break | |
540 | return rv | |
541 | ||
542 | #--------------------------------------------------------------------------- | |
543 | # Handler classes and functions | |
544 | #--------------------------------------------------------------------------- | |
545 | ||
546 | _handlers = {} #repository of handlers (for flushing when shutdown called) | |
547 | _handlerList = [] # added to allow handlers to be removed in reverse of order initialized | |
548 | ||
549 | class Handler(Filterer): | |
550 | """ | |
551 | Handler instances dispatch logging events to specific destinations. | |
552 | ||
553 | The base handler class. Acts as a placeholder which defines the Handler | |
554 | interface. Handlers can optionally use Formatter instances to format | |
555 | records as desired. By default, no formatter is specified; in this case, | |
556 | the 'raw' message as determined by record.message is logged. | |
557 | """ | |
558 | def __init__(self, level=NOTSET): | |
559 | """ | |
560 | Initializes the instance - basically setting the formatter to None | |
561 | and the filter list to empty. | |
562 | """ | |
563 | Filterer.__init__(self) | |
564 | self.level = level | |
565 | self.formatter = None | |
566 | #get the module data lock, as we're updating a shared structure. | |
567 | _acquireLock() | |
568 | try: #unlikely to raise an exception, but you never know... | |
569 | _handlers[self] = 1 | |
570 | _handlerList.insert(0, self) | |
571 | finally: | |
572 | _releaseLock() | |
573 | self.createLock() | |
574 | ||
575 | def createLock(self): | |
576 | """ | |
577 | Acquire a thread lock for serializing access to the underlying I/O. | |
578 | """ | |
579 | if thread: | |
580 | self.lock = threading.RLock() | |
581 | else: | |
582 | self.lock = None | |
583 | ||
584 | def acquire(self): | |
585 | """ | |
586 | Acquire the I/O thread lock. | |
587 | """ | |
588 | if self.lock: | |
589 | self.lock.acquire() | |
590 | ||
591 | def release(self): | |
592 | """ | |
593 | Release the I/O thread lock. | |
594 | """ | |
595 | if self.lock: | |
596 | self.lock.release() | |
597 | ||
598 | def setLevel(self, level): | |
599 | """ | |
600 | Set the logging level of this handler. | |
601 | """ | |
602 | self.level = level | |
603 | ||
604 | def format(self, record): | |
605 | """ | |
606 | Format the specified record. | |
607 | ||
608 | If a formatter is set, use it. Otherwise, use the default formatter | |
609 | for the module. | |
610 | """ | |
611 | if self.formatter: | |
612 | fmt = self.formatter | |
613 | else: | |
614 | fmt = _defaultFormatter | |
615 | return fmt.format(record) | |
616 | ||
617 | def emit(self, record): | |
618 | """ | |
619 | Do whatever it takes to actually log the specified logging record. | |
620 | ||
621 | This version is intended to be implemented by subclasses and so | |
622 | raises a NotImplementedError. | |
623 | """ | |
624 | raise NotImplementedError, 'emit must be implemented '\ | |
625 | 'by Handler subclasses' | |
626 | ||
627 | def handle(self, record): | |
628 | """ | |
629 | Conditionally emit the specified logging record. | |
630 | ||
631 | Emission depends on filters which may have been added to the handler. | |
632 | Wrap the actual emission of the record with acquisition/release of | |
633 | the I/O thread lock. Returns whether the filter passed the record for | |
634 | emission. | |
635 | """ | |
636 | rv = self.filter(record) | |
637 | if rv: | |
638 | self.acquire() | |
639 | try: | |
640 | self.emit(record) | |
641 | finally: | |
642 | self.release() | |
643 | return rv | |
644 | ||
645 | def setFormatter(self, fmt): | |
646 | """ | |
647 | Set the formatter for this handler. | |
648 | """ | |
649 | self.formatter = fmt | |
650 | ||
651 | def flush(self): | |
652 | """ | |
653 | Ensure all logging output has been flushed. | |
654 | ||
655 | This version does nothing and is intended to be implemented by | |
656 | subclasses. | |
657 | """ | |
658 | pass | |
659 | ||
660 | def close(self): | |
661 | """ | |
662 | Tidy up any resources used by the handler. | |
663 | ||
664 | This version does removes the handler from an internal list | |
665 | of handlers which is closed when shutdown() is called. Subclasses | |
666 | should ensure that this gets called from overridden close() | |
667 | methods. | |
668 | """ | |
669 | #get the module data lock, as we're updating a shared structure. | |
670 | _acquireLock() | |
671 | try: #unlikely to raise an exception, but you never know... | |
672 | del _handlers[self] | |
673 | _handlerList.remove(self) | |
674 | finally: | |
675 | _releaseLock() | |
676 | ||
677 | def handleError(self, record): | |
678 | """ | |
679 | Handle errors which occur during an emit() call. | |
680 | ||
681 | This method should be called from handlers when an exception is | |
682 | encountered during an emit() call. If raiseExceptions is false, | |
683 | exceptions get silently ignored. This is what is mostly wanted | |
684 | for a logging system - most users will not care about errors in | |
685 | the logging system, they are more interested in application errors. | |
686 | You could, however, replace this with a custom handler if you wish. | |
687 | The record which was being processed is passed in to this method. | |
688 | """ | |
689 | if raiseExceptions: | |
690 | ei = sys.exc_info() | |
691 | traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) | |
692 | del ei | |
693 | ||
694 | class StreamHandler(Handler): | |
695 | """ | |
696 | A handler class which writes logging records, appropriately formatted, | |
697 | to a stream. Note that this class does not close the stream, as | |
698 | sys.stdout or sys.stderr may be used. | |
699 | """ | |
700 | def __init__(self, strm=None): | |
701 | """ | |
702 | Initialize the handler. | |
703 | ||
704 | If strm is not specified, sys.stderr is used. | |
705 | """ | |
706 | Handler.__init__(self) | |
707 | if not strm: | |
708 | strm = sys.stderr | |
709 | self.stream = strm | |
710 | self.formatter = None | |
711 | ||
712 | def flush(self): | |
713 | """ | |
714 | Flushes the stream. | |
715 | """ | |
716 | self.stream.flush() | |
717 | ||
718 | def emit(self, record): | |
719 | """ | |
720 | Emit a record. | |
721 | ||
722 | If a formatter is specified, it is used to format the record. | |
723 | The record is then written to the stream with a trailing newline | |
724 | [N.B. this may be removed depending on feedback]. If exception | |
725 | information is present, it is formatted using | |
726 | traceback.print_exception and appended to the stream. | |
727 | """ | |
728 | try: | |
729 | msg = self.format(record) | |
730 | fs = "%s\n" | |
731 | if not hasattr(types, "UnicodeType"): #if no unicode support... | |
732 | self.stream.write(fs % msg) | |
733 | else: | |
734 | try: | |
735 | self.stream.write(fs % msg) | |
736 | except UnicodeError: | |
737 | self.stream.write(fs % msg.encode("UTF-8")) | |
738 | self.flush() | |
739 | except: | |
740 | self.handleError(record) | |
741 | ||
742 | class FileHandler(StreamHandler): | |
743 | """ | |
744 | A handler class which writes formatted logging records to disk files. | |
745 | """ | |
746 | def __init__(self, filename, mode='a', encoding=None): | |
747 | """ | |
748 | Open the specified file and use it as the stream for logging. | |
749 | """ | |
750 | if codecs is None: | |
751 | encoding = None | |
752 | if encoding is None: | |
753 | stream = open(filename, mode) | |
754 | else: | |
755 | stream = codecs.open(filename, mode, encoding) | |
756 | StreamHandler.__init__(self, stream) | |
757 | #keep the absolute path, otherwise derived classes which use this | |
758 | #may come a cropper when the current directory changes | |
759 | self.baseFilename = os.path.abspath(filename) | |
760 | self.mode = mode | |
761 | ||
762 | def close(self): | |
763 | """ | |
764 | Closes the stream. | |
765 | """ | |
766 | self.flush() | |
767 | self.stream.close() | |
768 | StreamHandler.close(self) | |
769 | ||
770 | #--------------------------------------------------------------------------- | |
771 | # Manager classes and functions | |
772 | #--------------------------------------------------------------------------- | |
773 | ||
774 | class PlaceHolder: | |
775 | """ | |
776 | PlaceHolder instances are used in the Manager logger hierarchy to take | |
777 | the place of nodes for which no loggers have been defined. This class is | |
778 | intended for internal use only and not as part of the public API. | |
779 | """ | |
780 | def __init__(self, alogger): | |
781 | """ | |
782 | Initialize with the specified logger being a child of this placeholder. | |
783 | """ | |
784 | self.loggers = [alogger] | |
785 | ||
786 | def append(self, alogger): | |
787 | """ | |
788 | Add the specified logger as a child of this placeholder. | |
789 | """ | |
790 | if alogger not in self.loggers: | |
791 | self.loggers.append(alogger) | |
792 | ||
793 | # | |
794 | # Determine which class to use when instantiating loggers. | |
795 | # | |
796 | _loggerClass = None | |
797 | ||
798 | def setLoggerClass(klass): | |
799 | """ | |
800 | Set the class to be used when instantiating a logger. The class should | |
801 | define __init__() such that only a name argument is required, and the | |
802 | __init__() should call Logger.__init__() | |
803 | """ | |
804 | if klass != Logger: | |
805 | if not issubclass(klass, Logger): | |
806 | raise TypeError, "logger not derived from logging.Logger: " + \ | |
807 | klass.__name__ | |
808 | global _loggerClass | |
809 | _loggerClass = klass | |
810 | ||
811 | def getLoggerClass(): | |
812 | """ | |
813 | Return the class to be used when instantiating a logger. | |
814 | """ | |
815 | ||
816 | return _loggerClass | |
817 | ||
818 | class Manager: | |
819 | """ | |
820 | There is [under normal circumstances] just one Manager instance, which | |
821 | holds the hierarchy of loggers. | |
822 | """ | |
823 | def __init__(self, rootnode): | |
824 | """ | |
825 | Initialize the manager with the root node of the logger hierarchy. | |
826 | """ | |
827 | self.root = rootnode | |
828 | self.disable = 0 | |
829 | self.emittedNoHandlerWarning = 0 | |
830 | self.loggerDict = {} | |
831 | ||
832 | def getLogger(self, name): | |
833 | """ | |
834 | Get a logger with the specified name (channel name), creating it | |
835 | if it doesn't yet exist. This name is a dot-separated hierarchical | |
836 | name, such as "a", "a.b", "a.b.c" or similar. | |
837 | ||
838 | If a PlaceHolder existed for the specified name [i.e. the logger | |
839 | didn't exist but a child of it did], replace it with the created | |
840 | logger and fix up the parent/child references which pointed to the | |
841 | placeholder to now point to the logger. | |
842 | """ | |
843 | rv = None | |
844 | _acquireLock() | |
845 | try: | |
846 | if self.loggerDict.has_key(name): | |
847 | rv = self.loggerDict[name] | |
848 | if isinstance(rv, PlaceHolder): | |
849 | ph = rv | |
850 | rv = _loggerClass(name) | |
851 | rv.manager = self | |
852 | self.loggerDict[name] = rv | |
853 | self._fixupChildren(ph, rv) | |
854 | self._fixupParents(rv) | |
855 | else: | |
856 | rv = _loggerClass(name) | |
857 | rv.manager = self | |
858 | self.loggerDict[name] = rv | |
859 | self._fixupParents(rv) | |
860 | finally: | |
861 | _releaseLock() | |
862 | return rv | |
863 | ||
864 | def _fixupParents(self, alogger): | |
865 | """ | |
866 | Ensure that there are either loggers or placeholders all the way | |
867 | from the specified logger to the root of the logger hierarchy. | |
868 | """ | |
869 | name = alogger.name | |
870 | i = string.rfind(name, ".") | |
871 | rv = None | |
872 | while (i > 0) and not rv: | |
873 | substr = name[:i] | |
874 | if not self.loggerDict.has_key(substr): | |
875 | self.loggerDict[substr] = PlaceHolder(alogger) | |
876 | else: | |
877 | obj = self.loggerDict[substr] | |
878 | if isinstance(obj, Logger): | |
879 | rv = obj | |
880 | else: | |
881 | assert isinstance(obj, PlaceHolder) | |
882 | obj.append(alogger) | |
883 | i = string.rfind(name, ".", 0, i - 1) | |
884 | if not rv: | |
885 | rv = self.root | |
886 | alogger.parent = rv | |
887 | ||
888 | def _fixupChildren(self, ph, alogger): | |
889 | """ | |
890 | Ensure that children of the placeholder ph are connected to the | |
891 | specified logger. | |
892 | """ | |
893 | for c in ph.loggers: | |
894 | if string.find(c.parent.name, alogger.name) <> 0: | |
895 | alogger.parent = c.parent | |
896 | c.parent = alogger | |
897 | ||
898 | #--------------------------------------------------------------------------- | |
899 | # Logger classes and functions | |
900 | #--------------------------------------------------------------------------- | |
901 | ||
902 | class Logger(Filterer): | |
903 | """ | |
904 | Instances of the Logger class represent a single logging channel. A | |
905 | "logging channel" indicates an area of an application. Exactly how an | |
906 | "area" is defined is up to the application developer. Since an | |
907 | application can have any number of areas, logging channels are identified | |
908 | by a unique string. Application areas can be nested (e.g. an area | |
909 | of "input processing" might include sub-areas "read CSV files", "read | |
910 | XLS files" and "read Gnumeric files"). To cater for this natural nesting, | |
911 | channel names are organized into a namespace hierarchy where levels are | |
912 | separated by periods, much like the Java or Python package namespace. So | |
913 | in the instance given above, channel names might be "input" for the upper | |
914 | level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels. | |
915 | There is no arbitrary limit to the depth of nesting. | |
916 | """ | |
917 | def __init__(self, name, level=NOTSET): | |
918 | """ | |
919 | Initialize the logger with a name and an optional level. | |
920 | """ | |
921 | Filterer.__init__(self) | |
922 | self.name = name | |
923 | self.level = level | |
924 | self.parent = None | |
925 | self.propagate = 1 | |
926 | self.handlers = [] | |
927 | self.disabled = 0 | |
928 | ||
929 | def setLevel(self, level): | |
930 | """ | |
931 | Set the logging level of this logger. | |
932 | """ | |
933 | self.level = level | |
934 | ||
935 | def debug(self, msg, *args, **kwargs): | |
936 | """ | |
937 | Log 'msg % args' with severity 'DEBUG'. | |
938 | ||
939 | To pass exception information, use the keyword argument exc_info with | |
940 | a true value, e.g. | |
941 | ||
942 | logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) | |
943 | """ | |
944 | if self.manager.disable >= DEBUG: | |
945 | return | |
946 | if DEBUG >= self.getEffectiveLevel(): | |
947 | apply(self._log, (DEBUG, msg, args), kwargs) | |
948 | ||
949 | def info(self, msg, *args, **kwargs): | |
950 | """ | |
951 | Log 'msg % args' with severity 'INFO'. | |
952 | ||
953 | To pass exception information, use the keyword argument exc_info with | |
954 | a true value, e.g. | |
955 | ||
956 | logger.info("Houston, we have a %s", "interesting problem", exc_info=1) | |
957 | """ | |
958 | if self.manager.disable >= INFO: | |
959 | return | |
960 | if INFO >= self.getEffectiveLevel(): | |
961 | apply(self._log, (INFO, msg, args), kwargs) | |
962 | ||
963 | def warning(self, msg, *args, **kwargs): | |
964 | """ | |
965 | Log 'msg % args' with severity 'WARNING'. | |
966 | ||
967 | To pass exception information, use the keyword argument exc_info with | |
968 | a true value, e.g. | |
969 | ||
970 | logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1) | |
971 | """ | |
972 | if self.manager.disable >= WARNING: | |
973 | return | |
974 | if self.isEnabledFor(WARNING): | |
975 | apply(self._log, (WARNING, msg, args), kwargs) | |
976 | ||
977 | warn = warning | |
978 | ||
979 | def error(self, msg, *args, **kwargs): | |
980 | """ | |
981 | Log 'msg % args' with severity 'ERROR'. | |
982 | ||
983 | To pass exception information, use the keyword argument exc_info with | |
984 | a true value, e.g. | |
985 | ||
986 | logger.error("Houston, we have a %s", "major problem", exc_info=1) | |
987 | """ | |
988 | if self.manager.disable >= ERROR: | |
989 | return | |
990 | if self.isEnabledFor(ERROR): | |
991 | apply(self._log, (ERROR, msg, args), kwargs) | |
992 | ||
993 | def exception(self, msg, *args): | |
994 | """ | |
995 | Convenience method for logging an ERROR with exception information. | |
996 | """ | |
997 | apply(self.error, (msg,) + args, {'exc_info': 1}) | |
998 | ||
999 | def critical(self, msg, *args, **kwargs): | |
1000 | """ | |
1001 | Log 'msg % args' with severity 'CRITICAL'. | |
1002 | ||
1003 | To pass exception information, use the keyword argument exc_info with | |
1004 | a true value, e.g. | |
1005 | ||
1006 | logger.critical("Houston, we have a %s", "major disaster", exc_info=1) | |
1007 | """ | |
1008 | if self.manager.disable >= CRITICAL: | |
1009 | return | |
1010 | if CRITICAL >= self.getEffectiveLevel(): | |
1011 | apply(self._log, (CRITICAL, msg, args), kwargs) | |
1012 | ||
1013 | fatal = critical | |
1014 | ||
1015 | def log(self, level, msg, *args, **kwargs): | |
1016 | """ | |
1017 | Log 'msg % args' with the integer severity 'level'. | |
1018 | ||
1019 | To pass exception information, use the keyword argument exc_info with | |
1020 | a true value, e.g. | |
1021 | ||
1022 | logger.log(level, "We have a %s", "mysterious problem", exc_info=1) | |
1023 | """ | |
1024 | if type(level) != types.IntType: | |
1025 | if raiseExceptions: | |
1026 | raise TypeError, "level must be an integer" | |
1027 | else: | |
1028 | return | |
1029 | if self.manager.disable >= level: | |
1030 | return | |
1031 | if self.isEnabledFor(level): | |
1032 | apply(self._log, (level, msg, args), kwargs) | |
1033 | ||
1034 | def findCaller(self): | |
1035 | """ | |
1036 | Find the stack frame of the caller so that we can note the source | |
1037 | file name, line number and function name. | |
1038 | """ | |
1039 | f = currentframe().f_back | |
1040 | while 1: | |
1041 | co = f.f_code | |
1042 | filename = os.path.normcase(co.co_filename) | |
1043 | if filename == _srcfile: | |
1044 | f = f.f_back | |
1045 | continue | |
1046 | return filename, f.f_lineno, co.co_name | |
1047 | ||
1048 | def makeRecord(self, name, level, fn, lno, msg, args, exc_info): | |
1049 | """ | |
1050 | A factory method which can be overridden in subclasses to create | |
1051 | specialized LogRecords. | |
1052 | """ | |
1053 | return LogRecord(name, level, fn, lno, msg, args, exc_info) | |
1054 | ||
1055 | def _log(self, level, msg, args, exc_info=None): | |
1056 | """ | |
1057 | Low-level logging routine which creates a LogRecord and then calls | |
1058 | all the handlers of this logger to handle the record. | |
1059 | """ | |
1060 | if _srcfile: | |
1061 | fn, lno, func = self.findCaller() | |
1062 | else: | |
1063 | fn, lno, func = "(unknown file)", 0, "(unknown function)" | |
1064 | if exc_info: | |
1065 | if type(exc_info) != types.TupleType: | |
1066 | exc_info = sys.exc_info() | |
1067 | record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info) | |
1068 | self.handle(record) | |
1069 | ||
1070 | def handle(self, record): | |
1071 | """ | |
1072 | Call the handlers for the specified record. | |
1073 | ||
1074 | This method is used for unpickled records received from a socket, as | |
1075 | well as those created locally. Logger-level filtering is applied. | |
1076 | """ | |
1077 | if (not self.disabled) and self.filter(record): | |
1078 | self.callHandlers(record) | |
1079 | ||
1080 | def addHandler(self, hdlr): | |
1081 | """ | |
1082 | Add the specified handler to this logger. | |
1083 | """ | |
1084 | if not (hdlr in self.handlers): | |
1085 | self.handlers.append(hdlr) | |
1086 | ||
1087 | def removeHandler(self, hdlr): | |
1088 | """ | |
1089 | Remove the specified handler from this logger. | |
1090 | """ | |
1091 | if hdlr in self.handlers: | |
1092 | #hdlr.close() | |
1093 | hdlr.acquire() | |
1094 | try: | |
1095 | self.handlers.remove(hdlr) | |
1096 | finally: | |
1097 | hdlr.release() | |
1098 | ||
1099 | def callHandlers(self, record): | |
1100 | """ | |
1101 | Pass a record to all relevant handlers. | |
1102 | ||
1103 | Loop through all handlers for this logger and its parents in the | |
1104 | logger hierarchy. If no handler was found, output a one-off error | |
1105 | message to sys.stderr. Stop searching up the hierarchy whenever a | |
1106 | logger with the "propagate" attribute set to zero is found - that | |
1107 | will be the last logger whose handlers are called. | |
1108 | """ | |
1109 | c = self | |
1110 | found = 0 | |
1111 | while c: | |
1112 | for hdlr in c.handlers: | |
1113 | found = found + 1 | |
1114 | if record.levelno >= hdlr.level: | |
1115 | hdlr.handle(record) | |
1116 | if not c.propagate: | |
1117 | c = None #break out | |
1118 | else: | |
1119 | c = c.parent | |
1120 | if (found == 0) and not self.manager.emittedNoHandlerWarning: | |
1121 | sys.stderr.write("No handlers could be found for logger" | |
1122 | " \"%s\"\n" % self.name) | |
1123 | self.manager.emittedNoHandlerWarning = 1 | |
1124 | ||
1125 | def getEffectiveLevel(self): | |
1126 | """ | |
1127 | Get the effective level for this logger. | |
1128 | ||
1129 | Loop through this logger and its parents in the logger hierarchy, | |
1130 | looking for a non-zero logging level. Return the first one found. | |
1131 | """ | |
1132 | logger = self | |
1133 | while logger: | |
1134 | if logger.level: | |
1135 | return logger.level | |
1136 | logger = logger.parent | |
1137 | return NOTSET | |
1138 | ||
1139 | def isEnabledFor(self, level): | |
1140 | """ | |
1141 | Is this logger enabled for level 'level'? | |
1142 | """ | |
1143 | if self.manager.disable >= level: | |
1144 | return 0 | |
1145 | return level >= self.getEffectiveLevel() | |
1146 | ||
1147 | class RootLogger(Logger): | |
1148 | """ | |
1149 | A root logger is not that different to any other logger, except that | |
1150 | it must have a logging level and there is only one instance of it in | |
1151 | the hierarchy. | |
1152 | """ | |
1153 | def __init__(self, level): | |
1154 | """ | |
1155 | Initialize the logger with the name "root". | |
1156 | """ | |
1157 | Logger.__init__(self, "root", level) | |
1158 | ||
1159 | _loggerClass = Logger | |
1160 | ||
1161 | root = RootLogger(WARNING) | |
1162 | Logger.root = root | |
1163 | Logger.manager = Manager(Logger.root) | |
1164 | ||
1165 | #--------------------------------------------------------------------------- | |
1166 | # Configuration classes and functions | |
1167 | #--------------------------------------------------------------------------- | |
1168 | ||
1169 | BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s" | |
1170 | ||
1171 | def basicConfig(**kwargs): | |
1172 | """ | |
1173 | Do basic configuration for the logging system. | |
1174 | ||
1175 | This function does nothing if the root logger already has handlers | |
1176 | configured. It is a convenience method intended for use by simple scripts | |
1177 | to do one-shot configuration of the logging package. | |
1178 | ||
1179 | The default behaviour is to create a StreamHandler which writes to | |
1180 | sys.stderr, set a formatter using the BASIC_FORMAT format string, and | |
1181 | add the handler to the root logger. | |
1182 | ||
1183 | A number of optional keyword arguments may be specified, which can alter | |
1184 | the default behaviour. | |
1185 | ||
1186 | filename Specifies that a FileHandler be created, using the specified | |
1187 | filename, rather than a StreamHandler. | |
1188 | filemode Specifies the mode to open the file, if filename is specified | |
1189 | (if filemode is unspecified, it defaults to 'a'). | |
1190 | format Use the specified format string for the handler. | |
1191 | datefmt Use the specified date/time format. | |
1192 | level Set the root logger level to the specified level. | |
1193 | stream Use the specified stream to initialize the StreamHandler. Note | |
1194 | that this argument is incompatible with 'filename' - if both | |
1195 | are present, 'stream' is ignored. | |
1196 | ||
1197 | Note that you could specify a stream created using open(filename, mode) | |
1198 | rather than passing the filename and mode in. However, it should be | |
1199 | remembered that StreamHandler does not close its stream (since it may be | |
1200 | using sys.stdout or sys.stderr), whereas FileHandler closes its stream | |
1201 | when the handler is closed. | |
1202 | """ | |
1203 | if len(root.handlers) == 0: | |
1204 | filename = kwargs.get("filename") | |
1205 | if filename: | |
1206 | mode = kwargs.get("filemode", 'a') | |
1207 | hdlr = FileHandler(filename, mode) | |
1208 | else: | |
1209 | stream = kwargs.get("stream") | |
1210 | hdlr = StreamHandler(stream) | |
1211 | fs = kwargs.get("format", BASIC_FORMAT) | |
1212 | dfs = kwargs.get("datefmt", None) | |
1213 | fmt = Formatter(fs, dfs) | |
1214 | hdlr.setFormatter(fmt) | |
1215 | root.addHandler(hdlr) | |
1216 | level = kwargs.get("level") | |
1217 | if level: | |
1218 | root.setLevel(level) | |
1219 | ||
1220 | #--------------------------------------------------------------------------- | |
1221 | # Utility functions at module level. | |
1222 | # Basically delegate everything to the root logger. | |
1223 | #--------------------------------------------------------------------------- | |
1224 | ||
1225 | def getLogger(name=None): | |
1226 | """ | |
1227 | Return a logger with the specified name, creating it if necessary. | |
1228 | ||
1229 | If no name is specified, return the root logger. | |
1230 | """ | |
1231 | if name: | |
1232 | return Logger.manager.getLogger(name) | |
1233 | else: | |
1234 | return root | |
1235 | ||
1236 | #def getRootLogger(): | |
1237 | # """ | |
1238 | # Return the root logger. | |
1239 | # | |
1240 | # Note that getLogger('') now does the same thing, so this function is | |
1241 | # deprecated and may disappear in the future. | |
1242 | # """ | |
1243 | # return root | |
1244 | ||
1245 | def critical(msg, *args, **kwargs): | |
1246 | """ | |
1247 | Log a message with severity 'CRITICAL' on the root logger. | |
1248 | """ | |
1249 | if len(root.handlers) == 0: | |
1250 | basicConfig() | |
1251 | apply(root.critical, (msg,)+args, kwargs) | |
1252 | ||
1253 | fatal = critical | |
1254 | ||
1255 | def error(msg, *args, **kwargs): | |
1256 | """ | |
1257 | Log a message with severity 'ERROR' on the root logger. | |
1258 | """ | |
1259 | if len(root.handlers) == 0: | |
1260 | basicConfig() | |
1261 | apply(root.error, (msg,)+args, kwargs) | |
1262 | ||
1263 | def exception(msg, *args): | |
1264 | """ | |
1265 | Log a message with severity 'ERROR' on the root logger, | |
1266 | with exception information. | |
1267 | """ | |
1268 | apply(error, (msg,)+args, {'exc_info': 1}) | |
1269 | ||
1270 | def warning(msg, *args, **kwargs): | |
1271 | """ | |
1272 | Log a message with severity 'WARNING' on the root logger. | |
1273 | """ | |
1274 | if len(root.handlers) == 0: | |
1275 | basicConfig() | |
1276 | apply(root.warning, (msg,)+args, kwargs) | |
1277 | ||
1278 | warn = warning | |
1279 | ||
1280 | def info(msg, *args, **kwargs): | |
1281 | """ | |
1282 | Log a message with severity 'INFO' on the root logger. | |
1283 | """ | |
1284 | if len(root.handlers) == 0: | |
1285 | basicConfig() | |
1286 | apply(root.info, (msg,)+args, kwargs) | |
1287 | ||
1288 | def debug(msg, *args, **kwargs): | |
1289 | """ | |
1290 | Log a message with severity 'DEBUG' on the root logger. | |
1291 | """ | |
1292 | if len(root.handlers) == 0: | |
1293 | basicConfig() | |
1294 | apply(root.debug, (msg,)+args, kwargs) | |
1295 | ||
1296 | def log(level, msg, *args, **kwargs): | |
1297 | """ | |
1298 | Log 'msg % args' with the integer severity 'level' on the root logger. | |
1299 | """ | |
1300 | if len(root.handlers) == 0: | |
1301 | basicConfig() | |
1302 | apply(root.log, (level, msg)+args, kwargs) | |
1303 | ||
1304 | def disable(level): | |
1305 | """ | |
1306 | Disable all logging calls less severe than 'level'. | |
1307 | """ | |
1308 | root.manager.disable = level | |
1309 | ||
1310 | def shutdown(): | |
1311 | """ | |
1312 | Perform any cleanup actions in the logging system (e.g. flushing | |
1313 | buffers). | |
1314 | ||
1315 | Should be called at application exit. | |
1316 | """ | |
1317 | for h in _handlerList[:]: # was _handlers.keys(): | |
1318 | #errors might occur, for example, if files are locked | |
1319 | #we just ignore them | |
1320 | try: | |
1321 | h.flush() | |
1322 | h.close() | |
1323 | except: | |
1324 | pass | |
1325 | ||
1326 | #Let's try and shutdown automatically on application exit... | |
1327 | try: | |
1328 | import atexit | |
1329 | atexit.register(shutdown) | |
1330 | except ImportError: # for Python versions < 2.0 | |
1331 | def exithook(status, old_exit=sys.exit): | |
1332 | try: | |
1333 | shutdown() | |
1334 | finally: | |
1335 | old_exit(status) | |
1336 | ||
1337 | sys.exit = exithook |