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