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