ia64/xen-unstable
changeset 1863:b5427b1bb6c1
bitkeeper revision 1.1108.1.8 (40ffc44fV2fs0W-8bRw5B0XcBdmAsw)
Add python 2.3 logging to the tree. Only install
if not present. Convert xend to use logging.
Add python 2.3 logging to the tree. Only install
if not present. Convert xend to use logging.
line diff
1.1 --- a/.rootkeys Wed Jul 21 15:41:08 2004 +0000 1.2 +++ b/.rootkeys Thu Jul 22 13:42:39 2004 +0000 1.3 @@ -311,6 +311,57 @@ 40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/mis 1.4 4056f5155QYZdsk-1fLdjsZPFTnlhg tools/misc/xensymoops.py 1.5 40cf2937dqM1jWW87O5OoOYND8leuA tools/misc/xm 1.6 40c9c468icGyC5RAF1bRKsCXPDCvsA tools/python/Makefile 1.7 +40ffc44dOwe1CcYXGCkYHdG_NxcccA tools/python/logging/logging-0.4.9.2/PKG-INFO 1.8 +40ffc44dpqpgqgrnLfR70PsiBc3liA tools/python/logging/logging-0.4.9.2/README.txt 1.9 +40ffc44ddfLckno4Gvzi3vZxwelZHQ tools/python/logging/logging-0.4.9.2/default.css 1.10 +40ffc44dKSkczdvpd_x7rWGH4_BRIQ tools/python/logging/logging-0.4.9.2/liblogging.tex 1.11 +40ffc44d2O51abh5t-1VTZfqhbS1ZQ tools/python/logging/logging-0.4.9.2/logging/__init__.py 1.12 +40ffc44dT8ustodG0hDjYMCzQ8UCbA tools/python/logging/logging-0.4.9.2/logging/config.py 1.13 +40ffc44dqbwdCcq6XgwpTvrUrABhhw tools/python/logging/logging-0.4.9.2/logging/handlers.py 1.14 +40ffc44dVEL3QwvZx_Rcl3d41WxMRQ tools/python/logging/logging-0.4.9.2/python_logging.html 1.15 +40ffc44dXypIfRTyuTD48cN0o-gAXg tools/python/logging/logging-0.4.9.2/setup.py 1.16 +40ffc44dqqdkY-Ox_eoPuNmQR0_ebw tools/python/logging/logging-0.4.9.2/test/app.py 1.17 +40ffc44d9vEJEV_44B-23sJHkT1-gA tools/python/logging/logging-0.4.9.2/test/critical.ini 1.18 +40ffc44dA5BiSVip8DlCh0DfAaQzbg tools/python/logging/logging-0.4.9.2/test/debug.ini 1.19 +40ffc44dAuxUnJx7Fu2puaiNheBRkg tools/python/logging/logging-0.4.9.2/test/error.ini 1.20 +40ffc44dkAIw1gbAzj_XiQyoru93_Q tools/python/logging/logging-0.4.9.2/test/events.xml 1.21 +40ffc44eZwnQ4wGs1zVaAGsnEoyz6Q tools/python/logging/logging-0.4.9.2/test/log_test.py 1.22 +40ffc44epbz06Y2nCkuYotfQFY4bJQ tools/python/logging/logging-0.4.9.2/test/log_test0.py 1.23 +40ffc44eVNdi9lXQOZ2n7yT1DXVQRQ tools/python/logging/logging-0.4.9.2/test/log_test1.py 1.24 +40ffc44eeqydHPpipbO4oVhRt90v0A tools/python/logging/logging-0.4.9.2/test/log_test10.py 1.25 +40ffc44eSum6e6Y_sh7hRBHnnRurfw tools/python/logging/logging-0.4.9.2/test/log_test11.py 1.26 +40ffc44euWPhfnbZw64ShBIrZot84A tools/python/logging/logging-0.4.9.2/test/log_test12.py 1.27 +40ffc44ekj8Hdg-2SLb0qdqJzGkXdA tools/python/logging/logging-0.4.9.2/test/log_test13.py 1.28 +40ffc44e5DnmO4OEa54mS8Q9AgP3rg tools/python/logging/logging-0.4.9.2/test/log_test14.py 1.29 +40ffc44e6uWMQdikNEzYeNeFewGQew tools/python/logging/logging-0.4.9.2/test/log_test15.py 1.30 +40ffc44eHJ_XsDp2Le-qc96G2n7GdQ tools/python/logging/logging-0.4.9.2/test/log_test16.py 1.31 +40ffc44eCIq8wSc2UI16VfkLPlW-SQ tools/python/logging/logging-0.4.9.2/test/log_test17.py 1.32 +40ffc44eHWycPlgiEpt8pE8xYTbUkg tools/python/logging/logging-0.4.9.2/test/log_test18.py 1.33 +40ffc44eeRuZcrB3tQzfrQnh22NBow tools/python/logging/logging-0.4.9.2/test/log_test19.py 1.34 +40ffc44e6jQPP-ASsVux4-ERGuDrmQ tools/python/logging/logging-0.4.9.2/test/log_test2.py 1.35 +40ffc44eNHf6r77J1VCNedKPTufY8Q tools/python/logging/logging-0.4.9.2/test/log_test20.py 1.36 +40ffc44emS2gplqyEwbcLS43QNrnyA tools/python/logging/logging-0.4.9.2/test/log_test21.py 1.37 +40ffc44e1Ojd79zACM2KAnXZyO3Nuw tools/python/logging/logging-0.4.9.2/test/log_test22.py 1.38 +40ffc44ektXcwDnK4h4HqMHFSTA3BA tools/python/logging/logging-0.4.9.2/test/log_test3.ini 1.39 +40ffc44e8ka-b5_nPYzWn0hXDSagMw tools/python/logging/logging-0.4.9.2/test/log_test3.py 1.40 +40ffc44eVmFkQt7FaHxspmMV7IZLxw tools/python/logging/logging-0.4.9.2/test/log_test4.py 1.41 +40ffc44eYJ7tjlUpS5bIF9I8YKK39g tools/python/logging/logging-0.4.9.2/test/log_test5.py 1.42 +40ffc44eo9GvB3GvC2Aoaxu74ffS4A tools/python/logging/logging-0.4.9.2/test/log_test6.py 1.43 +40ffc44eajjKGx6tj2nOVuYCfy-PoA tools/python/logging/logging-0.4.9.2/test/log_test7.py 1.44 +40ffc44eEM_uMfIGRNoxRSP7_jpc7w tools/python/logging/logging-0.4.9.2/test/log_test8.py 1.45 +40ffc44e4NBnAweOds3owURsSHZKyQ tools/python/logging/logging-0.4.9.2/test/log_test9.py 1.46 +40ffc44eu_SYlzJ464qUFb8fdSGHsg tools/python/logging/logging-0.4.9.2/test/logconf.ini 1.47 +40ffc44eTnvlvCSmjgGgfcOBm6SSAw tools/python/logging/logging-0.4.9.2/test/logconf.py 1.48 +40ffc44egchRehfcmsPX0WdV06yp_w tools/python/logging/logging-0.4.9.2/test/logging.dtd 1.49 +40ffc44ee9peTFswy96mwgBslmqEvQ tools/python/logging/logging-0.4.9.2/test/logging.xml 1.50 +40ffc44ekRTWScJDGTe7k2aAp_ltRg tools/python/logging/logging-0.4.9.2/test/logrecv.ini 1.51 +40ffc44eeQBA_QbbwrucZfYtksuEMA tools/python/logging/logging-0.4.9.2/test/logrecv.py 1.52 +40ffc44eECASCQD_QL3wJd4dyK2KVg tools/python/logging/logging-0.4.9.2/test/myapp.py 1.53 +40ffc44eUwKMOAwPTIBq0A8N8b56HQ tools/python/logging/logging-0.4.9.2/test/mymodule.py 1.54 +40ffc44evyvayldKLSsizMmsDpBtkQ tools/python/logging/logging-0.4.9.2/test/stderr.exp 1.55 +40ffc44eb-39RIR551oZoTiK11amSw tools/python/logging/logging-0.4.9.2/test/stdout.exp 1.56 +40ffc44eGvzBilqBZEozKaMHz-HdxA tools/python/logging/logging-0.4.9.2/test/warn.ini 1.57 +40ffc44eLXLuINsYi8eG0oJ6a2dSRA tools/python/logging/setup.py 1.58 40c9c469n2RRwCmjWdjdyyVRWKmgWg tools/python/setup.py 1.59 40dc4076hGpwa8-sWRN0jtXZeQJuKg tools/python/xen/__init__.py 1.60 40dfd40aMOhnw_cQLve9462UR5yYxQ tools/python/xen/lowlevel/__init__.py 1.61 @@ -336,6 +387,7 @@ 40c9c468fSl3H3IypyT0ppkbb0ZT9A tools/pyt 1.62 40c9c468bbKq3uC7_fuNUkiMMjArdw tools/python/xen/xend/XendDomainConfig.py 1.63 40c9c4685ykq87_n1kVUbMr9flx9fg tools/python/xen/xend/XendDomainInfo.py 1.64 40f50d99YiiaMI1fZBh1VCDFLD57qg tools/python/xen/xend/XendError.py 1.65 +40ffc44eGsgTEY355E3nN4mPLZHhMQ tools/python/xen/xend/XendLogging.py 1.66 40c9c46854nsHmuxHQHncKk5rAs5NA tools/python/xen/xend/XendMigrate.py 1.67 40c9c468M96gA1EYDvNa5w5kQNYLFA tools/python/xen/xend/XendNode.py 1.68 40c9c4686jruMyZIqiaZRMiMoqMJtg tools/python/xen/xend/XendRoot.py
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/tools/python/logging/logging-0.4.9.2/PKG-INFO Thu Jul 22 13:42:39 2004 +0000 2.3 @@ -0,0 +1,25 @@ 2.4 +Metadata-Version: 1.0 2.5 +Name: logging 2.6 +Version: 0.4.9.2 2.7 +Summary: A logging module for Python 2.8 +Home-page: http://www.red-dove.com/python_logging.html 2.9 +Author: Vinay Sajip 2.10 +Author-email: vinay_sajip@red-dove.com 2.11 +License: Copyright (C) 2001-2004 by Vinay Sajip. All Rights Reserved. 2.12 + 2.13 +Permission to use, copy, modify, and distribute this software and its 2.14 +documentation for any purpose and without fee is hereby granted, 2.15 +provided that the above copyright notice appear in all copies and that 2.16 +both that copyright notice and this permission notice appear in 2.17 +supporting documentation, and that the name of Vinay Sajip 2.18 +not be used in advertising or publicity pertaining to distribution 2.19 +of the software without specific, written prior permission. 2.20 + 2.21 +VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 2.22 +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 2.23 +VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 2.24 +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 2.25 +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 2.26 +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 2.27 +Description: This module is intended to provide a standard error logging mechanism in Python as per PEP 282. 2.28 +Platform: UNKNOWN
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/tools/python/logging/logging-0.4.9.2/README.txt Thu Jul 22 13:42:39 2004 +0000 3.3 @@ -0,0 +1,311 @@ 3.4 +This module is intended to provide error logging functionality for Python 3.5 +programs. It aims to be aligned with Python Enhancement Proposal (PEP) 282. 3.6 + 3.7 +For more information on the package itself, see python_logging.html. In it, 3.8 +there is a description of each file in the distribution. 3.9 +You can also use pydoc to browse the interfaces. 3.10 + 3.11 +Change History 3.12 +-------------- 3.13 + 3.14 +Version Date Description 3.15 +============================================================================= 3.16 +0.4.9.2 28 Feb 2004 Traceback text is now cached. 3.17 + Tracebacks can be propagated across sockets as text. 3.18 + Added makeLogRecord() to allow a LogRecord to be 3.19 + created from a dictionary. 3.20 + Closing a handler now removes it from the internal list 3.21 + used by shutdown(). 3.22 + Made close() call flush() for handlers where this makes 3.23 + sense (thanks to Jim Jewett). 3.24 + The exc_info keyword parameter can be used to pass an 3.25 + exception tuple as well as a flag indicating that the 3.26 + current exception should be logged. 3.27 + A shutdown hook is registered to call shutdown() on 3.28 + application (Python) exit (thanks to Jim Jewett). 3.29 + Removed redundant error check in setLoggerClass(). 3.30 + Added RESET_ERROR to logging.config. 3.31 + SocketHandler now uses an exponential backoff strategy 3.32 + (thanks to Robert Olson). 3.33 + Minor documentation corrections. 3.34 +----------------------------------------------------------------------------- 3.35 +0.4.8 22 Apr 2003 Made _listener global in stopListening(). 3.36 + Made listen() correctly pass the specified port. 3.37 + Removed some redundant imports in __init__.py. 3.38 + Added the record being processed as a parameter to 3.39 + handleError (thanks to Gordon den Otter for the idea). 3.40 + Handler.handle returns the result of applying the 3.41 + filter to the record (thanks to Gordon den Otter for 3.42 + the idea). 3.43 + Added a seek(0, 2) in RotatingFileHandler before the 3.44 + tell() call. This is because under Windows, tell() 3.45 + returns 0 until the first actual write (thanks to 3.46 + Gordon den Otter for the patch). 3.47 + Altered findCaller to not use inspect (thanks to 3.48 + Jeremy Hylton for the patch). 3.49 + Renamed warn and WARN to warning and WARNING. This may 3.50 + break existing code, but the standard Python module 3.51 + will use warning/WARNING rather than warn/WARN. The 3.52 + fatal and FATAL synonyms for critical and CRITICAL 3.53 + have also been removed. 3.54 + Added defaultEncoding and some support for encoding 3.55 + Unicode messages (thanks to Stéphane Bidoul for the 3.56 + suggestion). 3.57 + Added process ID to the list of LogRecord attributes. 3.58 + Modified Logger.removeHandler so that it does not 3.59 + close the handler on removal. 3.60 + Modified SMTPHandler to treat a single "to address" 3.61 + correctly (thanks to Anthony Baxter). 3.62 + Modified SMTPHandler to add a date header to the SMTP 3.63 + message (thanks to David Driver for the suggestion). 3.64 + Modified HTTPHandler to factor out the mapping of 3.65 + a LogRecord to a dictionary (thanks to Franz Glasner 3.66 + for the patch). 3.67 +----------------------------------------------------------------------------- 3.68 +0.4.7 15 Nov 2002 Made into a package with three modules: __init__ (the 3.69 + core code), handlers (all handlers other than 3.70 + FileHandler and its bases) and config (all the config 3.71 + stuff). Before doing this: 3.72 + Updated docstrings to include a short line, then a 3.73 + blank line, then more descriptive text. 3.74 + Renamed 'lvl' to 'level' in various functions. 3.75 + Changed FileHandler to use "a" and "w" instead of "a+" 3.76 + and "w+". 3.77 + Moved log file rotation functionality from FileHandler 3.78 + to a new class RotatingFileHandler. 3.79 + Improved docstring describing rollover. 3.80 + Updated makePickle to use 4-byte length and struct 3.81 + module, likewise logrecv.py. Also updated on-the-fly 3.82 + config reader to use 4-byte length/struct module. 3.83 + Altered ConfigParser test to look at 'readline' rather 3.84 + than 'read'. 3.85 + Added optional "defaults" argument to fileConfig, to 3.86 + be passed to ConfigParser. 3.87 + Renamed ALL to NOTSET to avoid confusion. 3.88 + Commented out getRootLogger(), as obsolete. 3.89 + To do regression testing, run log_test.py and compare 3.90 + the created files stdout.log and stderr.log against 3.91 + the files stdout.exp and stderr.exp. They should match 3.92 + except fir a couple of exception messages which give 3.93 + absolute file paths. 3.94 + Updated python_logging.html to remove links to 3.95 + logging_pydoc.html, which has been removed from the 3.96 + distribution. 3.97 + Changed default for raiseExceptions to 1. 3.98 +----------------------------------------------------------------------------- 3.99 +0.4.6 08 Jul 2002 Added raiseExceptions to allow conditional propagation 3.100 + of exceptions which occur during handling. 3.101 + Added converter to Formatter to allow use of any 3.102 + function to convert time from seconds to a tuple. It 3.103 + still defaults to time.localtime but now you can also 3.104 + use time.gmtime. 3.105 + Added log_test22.py to test the conversion feature. 3.106 + Changed rootlogger default level to WARN - was DEBUG. 3.107 + Updated some docstrings. 3.108 + Moved import of threading to where thread is imported. 3.109 + If either is unavailable, threading support is off. 3.110 + Updated minor defects in python_logging.html. 3.111 + Check to see if ConfigParser has readfp method; if it 3.112 + does and an object with a 'read' method is passed in, 3.113 + assumes a file-like object and uses readfp to read it 3.114 + in. 3.115 +----------------------------------------------------------------------------- 3.116 +0.4.5 04 Jun 2002 Fixed bug which caused problem if no args to message 3.117 + (suggested by Hye-Shik Chang). 3.118 + Fixed bug in _fixupParents (thanks to Nicholas Veeser) 3.119 + and added log_test19.py as a test case for this bug. 3.120 + Added getMessage to LogRecord (code was moved here from 3.121 + Formatter.format) 3.122 + Applied str() to record.msg to allow arbitrary classes 3.123 + to determine the formatting (as msg can now be a class 3.124 + instance). 3.125 + Table of Contents added to python_logging.html, the 3.126 + section on Loggers updated, and the logconf.ini file 3.127 + section annotated. 3.128 + Added log_test20.py which demonstrates how to use 3.129 + class instances to provide alternatives to numeric 3.130 + severities as mechanisms for control of logging. 3.131 + Added log_test21.py which builds on log_test20.py to 3.132 + show how you can use a regular expression-based Filter 3.133 + for flexible matching similar to e.g. Protomatter 3.134 + Syslog, where you can filter on e.g. "a.*" or "*.b" or 3.135 + "a.*.c". 3.136 + _levelNames changed to contain reverse mappings as well 3.137 + as forward mappings (leveltext->level as well as level 3.138 + -> leveltext). The reverse mappings are used by 3.139 + fileConfig(). 3.140 + fileConfig() now more forgiving of missing options in 3.141 + .ini file - sensible defaults now used when some 3.142 + options are absent. Also, eval() is used less when 3.143 + interpreting .ini file contents - int() and dict lookup 3.144 + are used in more places. 3.145 +----------------------------------------------------------------------------- 3.146 +0.4.4 02 May 2002 getEffectiveLevel() returns ALL instead of None when 3.147 + nothing found. Modified references to level=0 to 3.148 + level=ALL in a couple of places. 3.149 + SocketHandler now inherits from Handler (it used to 3.150 + inherit from StreamHandler, for no good reason). 3.151 + getLock() renamed to createLock(). 3.152 + Docstring tidy-ups, and some tidying up of 3.153 + DatagramHandler. 3.154 + Factored out unpickling in logrecv.py. 3.155 + Added log_test18.py to illustrate MatchFilter, which is 3.156 + a general matching filter. 3.157 + Improved FileHandler.doRollover() so that the base 3.158 + file name is always the most recent, then .1, then .2 3.159 + etc. up to the maximum backup count. Renamed formal 3.160 + args and attributes used in rollover. 3.161 + Changed LogRecord attributes lvl -> levelno, level -> 3.162 + levelname (less ambiguity) 3.163 + Formatter.format searches for "%(asctime)" rather than 3.164 + "(asctime)" 3.165 + Renamed _start_time to _startTime 3.166 + Formatter.formatTime now returns the time 3.167 + Altered logrecv.py to support stopping servers 3.168 + programmatically 3.169 + Added log_test.py as overall test harness 3.170 + basicConfig() can now be safely called more than once 3.171 + Modified test scripts to make it easier to call them 3.172 + from log_test.py 3.173 + Moved SOAPHandler from core to log_test13.py. It's not 3.174 + general enough to be in the core; most production use 3.175 + will have differing RPC signatures. 3.176 +----------------------------------------------------------------------------- 3.177 +0.4.3 14 Apr 2002 Bug fix one-off error message to go to sys.stderr 3.178 + rather than sys.stdout. 3.179 + logrecv.py fix TCP for busy network. 3.180 + Thread safety - added locking to Handler and for shared 3.181 + data in module, and log_test16.py to test it. 3.182 + Added socket listener to allow on-the-fly configuration 3.183 + and added log_test17.py to test it. 3.184 +----------------------------------------------------------------------------- 3.185 +0.4.2 11 Apr 2002 Bug fix fileConfig() - setup of MemoryHandler target 3.186 + and errors when loggers have no handlers set or 3.187 + handlers have no formatters set 3.188 + logconf.py - seems to hang if window closed when combo 3.189 + dropdown is showing - added code to close popup on exit 3.190 + Some tweaks to _srcfile computation (normpath added) 3.191 + findCaller() optimized, now a lot faster! 3.192 + Logger.removeHandler now closes the handler before 3.193 + removing it 3.194 + fileConfig() removes existing handlers before adding 3.195 + the new set, to avoid memory leakage when repeated 3.196 + calls are made 3.197 + Fixed logrecv.py bug which hogged CPU time when TCP 3.198 + connection was closed from the client 3.199 + Added log_test14.py to demonstrate/test a DBHandler 3.200 + which writes logging records into an RDBMS using the 3.201 + Python Database API 2.0 (to run, you need something 3.202 + which supports this already installed - I tested with 3.203 + mxODBC) 3.204 + Made getLogger name argument optional - returns root 3.205 + logger if omitted 3.206 + Altered Filter to take a string initializer, filtering 3.207 + a sub-hierarchy rooted at a particular point (idea from 3.208 + Denis S. Otkidach). 3.209 + Added log_test15.py to test Filter initializer 3.210 + Minor docstring changes 3.211 +----------------------------------------------------------------------------- 3.212 +0.4.1 03 Apr 2002 Bug fix SMTPHandler - extra \r\n needed (Oleg Orlov) 3.213 + Added BufferingHandler, BufferingFormatter 3.214 + Renamed getChainedPriority to getEffectiveLevel 3.215 + Removed Logger.getRoot as it is redundant 3.216 + Added log_test9.py to test Buffering classes and 3.217 + to show an XMLFormatter example. 3.218 + Added setLoggerClass. 3.219 + Added log_test10.py to test setLoggerClass, using an 3.220 + example Logger-derived class which outputs exception 3.221 + info even for DEBUG level logging calls 3.222 + Added log_test11.py to test a buffering implementation 3.223 + of SMTPHandler 3.224 + Changed logging call implementation to allow keyword 3.225 + arguments (Kevin Butler and others) 3.226 + Changed default SysLogHandler implementation. 3.227 + Renamed "additive" to "propagate" as it better 3.228 + describes the attribute. 3.229 + Added HTTPHandler. 3.230 + Modified logrecv.py to remove "both" option and to add 3.231 + "HTTP" and "SOAP" options (SOAP option needs you to 3.232 + have PyXML-0.6.6 and ZSI installed - for logrecv.py 3.233 + only, and not for the core logging module itself). 3.234 + Added log_test12.py to test HTTPHandler. 3.235 + Added log_test13.py to test SOAPHandler. 3.236 + Formatted to Python source guidelines (spaces, indent 3.237 + of 4, within 80 columns). 3.238 + More method renamings (result of feedback) - _handle() 3.239 + renamed to emit(), _logRecord() renamed to handle(). 3.240 + Renamed FATAL to CRITICAL (David Goodger), but left 3.241 + fatal() and FATAL in (until PEP is changed) 3.242 + Changed configuration file format to ConfigParser 3.243 + format. 3.244 + Factored filter application functionality out to a new 3.245 + Filterer class. The isLoggable() method is renamed to 3.246 + filter() in both Filter and Filterer classes. 3.247 + Altered SMTPHandler __init__ to accept (host, port) 3.248 + for the mail internet address. 3.249 + Added GUI configurator which uses Tkinter and the new 3.250 + configuration file format. (See logconf.py and an 3.251 + example configuration file in logconf.ini) 3.252 + Altered log_test3.py to test with the new file format. 3.253 +----------------------------------------------------------------------------- 3.254 +0.4 21 Mar 2002 Incorporated comments/patches from Ollie Rutherfurd: 3.255 + -Added level filtering for handlers. 3.256 + -Return root logger if no name specified in getLogger. 3.257 + Incorporated comments from Greg Ward: 3.258 + -Added distutils setup.py script. 3.259 + Added formatter initialization in Handler.__init__. 3.260 + Tidied up docstrings. 3.261 + Added removeHandler to Logger. 3.262 + Added removeFilter to Logger and Handler. 3.263 + logrecv.py modified to keep connection alive until 3.264 + client closes it. 3.265 + SocketHandler modified to not reset connection after 3.266 + each logging event. 3.267 + Added shutdown function which closes open sockets 3.268 + Renamed DEFAULT_LOGGING_PORT->DEFAULT_TCP_LOGGING_PORT 3.269 + Added DEFAULT_UDP_LOGGING_PORT 3.270 + Added log_test4.py (example of arbitrary levels) 3.271 + Added addLevelName, changed behaviour of getLevelName 3.272 + Fixed bugs in DatagramHandler 3.273 + Added SMTPHandler implementation 3.274 + Added log_test5.py to test SMTPHandler 3.275 + Added SysLogHandler (contribution from Nicolas Untz 3.276 + based on Sam Rushing's syslog.py) 3.277 + Modified log_test1.py to add a SysLogHandler 3.278 + Added rollover functionality to FileHandler 3.279 + Added NTEventLogHandler (based on Win32 extensions) 3.280 + Added MemoryHandler implementation 3.281 + Added log_test7.py to test MemoryHandler 3.282 + Added log_test8.py to test FileHandler rollover 3.283 + Added logException method to Logger 3.284 + Added formatException method to Formatter 3.285 + Added log_test6.py to test NTEventHandler and 3.286 + logException 3.287 + Numerous internal method renamings (sorry - but better 3.288 + to do this now, rather than when we enter beta status). 3.289 +----------------------------------------------------------------------------- 3.290 +0.3 14 Mar 2002 First public release, for early feedback 3.291 +----------------------------------------------------------------------------- 3.292 +0.2 Consolidated into single file (for internal use only) 3.293 +----------------------------------------------------------------------------- 3.294 +0.1 Initial implementation (for internal use only) 3.295 +----------------------------------------------------------------------------- 3.296 + 3.297 +----------------------------------------------------------------------------- 3.298 +COPYRIGHT 3.299 +----------------------------------------------------------------------------- 3.300 +Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 3.301 + 3.302 +Permission to use, copy, modify, and distribute this software and its 3.303 +documentation for any purpose and without fee is hereby granted, 3.304 +provided that the above copyright notice appear in all copies and that 3.305 +both that copyright notice and this permission notice appear in 3.306 +supporting documentation, and that the name of Vinay Sajip 3.307 +not be used in advertising or publicity pertaining to distribution 3.308 +of the software without specific, written prior permission. 3.309 +VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 3.310 +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 3.311 +VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 3.312 +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 3.313 +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 3.314 +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/tools/python/logging/logging-0.4.9.2/default.css Thu Jul 22 13:42:39 2004 +0000 4.3 @@ -0,0 +1,32 @@ 4.4 +BODY, P, UL, OL {font-family: Verdana, Arial, Helvetica, Geneva, sans-serif ; background: white; color: black; font-size: 10pt; } 4.5 +CODE {font-family: "Courier New", Courier, Monaco, monospace ; } 4.6 +PRE {font-family: "Courier New", Courier, Monaco, monospace ; } 4.7 +.program { color: navy; font-family: "Courier New", Courier, Monaco, monospace } 4.8 +.output { color: maroon } 4.9 +HR {color: gray ; } 4.10 +.navbar { font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; background: #FFFF66 ; border-style: none none solid none; border-width: thin; padding: 2px;} 4.11 +.navarea { background: #FFFF66 ; } 4.12 +TR { font-size: 10pt } 4.13 +.red { color: #FF0000 } 4.14 +.maroon { color: #800000 } 4.15 +.comment { color: green } 4.16 +.optional { color: silver } 4.17 +.strong { font-weight: bold } 4.18 +TR.tblheading { background: silver ; } 4.19 +.notrecommended {color: #666666 ; } 4.20 +.deemphasized {color: #666666 ; } 4.21 +TABLE.densetable {font-size: 80% ; } 4.22 +TH { text-align: left; background: silver } 4.23 +TR.summaryrow { font-size: 90%; background: #00FFFF ; } 4.24 +CAPTION {font-size: 100% ; } 4.25 +.embeddedfloat {float: right; margin-left: 3em} 4.26 +.bigtd { font-size: 18pt } 4.27 +TD, .normaltd { vertical-align: top; background: #EEEEEE; font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; font-size: 10pt } 4.28 +H1, H2, H3, H4, H5, H6 {font-family: Verdana, Arial, Helvetica, Geneva, sans-serif ; } 4.29 +H4 {margin-bottom: 0.5em } 4.30 +H5, H6 {margin-bottom: 0.1em } 4.31 +A:link {color: blue ; } 4.32 +A:visited {color: purple ; } 4.33 +A:active {color: red ; } 4.34 +A { font-family: Verdana, Arial, Helvetica, Geneva, sans-serif; font-size: 10pt } 4.35 +.warning { color: red } 4.36 \ No newline at end of file
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/tools/python/logging/logging-0.4.9.2/liblogging.tex Thu Jul 22 13:42:39 2004 +0000 5.3 @@ -0,0 +1,1281 @@ 5.4 +\section{\module{logging} --- 5.5 + Logging facility for Python} 5.6 + 5.7 +\declaremodule{standard}{logging} 5.8 + 5.9 +% These apply to all modules, and may be given more than once: 5.10 + 5.11 +\moduleauthor{Vinay Sajip}{vinay_sajip@red-dove.com} 5.12 +\sectionauthor{Vinay Sajip}{vinay_sajip@red-dove.com} 5.13 + 5.14 +\modulesynopsis{Logging module for Python based on \pep{282}.} 5.15 + 5.16 +\indexii{Errors}{logging} 5.17 + 5.18 +\versionadded{2.3} 5.19 +This module defines functions and classes which implement a flexible 5.20 +error logging system for applications. 5.21 + 5.22 +Logging is performed by calling methods on instances of the 5.23 +\class{Logger} class (hereafter called \dfn{loggers}). Each instance has a 5.24 +name, and they are conceptually arranged in a name space hierarchy 5.25 +using dots (periods) as separators. For example, a logger named 5.26 +"scan" is the parent of loggers "scan.text", "scan.html" and "scan.pdf". 5.27 +Logger names can be anything you want, and indicate the area of an 5.28 +application in which a logged message originates. 5.29 + 5.30 +Logged messages also have levels of importance associated with them. 5.31 +The default levels provided are \constant{DEBUG}, \constant{INFO}, 5.32 +\constant{WARNING}, \constant{ERROR} and \constant{CRITICAL}. As a 5.33 +convenience, you indicate the importance of a logged message by calling 5.34 +an appropriate method of \class{Logger}. The methods are 5.35 +\method{debug()}, \method{info()}, \method{warning()}, \method{error()} and 5.36 +\method{critical()}, which mirror the default levels. You are not 5.37 +constrained to use these levels: you can specify your own and use a 5.38 +more general \class{Logger} method, \method{log()}, which takes an 5.39 +explicit level argument. 5.40 + 5.41 +Levels can also be associated with loggers, being set either by the 5.42 +developer or through loading a saved logging configuration. When a 5.43 +logging method is called on a logger, the logger compares its own 5.44 +level with the level associated with the method call. If the logger's 5.45 +level is higher than the method call's, no logging message is actually 5.46 +generated. This is the basic mechanism controlling the verbosity of 5.47 +logging output. 5.48 + 5.49 +Logging messages are encoded as instances of the \class{LogRecord} class. 5.50 +When a logger decides to actually log an event, an \class{LogRecord} 5.51 +instance is created from the logging message. 5.52 + 5.53 +Logging messages are subjected to a dispatch mechanism through the 5.54 +use of \dfn{handlers}, which are instances of subclasses of the 5.55 +\class{Handler} class. Handlers are responsible for ensuring that a logged 5.56 +message (in the form of a \class{LogRecord}) ends up in a particular 5.57 +location (or set of locations) which is useful for the target audience for 5.58 +that message (such as end users, support desk staff, system administrators, 5.59 +developers). Handlers are passed \class{LogRecord} instances intended for 5.60 +particular destinations. Each logger can have zero, one or more handlers 5.61 +associated with it (via the \method{addHandler} method of \class{Logger}). 5.62 +In addition to any handlers directly associated with a logger, 5.63 +\emph{all handlers associated with all ancestors of the logger} are 5.64 +called to dispatch the message. 5.65 + 5.66 +Just as for loggers, handlers can have levels associated with them. 5.67 +A handler's level acts as a filter in the same way as a logger's level does. 5.68 +If a handler decides to actually dispatch an event, the \method{emit()} method 5.69 +is used to send the message to its destination. Most user-defined subclasses 5.70 +of \class{Handler} will need to override this \method{emit()}. 5.71 + 5.72 +In addition to the base \class{Handler} class, many useful subclasses 5.73 +are provided: 5.74 + 5.75 +\begin{enumerate} 5.76 + 5.77 +\item \class{StreamHandler} instances send error messages to 5.78 +streams (file-like objects). 5.79 + 5.80 +\item \class{FileHandler} instances send error messages to disk 5.81 +files. 5.82 + 5.83 +\item \class{RotatingFileHandler} instances send error messages to disk 5.84 +files, with support for maximum log file sizes and log file rotation. 5.85 + 5.86 +\item \class{SocketHandler} instances send error messages to 5.87 +TCP/IP sockets. 5.88 + 5.89 +\item \class{DatagramHandler} instances send error messages to UDP 5.90 +sockets. 5.91 + 5.92 +\item \class{SMTPHandler} instances send error messages to a 5.93 +designated email address. 5.94 + 5.95 +\item \class{SysLogHandler} instances send error messages to a 5.96 +\UNIX{} syslog daemon, possibly on a remote machine. 5.97 + 5.98 +\item \class{NTEventLogHandler} instances send error messages to a 5.99 +Windows NT/2000/XP event log. 5.100 + 5.101 +\item \class{MemoryHandler} instances send error messages to a 5.102 +buffer in memory, which is flushed whenever specific criteria are 5.103 +met. 5.104 + 5.105 +\item \class{HTTPHandler} instances send error messages to an 5.106 +HTTP server using either \samp{GET} or \samp{POST} semantics. 5.107 + 5.108 +\end{enumerate} 5.109 + 5.110 +The \class{StreamHandler} and \class{FileHandler} classes are defined 5.111 +in the core logging package. The other handlers are defined in a sub- 5.112 +module, \module{logging.handlers}. (There is also another sub-module, 5.113 +\module{logging.config}, for configuration functionality.) 5.114 + 5.115 +Logged messages are formatted for presentation through instances of the 5.116 +\class{Formatter} class. They are initialized with a format string 5.117 +suitable for use with the \% operator and a dictionary. 5.118 + 5.119 +For formatting multiple messages in a batch, instances of 5.120 +\class{BufferingFormatter} can be used. In addition to the format string 5.121 +(which is applied to each message in the batch), there is provision for 5.122 +header and trailer format strings. 5.123 + 5.124 +When filtering based on logger level and/or handler level is not enough, 5.125 +instances of \class{Filter} can be added to both \class{Logger} and 5.126 +\class{Handler} instances (through their \method{addFilter()} method). 5.127 +Before deciding to process a message further, both loggers and handlers 5.128 +consult all their filters for permission. If any filter returns a false 5.129 +value, the message is not processed further. 5.130 + 5.131 +The basic \class{Filter} functionality allows filtering by specific logger 5.132 +name. If this feature is used, messages sent to the named logger and its 5.133 +children are allowed through the filter, and all others dropped. 5.134 + 5.135 +In addition to the classes described above, there are a number of module- 5.136 +level functions. 5.137 + 5.138 +\begin{funcdesc}{getLogger}{\optional{name}} 5.139 +Return a logger with the specified name or, if no name is specified, return 5.140 +a logger which is the root logger of the hierarchy. 5.141 + 5.142 +All calls to this function with a given name return the same logger instance. 5.143 +This means that logger instances never need to be passed between different 5.144 +parts of an application. 5.145 +\end{funcdesc} 5.146 + 5.147 +\begin{funcdesc}{debug}{msg\optional{, *args\optional{, **kwargs}}} 5.148 +Logs a message with level \constant{DEBUG} on the root logger. 5.149 +The \var{msg} is the message format string, and the \var{args} are the 5.150 +arguments which are merged into \var{msg}. The only keyword argument in 5.151 +\var{kwargs} which is inspected is \var{exc_info} which, if it does not 5.152 +evaluate as false, causes exception information (via a call to 5.153 +\function{sys.exc_info()}) to be added to the logging message. 5.154 +\end{funcdesc} 5.155 + 5.156 +\begin{funcdesc}{info}{msg\optional{, *args\optional{, **kwargs}}} 5.157 +Logs a message with level \constant{INFO} on the root logger. 5.158 +The arguments are interpreted as for \function{debug()}. 5.159 +\end{funcdesc} 5.160 + 5.161 +\begin{funcdesc}{warning}{msg\optional{, *args\optional{, **kwargs}}} 5.162 +Logs a message with level \constant{WARNING} on the root logger. 5.163 +The arguments are interpreted as for \function{debug()}. 5.164 +\end{funcdesc} 5.165 + 5.166 +\begin{funcdesc}{error}{msg\optional{, *args\optional{, **kwargs}}} 5.167 +Logs a message with level \constant{ERROR} on the root logger. 5.168 +The arguments are interpreted as for \function{debug()}. 5.169 +\end{funcdesc} 5.170 + 5.171 +\begin{funcdesc}{critical}{msg\optional{, *args\optional{, **kwargs}}} 5.172 +Logs a message with level \constant{CRITICAL} on the root logger. 5.173 +The arguments are interpreted as for \function{debug()}. 5.174 +\end{funcdesc} 5.175 + 5.176 +\begin{funcdesc}{exception}{msg\optional{, *args}} 5.177 +Logs a message with level \constant{ERROR} on the root logger. 5.178 +The arguments are interpreted as for \function{debug()}. Exception info 5.179 +is added to the logging message. This function should only be called 5.180 +from an exception handler. 5.181 +\end{funcdesc} 5.182 + 5.183 +\begin{funcdesc}{disable}{lvl} 5.184 +Provides an overriding level \var{lvl} for all loggers which takes 5.185 +precedence over the logger's own level. When the need arises to 5.186 +temporarily throttle logging output down across the whole application, 5.187 +this function can be useful. 5.188 +\end{funcdesc} 5.189 + 5.190 +\begin{funcdesc}{addLevelName}{lvl, levelName} 5.191 +Associates level \var{lvl} with text \var{levelName} in an internal 5.192 +dictionary, which is used to map numeric levels to a textual 5.193 +representation, for example when a \class{Formatter} formats a message. 5.194 +This function can also be used to define your own levels. The only 5.195 +constraints are that all levels used must be registered using this 5.196 +function, levels should be positive integers and they should increase 5.197 +in increasing order of severity. 5.198 +\end{funcdesc} 5.199 + 5.200 +\begin{funcdesc}{getLevelName}{lvl} 5.201 +Returns the textual representation of logging level \var{lvl}. If the 5.202 +level is one of the predefined levels \constant{CRITICAL}, 5.203 +\constant{ERROR}, \constant{WARNING}, \constant{INFO} or \constant{DEBUG} 5.204 +then you get the corresponding string. If you have associated levels 5.205 +with names using \function{addLevelName()} then the name you have associated 5.206 +with \var{lvl} is returned. Otherwise, the string "Level \%s" \% lvl is 5.207 +returned. 5.208 +\end{funcdesc} 5.209 + 5.210 +\begin{funcdesc}{makeLogRecord}{attrdict} 5.211 +Creates and returns a new \class{LogRecord} instance whose attributes are 5.212 +defined by \var{attrdict}. This function is useful for taking a pickled 5.213 +\class{LogRecord} attribute dictionary, sent over a socket, and reconstituting 5.214 +it as a \class{LogRecord} instance at the receiving end. 5.215 +\end{funcdesc} 5.216 + 5.217 +\begin{funcdesc}{basicConfig}{} 5.218 +Does basic configuration for the logging system by creating a 5.219 +\class{StreamHandler} with a default \class{Formatter} and adding it to 5.220 +the root logger. The functions \function{debug()}, \function{info()}, 5.221 +\function{warning()}, \function{error()} and \function{critical()} will call 5.222 +\function{basicConfig()} automatically if no handlers are defined for the 5.223 +root logger. 5.224 +\end{funcdesc} 5.225 + 5.226 +\begin{funcdesc}{shutdown}{} 5.227 +Informs the logging system to perform an orderly shutdown by flushing and 5.228 +closing all handlers. 5.229 +\end{funcdesc} 5.230 + 5.231 +\begin{funcdesc}{setLoggerClass}{klass} 5.232 +Tells the logging system to use the class \var{klass} when instantiating a 5.233 +logger. The class should define \method{__init__()} such that only a name 5.234 +argument is required, and the \method{__init__()} should call 5.235 +\method{Logger.__init__()}. This function is typically called before any 5.236 +loggers are instantiated by applications which need to use custom logger 5.237 +behavior. 5.238 +\end{funcdesc} 5.239 + 5.240 + 5.241 +\begin{seealso} 5.242 + \seepep{282}{A Logging System} 5.243 + {The proposal which described this feature for inclusion in 5.244 + the Python standard library.} 5.245 + \seelink{http://www.red-dove.com/python_logging.html} 5.246 + {Original Python \module{logging} package} 5.247 + {This is the original source for the \module{logging} 5.248 + package. The version of the package available from this 5.249 + site is suitable for use with Python 1.5.2, 2.1.x and 2.2.x, which 5.250 + do not include the \module{logging} package in the standard 5.251 + library.} 5.252 +\end{seealso} 5.253 + 5.254 + 5.255 +\subsection{Logger Objects} 5.256 + 5.257 +Loggers have the following attributes and methods. Note that Loggers are 5.258 +never instantiated directly, but always through the module-level function 5.259 +\function{logging.getLogger(name)}. 5.260 + 5.261 +\begin{datadesc}{propagate} 5.262 +If this evaluates to false, logging messages are not passed by this 5.263 +logger or by child loggers to higher level (ancestor) loggers. The 5.264 +constructor sets this attribute to 1. 5.265 +\end{datadesc} 5.266 + 5.267 +\begin{methoddesc}{setLevel}{lvl} 5.268 +Sets the threshold for this logger to \var{lvl}. Logging messages 5.269 +which are less severe than \var{lvl} will be ignored. When a logger is 5.270 +created, the level is set to \constant{NOTSET} (which causes all messages 5.271 +to be processed in the root logger, or delegation to the parent in non-root 5.272 +loggers). 5.273 +\end{methoddesc} 5.274 + 5.275 +\begin{methoddesc}{isEnabledFor}{lvl} 5.276 +Indicates if a message of severity \var{lvl} would be processed by 5.277 +this logger. This method checks first the module-level level set by 5.278 +\function{logging.disable(lvl)} and then the logger's effective level as 5.279 +determined by \method{getEffectiveLevel()}. 5.280 +\end{methoddesc} 5.281 + 5.282 +\begin{methoddesc}{getEffectiveLevel}{} 5.283 +Indicates the effective level for this logger. If a value other than 5.284 +\constant{NOTSET} has been set using \method{setLevel()}, it is returned. 5.285 +Otherwise, the hierarchy is traversed towards the root until a value 5.286 +other than \constant{NOTSET} is found, and that value is returned. 5.287 +\end{methoddesc} 5.288 + 5.289 +\begin{methoddesc}{debug}{msg\optional{, *args\optional{, **kwargs}}} 5.290 +Logs a message with level \constant{DEBUG} on this logger. 5.291 +The \var{msg} is the message format string, and the \var{args} are the 5.292 +arguments which are merged into \var{msg}. The only keyword argument in 5.293 +\var{kwargs} which is inspected is \var{exc_info} which, if it does not 5.294 +evaluate as false, causes exception information (via a call to 5.295 +\function{sys.exc_info()}) to be added to the logging message. 5.296 +\end{methoddesc} 5.297 + 5.298 +\begin{methoddesc}{info}{msg\optional{, *args\optional{, **kwargs}}} 5.299 +Logs a message with level \constant{INFO} on this logger. 5.300 +The arguments are interpreted as for \method{debug()}. 5.301 +\end{methoddesc} 5.302 + 5.303 +\begin{methoddesc}{warning}{msg\optional{, *args\optional{, **kwargs}}} 5.304 +Logs a message with level \constant{WARNING} on this logger. 5.305 +The arguments are interpreted as for \method{debug()}. 5.306 +\end{methoddesc} 5.307 + 5.308 +\begin{methoddesc}{error}{msg\optional{, *args\optional{, **kwargs}}} 5.309 +Logs a message with level \constant{ERROR} on this logger. 5.310 +The arguments are interpreted as for \method{debug()}. 5.311 +\end{methoddesc} 5.312 + 5.313 +\begin{methoddesc}{critical}{msg\optional{, *args\optional{, **kwargs}}} 5.314 +Logs a message with level \constant{CRITICAL} on this logger. 5.315 +The arguments are interpreted as for \method{debug()}. 5.316 +\end{methoddesc} 5.317 + 5.318 +\begin{methoddesc}{log}{lvl, msg\optional{, *args\optional{, **kwargs}}} 5.319 +Logs a message with level \var{lvl} on this logger. 5.320 +The other arguments are interpreted as for \method{debug()}. 5.321 +\end{methoddesc} 5.322 + 5.323 +\begin{methoddesc}{exception}{msg\optional{, *args}} 5.324 +Logs a message with level \constant{ERROR} on this logger. 5.325 +The arguments are interpreted as for \method{debug()}. Exception info 5.326 +is added to the logging message. This method should only be called 5.327 +from an exception handler. 5.328 +\end{methoddesc} 5.329 + 5.330 +\begin{methoddesc}{addFilter}{filt} 5.331 +Adds the specified filter \var{filt} to this logger. 5.332 +\end{methoddesc} 5.333 + 5.334 +\begin{methoddesc}{removeFilter}{filt} 5.335 +Removes the specified filter \var{filt} from this logger. 5.336 +\end{methoddesc} 5.337 + 5.338 +\begin{methoddesc}{filter}{record} 5.339 +Applies this logger's filters to the record and returns a true value if 5.340 +the record is to be processed. 5.341 +\end{methoddesc} 5.342 + 5.343 +\begin{methoddesc}{addHandler}{hdlr} 5.344 +Adds the specified handler \var{hdlr} to this logger. 5.345 +\end{methoddesc} 5.346 + 5.347 +\begin{methoddesc}{removeHandler}{hdlr} 5.348 +Removes the specified handler \var{hdlr} from this logger. 5.349 +\end{methoddesc} 5.350 + 5.351 +\begin{methoddesc}{findCaller}{} 5.352 +Finds the caller's source filename and line number. Returns the filename 5.353 +and line number as a 2-element tuple. 5.354 +\end{methoddesc} 5.355 + 5.356 +\begin{methoddesc}{handle}{record} 5.357 +Handles a record by passing it to all handlers associated with this logger 5.358 +and its ancestors (until a false value of \var{propagate} is found). 5.359 +This method is used for unpickled records received from a socket, as well 5.360 +as those created locally. Logger-level filtering is applied using 5.361 +\method{filter()}. 5.362 +\end{methoddesc} 5.363 + 5.364 +\begin{methoddesc}{makeRecord}{name, lvl, fn, lno, msg, args, exc_info} 5.365 +This is a factory method which can be overridden in subclasses to create 5.366 +specialized \class{LogRecord} instances. 5.367 +\end{methoddesc} 5.368 + 5.369 +\subsection{Handler Objects} 5.370 + 5.371 +Handlers have the following attributes and methods. Note that 5.372 +\class{Handler} is never instantiated directly; this class acts as a 5.373 +base for more useful subclasses. However, the \method{__init__()} 5.374 +method in subclasses needs to call \method{Handler.__init__()}. 5.375 + 5.376 +\begin{methoddesc}{__init__}{level=\constant{NOTSET}} 5.377 +Initializes the \class{Handler} instance by setting its level, setting 5.378 +the list of filters to the empty list and creating a lock (using 5.379 +\method{createLock()}) for serializing access to an I/O mechanism. 5.380 +\end{methoddesc} 5.381 + 5.382 +\begin{methoddesc}{createLock}{} 5.383 +Initializes a thread lock which can be used to serialize access to 5.384 +underlying I/O functionality which may not be threadsafe. 5.385 +\end{methoddesc} 5.386 + 5.387 +\begin{methoddesc}{acquire}{} 5.388 +Acquires the thread lock created with \method{createLock()}. 5.389 +\end{methoddesc} 5.390 + 5.391 +\begin{methoddesc}{release}{} 5.392 +Releases the thread lock acquired with \method{acquire()}. 5.393 +\end{methoddesc} 5.394 + 5.395 +\begin{methoddesc}{setLevel}{lvl} 5.396 +Sets the threshold for this handler to \var{lvl}. Logging messages which are 5.397 +less severe than \var{lvl} will be ignored. When a handler is created, the 5.398 +level is set to \constant{NOTSET} (which causes all messages to be processed). 5.399 +\end{methoddesc} 5.400 + 5.401 +\begin{methoddesc}{setFormatter}{form} 5.402 +Sets the \class{Formatter} for this handler to \var{form}. 5.403 +\end{methoddesc} 5.404 + 5.405 +\begin{methoddesc}{addFilter}{filt} 5.406 +Adds the specified filter \var{filt} to this handler. 5.407 +\end{methoddesc} 5.408 + 5.409 +\begin{methoddesc}{removeFilter}{filt} 5.410 +Removes the specified filter \var{filt} from this handler. 5.411 +\end{methoddesc} 5.412 + 5.413 +\begin{methoddesc}{filter}{record} 5.414 +Applies this handler's filters to the record and returns a true value if 5.415 +the record is to be processed. 5.416 +\end{methoddesc} 5.417 + 5.418 +\begin{methoddesc}{flush}{} 5.419 +Ensure all logging output has been flushed. This version does 5.420 +nothing and is intended to be implemented by subclasses. 5.421 +\end{methoddesc} 5.422 + 5.423 +\begin{methoddesc}{close}{} 5.424 +Tidy up any resources used by the handler. This version does 5.425 +nothing and is intended to be implemented by subclasses. 5.426 +\end{methoddesc} 5.427 + 5.428 +\begin{methoddesc}{handle}{record} 5.429 +Conditionally emits the specified logging record, depending on 5.430 +filters which may have been added to the handler. Wraps the actual 5.431 +emission of the record with acquisition/release of the I/O thread 5.432 +lock. 5.433 +\end{methoddesc} 5.434 + 5.435 +\begin{methoddesc}{handleError}{record} 5.436 +This method should be called from handlers when an exception is 5.437 +encountered during an \method{emit()} call. By default it does nothing, 5.438 +which means that exceptions get silently ignored. This is what is 5.439 +mostly wanted for a logging system - most users will not care 5.440 +about errors in the logging system, they are more interested in 5.441 +application errors. You could, however, replace this with a custom 5.442 +handler if you wish. The specified record is the one which was being 5.443 +processed when the exception occurred. 5.444 +\end{methoddesc} 5.445 + 5.446 +\begin{methoddesc}{format}{record} 5.447 +Do formatting for a record - if a formatter is set, use it. 5.448 +Otherwise, use the default formatter for the module. 5.449 +\end{methoddesc} 5.450 + 5.451 +\begin{methoddesc}{emit}{record} 5.452 +Do whatever it takes to actually log the specified logging record. 5.453 +This version is intended to be implemented by subclasses and so 5.454 +raises a \exception{NotImplementedError}. 5.455 +\end{methoddesc} 5.456 + 5.457 +\subsubsection{StreamHandler} 5.458 + 5.459 +The \class{StreamHandler} class sends logging output to streams such as 5.460 +\var{sys.stdout}, \var{sys.stderr} or any file-like object (or, more 5.461 +precisely, any object which supports \method{write()} and \method{flush()} 5.462 +methods). 5.463 + 5.464 +\begin{classdesc}{StreamHandler}{\optional{strm}} 5.465 +Returns a new instance of the \class{StreamHandler} class. If \var{strm} is 5.466 +specified, the instance will use it for logging output; otherwise, 5.467 +\var{sys.stderr} will be used. 5.468 +\end{classdesc} 5.469 + 5.470 +\begin{methoddesc}{emit}{record} 5.471 +If a formatter is specified, it is used to format the record. 5.472 +The record is then written to the stream with a trailing newline. 5.473 +If exception information is present, it is formatted using 5.474 +\function{traceback.print_exception()} and appended to the stream. 5.475 +\end{methoddesc} 5.476 + 5.477 +\begin{methoddesc}{flush}{} 5.478 +Flushes the stream by calling its \method{flush()} method. Note that 5.479 +the \method{close()} method is inherited from \class{Handler} and 5.480 +so does nothing, so an explicit \method{flush()} call may be needed 5.481 +at times. 5.482 +\end{methoddesc} 5.483 + 5.484 +\subsubsection{FileHandler} 5.485 + 5.486 +The \class{FileHandler} class sends logging output to a disk file. 5.487 +It inherits the output functionality from \class{StreamHandler}. 5.488 + 5.489 +\begin{classdesc}{FileHandler}{filename\optional{, mode}} 5.490 +Returns a new instance of the \class{FileHandler} class. The specified 5.491 +file is opened and used as the stream for logging. If \var{mode} is 5.492 +not specified, \constant{'a'} is used. By default, the file grows 5.493 +indefinitely. 5.494 +\end{classdesc} 5.495 + 5.496 +\begin{methoddesc}{close}{} 5.497 +Closes the file. 5.498 +\end{methoddesc} 5.499 + 5.500 +\begin{methoddesc}{emit}{record} 5.501 +Outputs the record to the file. 5.502 +\end{methoddesc} 5.503 + 5.504 +\subsubsection{RotatingFileHandler} 5.505 + 5.506 +The \class{RotatingFileHandler} class supports rotation of disk log files. 5.507 + 5.508 +\begin{classdesc}{RotatingFileHandler}{filename\optional{, mode\optional{, 5.509 + maxBytes\optional{, backupCount}}}} 5.510 +Returns a new instance of the \class{RotatingFileHandler} class. The 5.511 +specified file is opened and used as the stream for logging. If 5.512 +\var{mode} is not specified, \code{'a'} is used. By default, the 5.513 +file grows indefinitely. 5.514 + 5.515 +You can use the \var{maxBytes} and 5.516 +\var{backupCount} values to allow the file to \dfn{rollover} at a 5.517 +predetermined size. When the size is about to be exceeded, the file is 5.518 +closed and a new file is silently opened for output. Rollover occurs 5.519 +whenever the current log file is nearly \var{maxBytes} in length; if 5.520 +\var{maxBytes} is zero, rollover never occurs. If \var{backupCount} 5.521 +is non-zero, the system will save old log files by appending the 5.522 +extensions ".1", ".2" etc., to the filename. For example, with 5.523 +a \var{backupCount} of 5 and a base file name of 5.524 +\file{app.log}, you would get \file{app.log}, 5.525 +\file{app.log.1}, \file{app.log.2}, up to \file{app.log.5}. The file being 5.526 +written to is always \file{app.log}. When this file is filled, it is 5.527 +closed and renamed to \file{app.log.1}, and if files \file{app.log.1}, 5.528 +\file{app.log.2}, etc. exist, then they are renamed to \file{app.log.2}, 5.529 +\file{app.log.3} etc. respectively. 5.530 +\end{classdesc} 5.531 + 5.532 +\begin{methoddesc}{doRollover}{} 5.533 +Does a rollover, as described above. 5.534 +\end{methoddesc} 5.535 + 5.536 +\begin{methoddesc}{emit}{record} 5.537 +Outputs the record to the file, catering for rollover as described 5.538 +in \method{setRollover()}. 5.539 +\end{methoddesc} 5.540 + 5.541 +\subsubsection{SocketHandler} 5.542 + 5.543 +The \class{SocketHandler} class sends logging output to a network 5.544 +socket. The base class uses a TCP socket. 5.545 + 5.546 +\begin{classdesc}{SocketHandler}{host, port} 5.547 +Returns a new instance of the \class{SocketHandler} class intended to 5.548 +communicate with a remote machine whose address is given by \var{host} 5.549 +and \var{port}. 5.550 +\end{classdesc} 5.551 + 5.552 +\begin{methoddesc}{close}{} 5.553 +Closes the socket. 5.554 +\end{methoddesc} 5.555 + 5.556 +\begin{methoddesc}{handleError}{} 5.557 +\end{methoddesc} 5.558 + 5.559 +\begin{methoddesc}{emit}{} 5.560 +Pickles the record's attribute dictionary and writes it to the socket in 5.561 +binary format. If there is an error with the socket, silently drops the 5.562 +packet. If the connection was previously lost, re-establishes the connection. 5.563 +To unpickle the record at the receiving end into a LogRecord, use the 5.564 +\function{makeLogRecord} function. 5.565 +\end{methoddesc} 5.566 + 5.567 +\begin{methoddesc}{handleError}{} 5.568 +Handles an error which has occurred during \method{emit()}. The 5.569 +most likely cause is a lost connection. Closes the socket so that 5.570 +we can retry on the next event. 5.571 +\end{methoddesc} 5.572 + 5.573 +\begin{methoddesc}{makeSocket}{} 5.574 +This is a factory method which allows subclasses to define the precise 5.575 +type of socket they want. The default implementation creates a TCP 5.576 +socket (\constant{socket.SOCK_STREAM}). 5.577 +\end{methoddesc} 5.578 + 5.579 +\begin{methoddesc}{makePickle}{record} 5.580 +Pickles the record's attribute dictionary in binary format with a length 5.581 +prefix, and returns it ready for transmission across the socket. 5.582 +\end{methoddesc} 5.583 + 5.584 +\begin{methoddesc}{send}{packet} 5.585 +Send a pickled string \var{packet} to the socket. This function allows 5.586 +for partial sends which can happen when the network is busy. 5.587 +\end{methoddesc} 5.588 + 5.589 +\subsubsection{DatagramHandler} 5.590 + 5.591 +The \class{DatagramHandler} class inherits from \class{SocketHandler} 5.592 +to support sending logging messages over UDP sockets. 5.593 + 5.594 +\begin{classdesc}{DatagramHandler}{host, port} 5.595 +Returns a new instance of the \class{DatagramHandler} class intended to 5.596 +communicate with a remote machine whose address is given by \var{host} 5.597 +and \var{port}. 5.598 +\end{classdesc} 5.599 + 5.600 +\begin{methoddesc}{emit}{} 5.601 +Pickles the record's attribute dictionary and writes it to the socket in 5.602 +binary format. If there is an error with the socket, silently drops the 5.603 +packet. 5.604 +To unpickle the record at the receiving end into a LogRecord, use the 5.605 +\function{makeLogRecord} function. 5.606 +\end{methoddesc} 5.607 + 5.608 +\begin{methoddesc}{makeSocket}{} 5.609 +The factory method of \class{SocketHandler} is here overridden to create 5.610 +a UDP socket (\constant{socket.SOCK_DGRAM}). 5.611 +\end{methoddesc} 5.612 + 5.613 +\begin{methoddesc}{send}{s} 5.614 +Send a pickled string to a socket. 5.615 +\end{methoddesc} 5.616 + 5.617 +\subsubsection{SysLogHandler} 5.618 + 5.619 +The \class{SysLogHandler} class supports sending logging messages to a 5.620 +remote or local \UNIX{} syslog. 5.621 + 5.622 +\begin{classdesc}{SysLogHandler}{\optional{address\optional{, facility}}} 5.623 +Returns a new instance of the \class{SysLogHandler} class intended to 5.624 +communicate with a remote \UNIX{} machine whose address is given by 5.625 +\var{address} in the form of a \code{(\var{host}, \var{port})} 5.626 +tuple. If \var{address} is not specified, \code{('localhost', 514)} is 5.627 +used. The address is used to open a UDP socket. If \var{facility} is 5.628 +not specified, \constant{LOG_USER} is used. 5.629 +\end{classdesc} 5.630 + 5.631 +\begin{methoddesc}{close}{} 5.632 +Closes the socket to the remote host. 5.633 +\end{methoddesc} 5.634 + 5.635 +\begin{methoddesc}{emit}{record} 5.636 +The record is formatted, and then sent to the syslog server. If 5.637 +exception information is present, it is \emph{not} sent to the server. 5.638 +\end{methoddesc} 5.639 + 5.640 +\begin{methoddesc}{encodePriority}{facility, priority} 5.641 +Encodes the facility and priority into an integer. You can pass in strings 5.642 +or integers - if strings are passed, internal mapping dictionaries are used 5.643 +to convert them to integers. 5.644 +\end{methoddesc} 5.645 + 5.646 +\subsubsection{NTEventLogHandler} 5.647 + 5.648 +The \class{NTEventLogHandler} class supports sending logging messages 5.649 +to a local Windows NT, Windows 2000 or Windows XP event log. Before 5.650 +you can use it, you need Mark Hammond's Win32 extensions for Python 5.651 +installed. 5.652 + 5.653 +\begin{classdesc}{NTEventLogHandler}{appname\optional{, 5.654 + dllname\optional{, logtype}}} 5.655 +Returns a new instance of the \class{NTEventLogHandler} class. The 5.656 +\var{appname} is used to define the application name as it appears in the 5.657 +event log. An appropriate registry entry is created using this name. 5.658 +The \var{dllname} should give the fully qualified pathname of a .dll or .exe 5.659 +which contains message definitions to hold in the log (if not specified, 5.660 +\code{'win32service.pyd'} is used - this is installed with the Win32 5.661 +extensions and contains some basic placeholder message definitions. 5.662 +Note that use of these placeholders will make your event logs big, as the 5.663 +entire message source is held in the log. If you want slimmer logs, you have 5.664 +to pass in the name of your own .dll or .exe which contains the message 5.665 +definitions you want to use in the event log). The \var{logtype} is one of 5.666 +\code{'Application'}, \code{'System'} or \code{'Security'}, and 5.667 +defaults to \code{'Application'}. 5.668 +\end{classdesc} 5.669 + 5.670 +\begin{methoddesc}{close}{} 5.671 +At this point, you can remove the application name from the registry as a 5.672 +source of event log entries. However, if you do this, you will not be able 5.673 +to see the events as you intended in the Event Log Viewer - it needs to be 5.674 +able to access the registry to get the .dll name. The current version does 5.675 +not do this (in fact it doesn't do anything). 5.676 +\end{methoddesc} 5.677 + 5.678 +\begin{methoddesc}{emit}{record} 5.679 +Determines the message ID, event category and event type, and then logs the 5.680 +message in the NT event log. 5.681 +\end{methoddesc} 5.682 + 5.683 +\begin{methoddesc}{getEventCategory}{record} 5.684 +Returns the event category for the record. Override this if you 5.685 +want to specify your own categories. This version returns 0. 5.686 +\end{methoddesc} 5.687 + 5.688 +\begin{methoddesc}{getEventType}{record} 5.689 +Returns the event type for the record. Override this if you want 5.690 +to specify your own types. This version does a mapping using the 5.691 +handler's typemap attribute, which is set up in \method{__init__()} 5.692 +to a dictionary which contains mappings for \constant{DEBUG}, 5.693 +\constant{INFO}, \constant{WARNING}, \constant{ERROR} and 5.694 +\constant{CRITICAL}. If you are using your own levels, you will either need 5.695 +to override this method or place a suitable dictionary in the 5.696 +handler's \var{typemap} attribute. 5.697 +\end{methoddesc} 5.698 + 5.699 +\begin{methoddesc}{getMessageID}{record} 5.700 +Returns the message ID for the record. If you are using your 5.701 +own messages, you could do this by having the \var{msg} passed to the 5.702 +logger being an ID rather than a format string. Then, in here, 5.703 +you could use a dictionary lookup to get the message ID. This 5.704 +version returns 1, which is the base message ID in 5.705 +\file{win32service.pyd}. 5.706 +\end{methoddesc} 5.707 + 5.708 +\subsubsection{SMTPHandler} 5.709 + 5.710 +The \class{SMTPHandler} class supports sending logging messages to an email 5.711 +address via SMTP. 5.712 + 5.713 +\begin{classdesc}{SMTPHandler}{mailhost, fromaddr, toaddrs, subject} 5.714 +Returns a new instance of the \class{SMTPHandler} class. The 5.715 +instance is initialized with the from and to addresses and subject 5.716 +line of the email. The \var{toaddrs} should be a list of strings without 5.717 +domain names (That's what the \var{mailhost} is for). To specify a 5.718 +non-standard SMTP port, use the (host, port) tuple format for the 5.719 +\var{mailhost} argument. If you use a string, the standard SMTP port 5.720 +is used. 5.721 +\end{classdesc} 5.722 + 5.723 +\begin{methoddesc}{emit}{record} 5.724 +Formats the record and sends it to the specified addressees. 5.725 +\end{methoddesc} 5.726 + 5.727 +\begin{methoddesc}{getSubject}{record} 5.728 +If you want to specify a subject line which is record-dependent, 5.729 +override this method. 5.730 +\end{methoddesc} 5.731 + 5.732 +\subsubsection{MemoryHandler} 5.733 + 5.734 +The \class{MemoryHandler} supports buffering of logging records in memory, 5.735 +periodically flushing them to a \dfn{target} handler. Flushing occurs 5.736 +whenever the buffer is full, or when an event of a certain severity or 5.737 +greater is seen. 5.738 + 5.739 +\class{MemoryHandler} is a subclass of the more general 5.740 +\class{BufferingHandler}, which is an abstract class. This buffers logging 5.741 +records in memory. Whenever each record is added to the buffer, a 5.742 +check is made by calling \method{shouldFlush()} to see if the buffer 5.743 +should be flushed. If it should, then \method{flush()} is expected to 5.744 +do the needful. 5.745 + 5.746 +\begin{classdesc}{BufferingHandler}{capacity} 5.747 +Initializes the handler with a buffer of the specified capacity. 5.748 +\end{classdesc} 5.749 + 5.750 +\begin{methoddesc}{emit}{record} 5.751 +Appends the record to the buffer. If \method{shouldFlush()} returns true, 5.752 +calls \method{flush()} to process the buffer. 5.753 +\end{methoddesc} 5.754 + 5.755 +\begin{methoddesc}{flush}{} 5.756 +You can override this to implement custom flushing behavior. This version 5.757 +just zaps the buffer to empty. 5.758 +\end{methoddesc} 5.759 + 5.760 +\begin{methoddesc}{shouldFlush}{record} 5.761 +Returns true if the buffer is up to capacity. This method can be 5.762 +overridden to implement custom flushing strategies. 5.763 +\end{methoddesc} 5.764 + 5.765 +\begin{classdesc}{MemoryHandler}{capacity\optional{, flushLevel 5.766 +\optional{, target}}} 5.767 +Returns a new instance of the \class{MemoryHandler} class. The 5.768 +instance is initialized with a buffer size of \var{capacity}. If 5.769 +\var{flushLevel} is not specified, \constant{ERROR} is used. If no 5.770 +\var{target} is specified, the target will need to be set using 5.771 +\method{setTarget()} before this handler does anything useful. 5.772 +\end{classdesc} 5.773 + 5.774 +\begin{methoddesc}{close}{} 5.775 +Calls \method{flush()}, sets the target to \constant{None} and 5.776 +clears the buffer. 5.777 +\end{methoddesc} 5.778 + 5.779 +\begin{methoddesc}{flush}{} 5.780 +For a \class{MemoryHandler}, flushing means just sending the buffered 5.781 +records to the target, if there is one. Override if you want 5.782 +different behavior. 5.783 +\end{methoddesc} 5.784 + 5.785 +\begin{methoddesc}{setTarget}{target} 5.786 +Sets the target handler for this handler. 5.787 +\end{methoddesc} 5.788 + 5.789 +\begin{methoddesc}{shouldFlush}{record} 5.790 +Checks for buffer full or a record at the \var{flushLevel} or higher. 5.791 +\end{methoddesc} 5.792 + 5.793 +\subsubsection{HTTPHandler} 5.794 + 5.795 +The \class{HTTPHandler} class supports sending logging messages to a 5.796 +Web server, using either \samp{GET} or \samp{POST} semantics. 5.797 + 5.798 +\begin{classdesc}{HTTPHandler}{host, url\optional{, method}} 5.799 +Returns a new instance of the \class{HTTPHandler} class. The 5.800 +instance is initialized with a host address, url and HTTP method. 5.801 +If no \var{method} is specified, \samp{GET} is used. 5.802 +\end{classdesc} 5.803 + 5.804 +\begin{methoddesc}{emit}{record} 5.805 +Sends the record to the Web server as an URL-encoded dictionary. 5.806 +\end{methoddesc} 5.807 + 5.808 +\subsection{Formatter Objects} 5.809 + 5.810 +\class{Formatter}s have the following attributes and methods. They are 5.811 +responsible for converting a \class{LogRecord} to (usually) a string 5.812 +which can be interpreted by either a human or an external system. The 5.813 +base 5.814 +\class{Formatter} allows a formatting string to be specified. If none is 5.815 +supplied, the default value of \code{'\%(message)s\e'} is used. 5.816 + 5.817 +A Formatter can be initialized with a format string which makes use of 5.818 +knowledge of the \class{LogRecord} attributes - such as the default value 5.819 +mentioned above making use of the fact that the user's message and 5.820 +arguments are pre-formatted into a LogRecord's \var{message} 5.821 +attribute. This format string contains standard python \%-style 5.822 +mapping keys. See section \ref{typesseq-strings}, ``String Formatting 5.823 +Operations,'' for more information on string formatting. 5.824 + 5.825 +Currently, the useful mapping keys in a LogRecord are: 5.826 + 5.827 +\begin{tableii}{l|l}{code}{Format}{Description} 5.828 +\lineii{\%(name)s} {Name of the logger (logging channel).} 5.829 +\lineii{\%(levelno)s} {Numeric logging level for the message 5.830 + (\constant{DEBUG}, \constant{INFO}, 5.831 + \constant{WARNING}, \constant{ERROR}, 5.832 + \constant{CRITICAL}).} 5.833 +\lineii{\%(levelname)s}{Text logging level for the message 5.834 + (\code{'DEBUG'}, \code{'INFO'}, 5.835 + \code{'WARNING'}, \code{'ERROR'}, 5.836 + \code{'CRITICAL'}).} 5.837 +\lineii{\%(pathname)s} {Full pathname of the source file where the logging 5.838 + call was issued (if available).} 5.839 +\lineii{\%(filename)s} {Filename portion of pathname.} 5.840 +\lineii{\%(module)s} {Module (name portion of filename).} 5.841 +\lineii{\%(lineno)d} {Source line number where the logging call was issued 5.842 + (if available).} 5.843 +\lineii{\%(created)f} {Time when the LogRecord was created (as 5.844 + returned by \function{time.time()}).} 5.845 +\lineii{\%(asctime)s} {Human-readable time when the LogRecord was created. 5.846 + By default this is of the form 5.847 + ``2003-07-08 16:49:45,896'' (the numbers after the 5.848 + comma are millisecond portion of the time).} 5.849 +\lineii{\%(msecs)d} {Millisecond portion of the time when the 5.850 + \class{LogRecord} was created.} 5.851 +\lineii{\%(thread)d} {Thread ID (if available).} 5.852 +\lineii{\%(process)d} {Process ID (if available).} 5.853 +\lineii{\%(message)s} {The logged message, computed as \code{msg \% args}.} 5.854 +\end{tableii} 5.855 + 5.856 +\begin{classdesc}{Formatter}{\optional{fmt\optional{, datefmt}}} 5.857 +Returns a new instance of the \class{Formatter} class. The 5.858 +instance is initialized with a format string for the message as a whole, 5.859 +as well as a format string for the date/time portion of a message. If 5.860 +no \var{fmt} is specified, \code{'\%(message)s'} is used. If no \var{datefmt} 5.861 +is specified, the ISO8601 date format is used. 5.862 +\end{classdesc} 5.863 + 5.864 +\begin{methoddesc}{format}{record} 5.865 +The record's attribute dictionary is used as the operand to a 5.866 +string formatting operation. Returns the resulting string. 5.867 +Before formatting the dictionary, a couple of preparatory steps 5.868 +are carried out. The \var{message} attribute of the record is computed 5.869 +using \var{msg} \% \var{args}. If the formatting string contains 5.870 +\code{'(asctime)'}, \method{formatTime()} is called to format the 5.871 +event time. If there is exception information, it is formatted using 5.872 +\method{formatException()} and appended to the message. 5.873 +\end{methoddesc} 5.874 + 5.875 +\begin{methoddesc}{formatTime}{record\optional{, datefmt}} 5.876 +This method should be called from \method{format()} by a formatter which 5.877 +wants to make use of a formatted time. This method can be overridden 5.878 +in formatters to provide for any specific requirement, but the 5.879 +basic behavior is as follows: if \var{datefmt} (a string) is specified, 5.880 +it is used with \function{time.strftime()} to format the creation time of the 5.881 +record. Otherwise, the ISO8601 format is used. The resulting 5.882 +string is returned. 5.883 +\end{methoddesc} 5.884 + 5.885 +\begin{methoddesc}{formatException}{exc_info} 5.886 +Formats the specified exception information (a standard exception tuple 5.887 +as returned by \function{sys.exc_info()}) as a string. This default 5.888 +implementation just uses \function{traceback.print_exception()}. 5.889 +The resulting string is returned. 5.890 +\end{methoddesc} 5.891 + 5.892 +\subsection{Filter Objects} 5.893 + 5.894 +\class{Filter}s can be used by \class{Handler}s and \class{Logger}s for 5.895 +more sophisticated filtering than is provided by levels. The base filter 5.896 +class only allows events which are below a certain point in the logger 5.897 +hierarchy. For example, a filter initialized with "A.B" will allow events 5.898 +logged by loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", 5.899 +"B.A.B" etc. If initialized with the empty string, all events are passed. 5.900 + 5.901 +\begin{classdesc}{Filter}{\optional{name}} 5.902 +Returns an instance of the \class{Filter} class. If \var{name} is specified, 5.903 +it names a logger which, together with its children, will have its events 5.904 +allowed through the filter. If no name is specified, allows every event. 5.905 +\end{classdesc} 5.906 + 5.907 +\begin{methoddesc}{filter}{record} 5.908 +Is the specified record to be logged? Returns zero for no, nonzero for 5.909 +yes. If deemed appropriate, the record may be modified in-place by this 5.910 +method. 5.911 +\end{methoddesc} 5.912 + 5.913 +\subsection{LogRecord Objects} 5.914 + 5.915 +LogRecord instances are created every time something is logged. They 5.916 +contain all the information pertinent to the event being logged. The 5.917 +main information passed in is in msg and args, which are combined 5.918 +using msg \% args to create the message field of the record. The record 5.919 +also includes information such as when the record was created, the 5.920 +source line where the logging call was made, and any exception 5.921 +information to be logged. 5.922 + 5.923 +LogRecord has no methods; it's just a repository for information about the 5.924 +logging event. The only reason it's a class rather than a dictionary is to 5.925 +facilitate extension. 5.926 + 5.927 +\begin{classdesc}{LogRecord}{name, lvl, pathname, lineno, msg, args, 5.928 + exc_info} 5.929 +Returns an instance of \class{LogRecord} initialized with interesting 5.930 +information. The \var{name} is the logger name; \var{lvl} is the 5.931 +numeric level; \var{pathname} is the absolute pathname of the source 5.932 +file in which the logging call was made; \var{lineno} is the line 5.933 +number in that file where the logging call is found; \var{msg} is the 5.934 +user-supplied message (a format string); \var{args} is the tuple 5.935 +which, together with \var{msg}, makes up the user message; and 5.936 +\var{exc_info} is the exception tuple obtained by calling 5.937 +\function{sys.exc_info() }(or \constant{None}, if no exception information 5.938 +is available). 5.939 +\end{classdesc} 5.940 + 5.941 +\subsection{Thread Safety} 5.942 + 5.943 +The logging module is intended to be thread-safe without any special work 5.944 +needing to be done by its clients. It achieves this though using threading 5.945 +locks; there is one lock to serialize access to the module's shared data, 5.946 +and each handler also creates a lock to serialize access to its underlying 5.947 +I/O. 5.948 + 5.949 +\subsection{Configuration} 5.950 + 5.951 + 5.952 +\subsubsection{Configuration functions} 5.953 + 5.954 +The following functions allow the logging module to be 5.955 +configured. Before they can be used, you must import 5.956 +\module{logging.config}. Their use is optional --- you can configure 5.957 +the logging module entirely by making calls to the main API (defined 5.958 +in \module{logging} itself) and defining handlers which are declared 5.959 +either in \module{logging} or \module{logging.handlers}. 5.960 + 5.961 +\begin{funcdesc}{fileConfig}{fname\optional{, defaults}} 5.962 +Reads the logging configuration from a ConfigParser-format file named 5.963 +\var{fname}. This function can be called several times from an application, 5.964 +allowing an end user the ability to select from various pre-canned 5.965 +configurations (if the developer provides a mechanism to present the 5.966 +choices and load the chosen configuration). Defaults to be passed to 5.967 +ConfigParser can be specified in the \var{defaults} argument. 5.968 +\end{funcdesc} 5.969 + 5.970 +\begin{funcdesc}{listen}{\optional{port}} 5.971 +Starts up a socket server on the specified port, and listens for new 5.972 +configurations. If no port is specified, the module's default 5.973 +\constant{DEFAULT_LOGGING_CONFIG_PORT} is used. Logging configurations 5.974 +will be sent as a file suitable for processing by \function{fileConfig()}. 5.975 +Returns a \class{Thread} instance on which you can call \method{start()} 5.976 +to start the server, and which you can \method{join()} when appropriate. 5.977 +To stop the server, call \function{stopListening()}. 5.978 +\end{funcdesc} 5.979 + 5.980 +\begin{funcdesc}{stopListening}{} 5.981 +Stops the listening server which was created with a call to 5.982 +\function{listen()}. This is typically called before calling \method{join()} 5.983 +on the return value from \function{listen()}. 5.984 +\end{funcdesc} 5.985 + 5.986 +\subsubsection{Configuration file format} 5.987 + 5.988 +The configuration file format understood by \function{fileConfig} is 5.989 +based on ConfigParser functionality. The file must contain sections 5.990 +called \code{[loggers]}, \code{[handlers]} and \code{[formatters]} 5.991 +which identify by name the entities of each type which are defined in 5.992 +the file. For each such entity, there is a separate section which 5.993 +identified how that entity is configured. Thus, for a logger named 5.994 +\code{log01} in the \code{[loggers]} section, the relevant 5.995 +configuration details are held in a section 5.996 +\code{[logger_log01]}. Similarly, a handler called \code{hand01} in 5.997 +the \code{[handlers]} section will have its configuration held in a 5.998 +section called \code{[handler_hand01]}, while a formatter called 5.999 +\code{form01} in the \code{[formatters]} section will have its 5.1000 +configuration specified in a section called 5.1001 +\code{[formatter_form01]}. The root logger configuration must be 5.1002 +specified in a section called \code{[logger_root]}. 5.1003 + 5.1004 +Examples of these sections in the file are given below. 5.1005 + 5.1006 +\begin{verbatim} 5.1007 +[loggers] 5.1008 +keys=root,log02,log03,log04,log05,log06,log07 5.1009 + 5.1010 +[handlers] 5.1011 +keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09 5.1012 + 5.1013 +[formatters] 5.1014 +keys=form01,form02,form03,form04,form05,form06,form07,form08,form09 5.1015 +\end{verbatim} 5.1016 + 5.1017 +The root logger must specify a level and a list of handlers. An 5.1018 +example of a root logger section is given below. 5.1019 + 5.1020 +\begin{verbatim} 5.1021 +[logger_root] 5.1022 +level=NOTSET 5.1023 +handlers=hand01 5.1024 +\end{verbatim} 5.1025 + 5.1026 +The \code{level} entry can be one of \code{DEBUG, INFO, WARNING, 5.1027 +ERROR, CRITICAL} or \code{NOTSET}. For the root logger only, 5.1028 +\code{NOTSET} means that all messages will be logged. Level values are 5.1029 +\function{eval()}uated in the context of the \code{logging} package's 5.1030 +namespace. 5.1031 + 5.1032 +The \code{handlers} entry is a comma-separated list of handler names, 5.1033 +which must appear in the \code{[handlers]} section. These names must 5.1034 +appear in the \code{[handlers]} section and have corresponding 5.1035 +sections in the configuration file. 5.1036 + 5.1037 +For loggers other than the root logger, some additional information is 5.1038 +required. This is illustrated by the following example. 5.1039 + 5.1040 +\begin{verbatim} 5.1041 +[logger_parser] 5.1042 +level=DEBUG 5.1043 +handlers=hand01 5.1044 +propagate=1 5.1045 +qualname=compiler.parser 5.1046 +\end{verbatim} 5.1047 + 5.1048 +The \code{level} and \code{handlers} entries are interpreted as for 5.1049 +the root logger, except that if a non-root logger's level is specified 5.1050 +as \code{NOTSET}, the system consults loggers higher up the hierarchy 5.1051 +to determine the effective level of the logger. The \code{propagate} 5.1052 +entry is set to 1 to indicate that messages must propagate to handlers 5.1053 +higher up the logger hierarchy from this logger, or 0 to indicate that 5.1054 +messages are \strong{not} propagated to handlers up the hierarchy. The 5.1055 +\code{qualname} entry is the hierarchical channel name of the logger, 5.1056 +that is to say the name used by the application to get the logger. 5.1057 + 5.1058 +Sections which specify handler configuration are exemplified by the 5.1059 +following. 5.1060 + 5.1061 +\begin{verbatim} 5.1062 +[handler_hand01] 5.1063 +class=StreamHandler 5.1064 +level=NOTSET 5.1065 +formatter=form01 5.1066 +args=(sys.stdout,) 5.1067 +\end{verbatim} 5.1068 + 5.1069 +The \code{class} entry indicates the handler's class (as determined by 5.1070 +\function{eval()} in the \code{logging} package's namespace). The 5.1071 +\code{level} is interpreted as for loggers, and \code{NOTSET} is taken 5.1072 +to mean "log everything". 5.1073 + 5.1074 +The \code{formatter} entry indicates the key name of the formatter for 5.1075 +this handler. If blank, a default formatter 5.1076 +(\code{logging._defaultFormatter}) is used. If a name is specified, it 5.1077 +must appear in the \code{[formatters]} section and have a 5.1078 +corresponding section in the configuration file. 5.1079 + 5.1080 +The \code{args} entry, when \function{eval()}uated in the context of 5.1081 +the \code{logging} package's namespace, is the list of arguments to 5.1082 +the constructor for the handler class. Refer to the constructors for 5.1083 +the relevant handlers, or to the examples below, to see how typical 5.1084 +entries are constructed. 5.1085 + 5.1086 +\begin{verbatim} 5.1087 +[handler_hand02] 5.1088 +class=FileHandler 5.1089 +level=DEBUG 5.1090 +formatter=form02 5.1091 +args=('python.log', 'w') 5.1092 + 5.1093 +[handler_hand03] 5.1094 +class=handlers.SocketHandler 5.1095 +level=INFO 5.1096 +formatter=form03 5.1097 +args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT) 5.1098 + 5.1099 +[handler_hand04] 5.1100 +class=handlers.DatagramHandler 5.1101 +level=WARN 5.1102 +formatter=form04 5.1103 +args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT) 5.1104 + 5.1105 +[handler_hand05] 5.1106 +class=handlers.SysLogHandler 5.1107 +level=ERROR 5.1108 +formatter=form05 5.1109 +args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) 5.1110 + 5.1111 +[handler_hand06] 5.1112 +class=NTEventLogHandler 5.1113 +level=CRITICAL 5.1114 +formatter=form06 5.1115 +args=('Python Application', '', 'Application') 5.1116 + 5.1117 +[handler_hand07] 5.1118 +class=SMTPHandler 5.1119 +level=WARN 5.1120 +formatter=form07 5.1121 +args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject') 5.1122 + 5.1123 +[handler_hand08] 5.1124 +class=MemoryHandler 5.1125 +level=NOTSET 5.1126 +formatter=form08 5.1127 +target= 5.1128 +args=(10, ERROR) 5.1129 + 5.1130 +[handler_hand09] 5.1131 +class=HTTPHandler 5.1132 +level=NOTSET 5.1133 +formatter=form09 5.1134 +args=('localhost:9022', '/log', 'GET') 5.1135 +\end{verbatim} 5.1136 + 5.1137 +Sections which specify formatter configuration are typified by the following. 5.1138 + 5.1139 +\begin{verbatim} 5.1140 +[formatter_form01] 5.1141 +format=F1 %(asctime)s %(levelname)s %(message)s 5.1142 +datefmt= 5.1143 +\end{verbatim} 5.1144 + 5.1145 +The \code{format} entry is the overall format string, and the 5.1146 +\code{datefmt} entry is the \function{strftime()}-compatible date/time format 5.1147 +string. If empty, the package substitutes ISO8601 format date/times, which 5.1148 +is almost equivalent to specifying the date format string "%Y-%m-%d %H:%M:%S". 5.1149 +The ISO8601 format also specifies milliseconds, which are appended to the 5.1150 +result of using the above format string, with a comma separator. An example 5.1151 +time in ISO8601 format is \code{2003-01-23 00:29:50,411}. 5.1152 + 5.1153 +\subsection{Using the logging package} 5.1154 + 5.1155 +\subsubsection{Simplest usage} 5.1156 + 5.1157 +Here's a simple example which shows the most casual usage of the logging 5.1158 +package. 5.1159 + 5.1160 +\begin{verbatim} 5.1161 +import logging 5.1162 +logging.debug("Houston, we have a %s", "thorny problem") 5.1163 +logging.info("Houston, we have a %s", "interesting problem") 5.1164 +logging.warning("Houston, we have a %s", "bit of a problem") 5.1165 +logging.error("Houston, we have a %s", "major problem") 5.1166 +logging.critical("Houston, we have a %s", "major disaster") 5.1167 +try: 5.1168 + infinity = 1 / 0 5.1169 +except: 5.1170 + logging.exception("Houston, we have an %s", "unexpected problem") 5.1171 +\end{verbatim} 5.1172 + 5.1173 +If you run the above example, this will produce: 5.1174 + 5.1175 +\begin{verbatim} 5.1176 +WARNING:root:Houston, we have a bit of a problem 5.1177 +ERROR:root:Houston, we have a major problem 5.1178 +CRITICAL:root:Houston, we have a major disaster 5.1179 +ERROR:root:Houston, we have an unexpected problem 5.1180 +Traceback (most recent call last): 5.1181 + File "C:\Projects\RDC\Python\packages\logging\test\tmp.py", line 8, in ? 5.1182 + infinity = 1 / 0 5.1183 +ZeroDivisionError: integer division or modulo by zero 5.1184 +\end{verbatim} 5.1185 + 5.1186 +The reason you get this output is that the default format is 5.1187 + 5.1188 +\begin{verbatim} 5.1189 +"%(levelname)s:%(name)s:%(message)s". 5.1190 +\end{verbatim} 5.1191 + 5.1192 +When you invoke functions \function{info()}, \function{warning()} etc. in the 5.1193 +logging package itself, these calls are delegated to the correspondingly 5.1194 +named methods in the root logger. This is why the logger name shown in the above 5.1195 +logging output is "root". If the root logger has no handlers configured, the 5.1196 +logging package creates a console handler and adds it to the root logger 5.1197 +automatically. (It does this by calling the \function{basicConfig()}, which you 5.1198 +can also call directly from your own code.) 5.1199 + 5.1200 +By default, events with a severity below WARNING are suppressed. Notice 5.1201 +that the \function{exception()} function acts like \function{error()}, except 5.1202 +that a traceback is appended to the log entry. 5.1203 + 5.1204 +\subsubsection{Logging to the console} 5.1205 + 5.1206 +Here's a simple example which logs all messages to the console. We use a named 5.1207 +logger: 5.1208 + 5.1209 +\begin{verbatim} 5.1210 +import logging 5.1211 +logging.basicConfig() 5.1212 +logger = logging.getLogger('myapp') 5.1213 +logger.setLevel(logging.DEBUG) 5.1214 +logger.debug("Houston, we have a %s", "thorny problem") 5.1215 +logger.info("Houston, we have a %s", "interesting problem") 5.1216 +logger.warning("Houston, we have a %s", "bit of a problem") 5.1217 +logger.error("Houston, we have a %s", "major problem") 5.1218 +logger.critical("Houston, we have a %s", "major disaster") 5.1219 +try: 5.1220 + infinity = 1 / 0 5.1221 +except: 5.1222 + logger.exception("Houston, we have an %s", "unexpected problem") 5.1223 +\end{verbatim} 5.1224 + 5.1225 +Here's the corresponding output: 5.1226 + 5.1227 +\begin{verbatim} 5.1228 +DEBUG:myapp:Houston, we have a thorny problem 5.1229 +INFO:myapp:Houston, we have a interesting problem 5.1230 +WARNING:myapp:Houston, we have a bit of a problem 5.1231 +ERROR:myapp:Houston, we have a major problem 5.1232 +CRITICAL:myapp:Houston, we have a major disaster 5.1233 +ERROR:myapp:Houston, we have an unexpected problem 5.1234 +Traceback (most recent call last): 5.1235 + File "C:\Projects\RDC\Python\packages\logging\test\tmp.py", line 11, in ? 5.1236 + infinity = 1 / 0 5.1237 +ZeroDivisionError: integer division or modulo by zero 5.1238 +\end{verbatim} 5.1239 + 5.1240 +As you can see, the specified logger name now appears in the output, and 5.1241 +DEBUG and INFO messages are included in the output because we explicitly 5.1242 +asked for them via the call to \method{setLevel()}. 5.1243 + 5.1244 +\subsubsection{Logging to a file} 5.1245 + 5.1246 +Here's a simple logging example that just logs to a file. In order, 5.1247 +it creates a \class{Logger} instance, then a \class{FileHandler} 5.1248 +and a \class{Formatter}. It attaches the \class{Formatter} to the 5.1249 +\class{FileHandler}, then the \class{FileHandler} to the \class{Logger}. 5.1250 +Finally, it sets a debug level for the logger. 5.1251 + 5.1252 +\begin{verbatim} 5.1253 +import logging 5.1254 +logger = logging.getLogger('myapp') 5.1255 +hdlr = logging.FileHandler('/var/tmp/myapp.log') 5.1256 +formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 5.1257 +hdlr.setFormatter(formatter) 5.1258 +logger.addHandler(hdlr) 5.1259 +logger.setLevel(logging.WARNING) 5.1260 +\end{verbatim} 5.1261 + 5.1262 +We can use this logger object now to write entries to the log file: 5.1263 + 5.1264 +\begin{verbatim} 5.1265 +logger.error('We have a problem') 5.1266 +logger.info('While this is just chatty') 5.1267 +\end{verbatim} 5.1268 + 5.1269 +If we look in the file that was created, we'll see something like this: 5.1270 +\begin{verbatim} 5.1271 +2003-07-08 16:49:45,896 ERROR We have a problem 5.1272 +\end{verbatim} 5.1273 + 5.1274 +The info message was not written to the file - we called the \method{setLevel} 5.1275 +method to say we only wanted \code{WARNING} or worse, so the info message is 5.1276 +discarded. 5.1277 + 5.1278 +The timestamp is of the form 5.1279 +``year-month-day hour:minutes:seconds,milliseconds.'' 5.1280 +Note that despite the three digits of precision in the milliseconds field, 5.1281 +not all systems provide time with this much precision. 5.1282 + 5.1283 +\subsubsection{Logging to a rotating set of files} 5.1284 +
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/tools/python/logging/logging-0.4.9.2/logging/__init__.py Thu Jul 22 13:42:39 2004 +0000 6.3 @@ -0,0 +1,1225 @@ 6.4 +# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. 6.5 +# 6.6 +# Permission to use, copy, modify, and distribute this software and its 6.7 +# documentation for any purpose and without fee is hereby granted, 6.8 +# provided that the above copyright notice appear in all copies and that 6.9 +# both that copyright notice and this permission notice appear in 6.10 +# supporting documentation, and that the name of Vinay Sajip 6.11 +# not be used in advertising or publicity pertaining to distribution 6.12 +# of the software without specific, written prior permission. 6.13 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 6.14 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 6.15 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 6.16 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 6.17 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 6.18 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6.19 + 6.20 +""" 6.21 +Logging package for Python. Based on PEP 282 and comments thereto in 6.22 +comp.lang.python, and influenced by Apache's log4j system. 6.23 + 6.24 +Should work under Python versions >= 1.5.2, except that source line 6.25 +information is not available unless 'sys._getframe()' is. 6.26 + 6.27 +Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. 6.28 + 6.29 +To use, simply 'import logging' and log away! 6.30 +""" 6.31 + 6.32 +import sys, os, types, time, string, cStringIO 6.33 + 6.34 +try: 6.35 + import thread 6.36 + import threading 6.37 +except ImportError: 6.38 + thread = None 6.39 + 6.40 +__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>" 6.41 +__status__ = "beta" 6.42 +__version__ = "0.4.9.2" 6.43 +__date__ = "28 February 2004" 6.44 + 6.45 +#--------------------------------------------------------------------------- 6.46 +# Miscellaneous module data 6.47 +#--------------------------------------------------------------------------- 6.48 + 6.49 +# 6.50 +#_srcfile is used when walking the stack to check when we've got the first 6.51 +# caller stack frame. 6.52 +# 6.53 +if string.lower(__file__[-4:]) in ['.pyc', '.pyo']: 6.54 + _srcfile = __file__[:-4] + '.py' 6.55 +else: 6.56 + _srcfile = __file__ 6.57 +_srcfile = os.path.normcase(_srcfile) 6.58 + 6.59 +# _srcfile is only used in conjunction with sys._getframe(). 6.60 +# To provide compatibility with older versions of Python, set _srcfile 6.61 +# to None if _getframe() is not available; this value will prevent 6.62 +# findCaller() from being called. 6.63 +if not hasattr(sys, "_getframe"): 6.64 + _srcfile = None 6.65 + 6.66 +# 6.67 +#_startTime is used as the base when calculating the relative time of events 6.68 +# 6.69 +_startTime = time.time() 6.70 + 6.71 +# 6.72 +#raiseExceptions is used to see if exceptions during handling should be 6.73 +#propagated 6.74 +# 6.75 +raiseExceptions = 1 6.76 + 6.77 +#--------------------------------------------------------------------------- 6.78 +# Level related stuff 6.79 +#--------------------------------------------------------------------------- 6.80 +# 6.81 +# Default levels and level names, these can be replaced with any positive set 6.82 +# of values having corresponding names. There is a pseudo-level, NOTSET, which 6.83 +# is only really there as a lower limit for user-defined levels. Handlers and 6.84 +# loggers are initialized with NOTSET so that they will log all messages, even 6.85 +# at user-defined levels. 6.86 +# 6.87 +CRITICAL = 50 6.88 +FATAL = CRITICAL 6.89 +ERROR = 40 6.90 +WARNING = 30 6.91 +WARN = WARNING 6.92 +INFO = 20 6.93 +DEBUG = 10 6.94 +NOTSET = 0 6.95 + 6.96 +_levelNames = { 6.97 + CRITICAL : 'CRITICAL', 6.98 + ERROR : 'ERROR', 6.99 + WARNING : 'WARNING', 6.100 + INFO : 'INFO', 6.101 + DEBUG : 'DEBUG', 6.102 + NOTSET : 'NOTSET', 6.103 + 'CRITICAL' : CRITICAL, 6.104 + 'ERROR' : ERROR, 6.105 + 'WARN' : WARNING, 6.106 + 'WARNING' : WARNING, 6.107 + 'INFO' : INFO, 6.108 + 'DEBUG' : DEBUG, 6.109 + 'NOTSET' : NOTSET, 6.110 +} 6.111 + 6.112 +def getLevelName(level): 6.113 + """ 6.114 + Return the textual representation of logging level 'level'. 6.115 + 6.116 + If the level is one of the predefined levels (CRITICAL, ERROR, WARNING, 6.117 + INFO, DEBUG) then you get the corresponding string. If you have 6.118 + associated levels with names using addLevelName then the name you have 6.119 + associated with 'level' is returned. Otherwise, the string 6.120 + "Level %s" % level is returned. 6.121 + """ 6.122 + return _levelNames.get(level, ("Level %s" % level)) 6.123 + 6.124 +def addLevelName(level, levelName): 6.125 + """ 6.126 + Associate 'levelName' with 'level'. 6.127 + 6.128 + This is used when converting levels to text during message formatting. 6.129 + """ 6.130 + _acquireLock() 6.131 + try: #unlikely to cause an exception, but you never know... 6.132 + _levelNames[level] = levelName 6.133 + _levelNames[levelName] = level 6.134 + finally: 6.135 + _releaseLock() 6.136 + 6.137 +#--------------------------------------------------------------------------- 6.138 +# Thread-related stuff 6.139 +#--------------------------------------------------------------------------- 6.140 + 6.141 +# 6.142 +#_lock is used to serialize access to shared data structures in this module. 6.143 +#This needs to be an RLock because fileConfig() creates Handlers and so 6.144 +#might arbitrary user threads. Since Handler.__init__() updates the shared 6.145 +#dictionary _handlers, it needs to acquire the lock. But if configuring, 6.146 +#the lock would already have been acquired - so we need an RLock. 6.147 +#The same argument applies to Loggers and Manager.loggerDict. 6.148 +# 6.149 +_lock = None 6.150 + 6.151 +def _acquireLock(): 6.152 + """ 6.153 + Acquire the module-level lock for serializing access to shared data. 6.154 + 6.155 + This should be released with _releaseLock(). 6.156 + """ 6.157 + global _lock 6.158 + if (not _lock) and thread: 6.159 + _lock = threading.RLock() 6.160 + if _lock: 6.161 + _lock.acquire() 6.162 + 6.163 +def _releaseLock(): 6.164 + """ 6.165 + Release the module-level lock acquired by calling _acquireLock(). 6.166 + """ 6.167 + if _lock: 6.168 + _lock.release() 6.169 + 6.170 +#--------------------------------------------------------------------------- 6.171 +# The logging record 6.172 +#--------------------------------------------------------------------------- 6.173 + 6.174 +class LogRecord: 6.175 + """ 6.176 + A LogRecord instance represents an event being logged. 6.177 + 6.178 + LogRecord instances are created every time something is logged. They 6.179 + contain all the information pertinent to the event being logged. The 6.180 + main information passed in is in msg and args, which are combined 6.181 + using str(msg) % args to create the message field of the record. The 6.182 + record also includes information such as when the record was created, 6.183 + the source line where the logging call was made, and any exception 6.184 + information to be logged. 6.185 + """ 6.186 + def __init__(self, name, level, pathname, lineno, msg, args, exc_info): 6.187 + """ 6.188 + Initialize a logging record with interesting information. 6.189 + """ 6.190 + ct = time.time() 6.191 + self.name = name 6.192 + self.msg = msg 6.193 + self.args = args 6.194 + self.levelname = getLevelName(level) 6.195 + self.levelno = level 6.196 + self.pathname = pathname 6.197 + try: 6.198 + self.filename = os.path.basename(pathname) 6.199 + self.module = os.path.splitext(self.filename)[0] 6.200 + except: 6.201 + self.filename = pathname 6.202 + self.module = "Unknown module" 6.203 + self.exc_info = exc_info 6.204 + self.exc_text = None # used to cache the traceback text 6.205 + self.lineno = lineno 6.206 + self.created = ct 6.207 + self.msecs = (ct - long(ct)) * 1000 6.208 + self.relativeCreated = (self.created - _startTime) * 1000 6.209 + if thread: 6.210 + self.thread = thread.get_ident() 6.211 + else: 6.212 + self.thread = None 6.213 + if hasattr(os, 'getpid'): 6.214 + self.process = os.getpid() 6.215 + else: 6.216 + self.process = None 6.217 + 6.218 + def __str__(self): 6.219 + return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno, 6.220 + self.pathname, self.lineno, self.msg) 6.221 + 6.222 + def getMessage(self): 6.223 + """ 6.224 + Return the message for this LogRecord. 6.225 + 6.226 + Return the message for this LogRecord after merging any user-supplied 6.227 + arguments with the message. 6.228 + """ 6.229 + if not hasattr(types, "UnicodeType"): #if no unicode support... 6.230 + msg = str(self.msg) 6.231 + else: 6.232 + try: 6.233 + msg = str(self.msg) 6.234 + except UnicodeError: 6.235 + msg = self.msg #Defer encoding till later 6.236 + if self.args: 6.237 + msg = msg % self.args 6.238 + return msg 6.239 + 6.240 +def makeLogRecord(dict): 6.241 + """ 6.242 + Make a LogRecord whose attributes are defined by the specified dictionary, 6.243 + This function is useful for converting a logging event received over 6.244 + a socket connection (which is sent as a dictionary) into a LogRecord 6.245 + instance. 6.246 + """ 6.247 + rv = LogRecord(None, None, "", 0, "", (), None) 6.248 + rv.__dict__.update(dict) 6.249 + return rv 6.250 + 6.251 +#--------------------------------------------------------------------------- 6.252 +# Formatter classes and functions 6.253 +#--------------------------------------------------------------------------- 6.254 + 6.255 +class Formatter: 6.256 + """ 6.257 + Formatter instances are used to convert a LogRecord to text. 6.258 + 6.259 + Formatters need to know how a LogRecord is constructed. They are 6.260 + responsible for converting a LogRecord to (usually) a string which can 6.261 + be interpreted by either a human or an external system. The base Formatter 6.262 + allows a formatting string to be specified. If none is supplied, the 6.263 + default value of "%s(message)\\n" is used. 6.264 + 6.265 + The Formatter can be initialized with a format string which makes use of 6.266 + knowledge of the LogRecord attributes - e.g. the default value mentioned 6.267 + above makes use of the fact that the user's message and arguments are pre- 6.268 + formatted into a LogRecord's message attribute. Currently, the useful 6.269 + attributes in a LogRecord are described by: 6.270 + 6.271 + %(name)s Name of the logger (logging channel) 6.272 + %(levelno)s Numeric logging level for the message (DEBUG, INFO, 6.273 + WARNING, ERROR, CRITICAL) 6.274 + %(levelname)s Text logging level for the message ("DEBUG", "INFO", 6.275 + "WARNING", "ERROR", "CRITICAL") 6.276 + %(pathname)s Full pathname of the source file where the logging 6.277 + call was issued (if available) 6.278 + %(filename)s Filename portion of pathname 6.279 + %(module)s Module (name portion of filename) 6.280 + %(lineno)d Source line number where the logging call was issued 6.281 + (if available) 6.282 + %(created)f Time when the LogRecord was created (time.time() 6.283 + return value) 6.284 + %(asctime)s Textual time when the LogRecord was created 6.285 + %(msecs)d Millisecond portion of the creation time 6.286 + %(relativeCreated)d Time in milliseconds when the LogRecord was created, 6.287 + relative to the time the logging module was loaded 6.288 + (typically at application startup time) 6.289 + %(thread)d Thread ID (if available) 6.290 + %(process)d Process ID (if available) 6.291 + %(message)s The result of record.getMessage(), computed just as 6.292 + the record is emitted 6.293 + """ 6.294 + 6.295 + converter = time.localtime 6.296 + 6.297 + def __init__(self, fmt=None, datefmt=None): 6.298 + """ 6.299 + Initialize the formatter with specified format strings. 6.300 + 6.301 + Initialize the formatter either with the specified format string, or a 6.302 + default as described above. Allow for specialized date formatting with 6.303 + the optional datefmt argument (if omitted, you get the ISO8601 format). 6.304 + """ 6.305 + if fmt: 6.306 + self._fmt = fmt 6.307 + else: 6.308 + self._fmt = "%(message)s" 6.309 + self.datefmt = datefmt 6.310 + 6.311 + def formatTime(self, record, datefmt=None): 6.312 + """ 6.313 + Return the creation time of the specified LogRecord as formatted text. 6.314 + 6.315 + This method should be called from format() by a formatter which 6.316 + wants to make use of a formatted time. This method can be overridden 6.317 + in formatters to provide for any specific requirement, but the 6.318 + basic behaviour is as follows: if datefmt (a string) is specified, 6.319 + it is used with time.strftime() to format the creation time of the 6.320 + record. Otherwise, the ISO8601 format is used. The resulting 6.321 + string is returned. This function uses a user-configurable function 6.322 + to convert the creation time to a tuple. By default, time.localtime() 6.323 + is used; to change this for a particular formatter instance, set the 6.324 + 'converter' attribute to a function with the same signature as 6.325 + time.localtime() or time.gmtime(). To change it for all formatters, 6.326 + for example if you want all logging times to be shown in GMT, 6.327 + set the 'converter' attribute in the Formatter class. 6.328 + """ 6.329 + ct = self.converter(record.created) 6.330 + if datefmt: 6.331 + s = time.strftime(datefmt, ct) 6.332 + else: 6.333 + t = time.strftime("%Y-%m-%d %H:%M:%S", ct) 6.334 + s = "%s,%03d" % (t, record.msecs) 6.335 + return s 6.336 + 6.337 + def formatException(self, ei): 6.338 + """ 6.339 + Format and return the specified exception information as a string. 6.340 + 6.341 + This default implementation just uses 6.342 + traceback.print_exception() 6.343 + """ 6.344 + import traceback 6.345 + sio = cStringIO.StringIO() 6.346 + traceback.print_exception(ei[0], ei[1], ei[2], None, sio) 6.347 + s = sio.getvalue() 6.348 + sio.close() 6.349 + if s[-1] == "\n": 6.350 + s = s[:-1] 6.351 + return s 6.352 + 6.353 + def format(self, record): 6.354 + """ 6.355 + Format the specified record as text. 6.356 + 6.357 + The record's attribute dictionary is used as the operand to a 6.358 + string formatting operation which yields the returned string. 6.359 + Before formatting the dictionary, a couple of preparatory steps 6.360 + are carried out. The message attribute of the record is computed 6.361 + using LogRecord.getMessage(). If the formatting string contains 6.362 + "%(asctime)", formatTime() is called to format the event time. 6.363 + If there is exception information, it is formatted using 6.364 + formatException() and appended to the message. 6.365 + """ 6.366 + record.message = record.getMessage() 6.367 + if string.find(self._fmt,"%(asctime)") >= 0: 6.368 + record.asctime = self.formatTime(record, self.datefmt) 6.369 + s = self._fmt % record.__dict__ 6.370 + if record.exc_info: 6.371 + # Cache the traceback text to avoid converting it multiple times 6.372 + # (it's constant anyway) 6.373 + if not record.exc_text: 6.374 + record.exc_text = self.formatException(record.exc_info) 6.375 + if record.exc_text: 6.376 + if s[-1] != "\n": 6.377 + s = s + "\n" 6.378 + s = s + record.exc_text 6.379 + return s 6.380 + 6.381 +# 6.382 +# The default formatter to use when no other is specified 6.383 +# 6.384 +_defaultFormatter = Formatter() 6.385 + 6.386 +class BufferingFormatter: 6.387 + """ 6.388 + A formatter suitable for formatting a number of records. 6.389 + """ 6.390 + def __init__(self, linefmt=None): 6.391 + """ 6.392 + Optionally specify a formatter which will be used to format each 6.393 + individual record. 6.394 + """ 6.395 + if linefmt: 6.396 + self.linefmt = linefmt 6.397 + else: 6.398 + self.linefmt = _defaultFormatter 6.399 + 6.400 + def formatHeader(self, records): 6.401 + """ 6.402 + Return the header string for the specified records. 6.403 + """ 6.404 + return "" 6.405 + 6.406 + def formatFooter(self, records): 6.407 + """ 6.408 + Return the footer string for the specified records. 6.409 + """ 6.410 + return "" 6.411 + 6.412 + def format(self, records): 6.413 + """ 6.414 + Format the specified records and return the result as a string. 6.415 + """ 6.416 + rv = "" 6.417 + if len(records) > 0: 6.418 + rv = rv + self.formatHeader(records) 6.419 + for record in records: 6.420 + rv = rv + self.linefmt.format(record) 6.421 + rv = rv + self.formatFooter(records) 6.422 + return rv 6.423 + 6.424 +#--------------------------------------------------------------------------- 6.425 +# Filter classes and functions 6.426 +#--------------------------------------------------------------------------- 6.427 + 6.428 +class Filter: 6.429 + """ 6.430 + Filter instances are used to perform arbitrary filtering of LogRecords. 6.431 + 6.432 + Loggers and Handlers can optionally use Filter instances to filter 6.433 + records as desired. The base filter class only allows events which are 6.434 + below a certain point in the logger hierarchy. For example, a filter 6.435 + initialized with "A.B" will allow events logged by loggers "A.B", 6.436 + "A.B.C", "A.B.C.D", "A.B.D" etc. but not "A.BB", "B.A.B" etc. If 6.437 + initialized with the empty string, all events are passed. 6.438 + """ 6.439 + def __init__(self, name=''): 6.440 + """ 6.441 + Initialize a filter. 6.442 + 6.443 + Initialize with the name of the logger which, together with its 6.444 + children, will have its events allowed through the filter. If no 6.445 + name is specified, allow every event. 6.446 + """ 6.447 + self.name = name 6.448 + self.nlen = len(name) 6.449 + 6.450 + def filter(self, record): 6.451 + """ 6.452 + Determine if the specified record is to be logged. 6.453 + 6.454 + Is the specified record to be logged? Returns 0 for no, nonzero for 6.455 + yes. If deemed appropriate, the record may be modified in-place. 6.456 + """ 6.457 + if self.nlen == 0: 6.458 + return 1 6.459 + elif self.name == record.name: 6.460 + return 1 6.461 + elif string.find(record.name, self.name, 0, self.nlen) != 0: 6.462 + return 0 6.463 + return (record.name[self.nlen] == ".") 6.464 + 6.465 +class Filterer: 6.466 + """ 6.467 + A base class for loggers and handlers which allows them to share 6.468 + common code. 6.469 + """ 6.470 + def __init__(self): 6.471 + """ 6.472 + Initialize the list of filters to be an empty list. 6.473 + """ 6.474 + self.filters = [] 6.475 + 6.476 + def addFilter(self, filter): 6.477 + """ 6.478 + Add the specified filter to this handler. 6.479 + """ 6.480 + if not (filter in self.filters): 6.481 + self.filters.append(filter) 6.482 + 6.483 + def removeFilter(self, filter): 6.484 + """ 6.485 + Remove the specified filter from this handler. 6.486 + """ 6.487 + if filter in self.filters: 6.488 + self.filters.remove(filter) 6.489 + 6.490 + def filter(self, record): 6.491 + """ 6.492 + Determine if a record is loggable by consulting all the filters. 6.493 + 6.494 + The default is to allow the record to be logged; any filter can veto 6.495 + this and the record is then dropped. Returns a zero value if a record 6.496 + is to be dropped, else non-zero. 6.497 + """ 6.498 + rv = 1 6.499 + for f in self.filters: 6.500 + if not f.filter(record): 6.501 + rv = 0 6.502 + break 6.503 + return rv 6.504 + 6.505 +#--------------------------------------------------------------------------- 6.506 +# Handler classes and functions 6.507 +#--------------------------------------------------------------------------- 6.508 + 6.509 +_handlers = {} #repository of handlers (for flushing when shutdown called) 6.510 + 6.511 +class Handler(Filterer): 6.512 + """ 6.513 + Handler instances dispatch logging events to specific destinations. 6.514 + 6.515 + The base handler class. Acts as a placeholder which defines the Handler 6.516 + interface. Handlers can optionally use Formatter instances to format 6.517 + records as desired. By default, no formatter is specified; in this case, 6.518 + the 'raw' message as determined by record.message is logged. 6.519 + """ 6.520 + def __init__(self, level=NOTSET): 6.521 + """ 6.522 + Initializes the instance - basically setting the formatter to None 6.523 + and the filter list to empty. 6.524 + """ 6.525 + Filterer.__init__(self) 6.526 + self.level = level 6.527 + self.formatter = None 6.528 + #get the module data lock, as we're updating a shared structure. 6.529 + _acquireLock() 6.530 + try: #unlikely to raise an exception, but you never know... 6.531 + _handlers[self] = 1 6.532 + finally: 6.533 + _releaseLock() 6.534 + self.createLock() 6.535 + 6.536 + def createLock(self): 6.537 + """ 6.538 + Acquire a thread lock for serializing access to the underlying I/O. 6.539 + """ 6.540 + if thread: 6.541 + self.lock = thread.allocate_lock() 6.542 + else: 6.543 + self.lock = None 6.544 + 6.545 + def acquire(self): 6.546 + """ 6.547 + Acquire the I/O thread lock. 6.548 + """ 6.549 + if self.lock: 6.550 + self.lock.acquire() 6.551 + 6.552 + def release(self): 6.553 + """ 6.554 + Release the I/O thread lock. 6.555 + """ 6.556 + if self.lock: 6.557 + self.lock.release() 6.558 + 6.559 + def setLevel(self, level): 6.560 + """ 6.561 + Set the logging level of this handler. 6.562 + """ 6.563 + self.level = level 6.564 + 6.565 + def format(self, record): 6.566 + """ 6.567 + Format the specified record. 6.568 + 6.569 + If a formatter is set, use it. Otherwise, use the default formatter 6.570 + for the module. 6.571 + """ 6.572 + if self.formatter: 6.573 + fmt = self.formatter 6.574 + else: 6.575 + fmt = _defaultFormatter 6.576 + return fmt.format(record) 6.577 + 6.578 + def emit(self, record): 6.579 + """ 6.580 + Do whatever it takes to actually log the specified logging record. 6.581 + 6.582 + This version is intended to be implemented by subclasses and so 6.583 + raises a NotImplementedError. 6.584 + """ 6.585 + raise NotImplementedError, 'emit must be implemented '\ 6.586 + 'by Handler subclasses' 6.587 + 6.588 + def handle(self, record): 6.589 + """ 6.590 + Conditionally emit the specified logging record. 6.591 + 6.592 + Emission depends on filters which may have been added to the handler. 6.593 + Wrap the actual emission of the record with acquisition/release of 6.594 + the I/O thread lock. Returns whether the filter passed the record for 6.595 + emission. 6.596 + """ 6.597 + rv = self.filter(record) 6.598 + if rv: 6.599 + self.acquire() 6.600 + try: 6.601 + self.emit(record) 6.602 + finally: 6.603 + self.release() 6.604 + return rv 6.605 + 6.606 + def setFormatter(self, fmt): 6.607 + """ 6.608 + Set the formatter for this handler. 6.609 + """ 6.610 + self.formatter = fmt 6.611 + 6.612 + def flush(self): 6.613 + """ 6.614 + Ensure all logging output has been flushed. 6.615 + 6.616 + This version does nothing and is intended to be implemented by 6.617 + subclasses. 6.618 + """ 6.619 + pass 6.620 + 6.621 + def close(self): 6.622 + """ 6.623 + Tidy up any resources used by the handler. 6.624 + 6.625 + This version does removes the handler from an internal list 6.626 + of handlers which is closed when shutdown() is called. Subclasses 6.627 + should ensure that this gets called from overridden close() 6.628 + methods. 6.629 + """ 6.630 + #get the module data lock, as we're updating a shared structure. 6.631 + _acquireLock() 6.632 + try: #unlikely to raise an exception, but you never know... 6.633 + del _handlers[self] 6.634 + finally: 6.635 + _releaseLock() 6.636 + 6.637 + def handleError(self, record): 6.638 + """ 6.639 + Handle errors which occur during an emit() call. 6.640 + 6.641 + This method should be called from handlers when an exception is 6.642 + encountered during an emit() call. If raiseExceptions is false, 6.643 + exceptions get silently ignored. This is what is mostly wanted 6.644 + for a logging system - most users will not care about errors in 6.645 + the logging system, they are more interested in application errors. 6.646 + You could, however, replace this with a custom handler if you wish. 6.647 + The record which was being processed is passed in to this method. 6.648 + """ 6.649 + if raiseExceptions: 6.650 + import traceback 6.651 + ei = sys.exc_info() 6.652 + traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) 6.653 + del ei 6.654 + 6.655 +class StreamHandler(Handler): 6.656 + """ 6.657 + A handler class which writes logging records, appropriately formatted, 6.658 + to a stream. Note that this class does not close the stream, as 6.659 + sys.stdout or sys.stderr may be used. 6.660 + """ 6.661 + def __init__(self, strm=None): 6.662 + """ 6.663 + Initialize the handler. 6.664 + 6.665 + If strm is not specified, sys.stderr is used. 6.666 + """ 6.667 + Handler.__init__(self) 6.668 + if not strm: 6.669 + strm = sys.stderr 6.670 + self.stream = strm 6.671 + self.formatter = None 6.672 + 6.673 + def flush(self): 6.674 + """ 6.675 + Flushes the stream. 6.676 + """ 6.677 + self.stream.flush() 6.678 + 6.679 + def emit(self, record): 6.680 + """ 6.681 + Emit a record. 6.682 + 6.683 + If a formatter is specified, it is used to format the record. 6.684 + The record is then written to the stream with a trailing newline 6.685 + [N.B. this may be removed depending on feedback]. If exception 6.686 + information is present, it is formatted using 6.687 + traceback.print_exception and appended to the stream. 6.688 + """ 6.689 + try: 6.690 + msg = self.format(record) 6.691 + if not hasattr(types, "UnicodeType"): #if no unicode support... 6.692 + self.stream.write("%s\n" % msg) 6.693 + else: 6.694 + try: 6.695 + self.stream.write("%s\n" % msg) 6.696 + except UnicodeError: 6.697 + self.stream.write("%s\n" % msg.encode("UTF-8")) 6.698 + self.flush() 6.699 + except: 6.700 + self.handleError(record) 6.701 + 6.702 +class FileHandler(StreamHandler): 6.703 + """ 6.704 + A handler class which writes formatted logging records to disk files. 6.705 + """ 6.706 + def __init__(self, filename, mode="a"): 6.707 + """ 6.708 + Open the specified file and use it as the stream for logging. 6.709 + """ 6.710 + StreamHandler.__init__(self, open(filename, mode)) 6.711 + self.baseFilename = filename 6.712 + self.mode = mode 6.713 + 6.714 + def close(self): 6.715 + """ 6.716 + Closes the stream. 6.717 + """ 6.718 + self.flush() 6.719 + self.stream.close() 6.720 + StreamHandler.close(self) 6.721 + 6.722 +#--------------------------------------------------------------------------- 6.723 +# Manager classes and functions 6.724 +#--------------------------------------------------------------------------- 6.725 + 6.726 +class PlaceHolder: 6.727 + """ 6.728 + PlaceHolder instances are used in the Manager logger hierarchy to take 6.729 + the place of nodes for which no loggers have been defined. This class is 6.730 + intended for internal use only and not as part of the public API. 6.731 + """ 6.732 + def __init__(self, alogger): 6.733 + """ 6.734 + Initialize with the specified logger being a child of this placeholder. 6.735 + """ 6.736 + self.loggers = [alogger] 6.737 + 6.738 + def append(self, alogger): 6.739 + """ 6.740 + Add the specified logger as a child of this placeholder. 6.741 + """ 6.742 + if alogger not in self.loggers: 6.743 + self.loggers.append(alogger) 6.744 + 6.745 +# 6.746 +# Determine which class to use when instantiating loggers. 6.747 +# 6.748 +_loggerClass = None 6.749 + 6.750 +def setLoggerClass(klass): 6.751 + """ 6.752 + Set the class to be used when instantiating a logger. The class should 6.753 + define __init__() such that only a name argument is required, and the 6.754 + __init__() should call Logger.__init__() 6.755 + """ 6.756 + if klass != Logger: 6.757 + if not issubclass(klass, Logger): 6.758 + raise TypeError, "logger not derived from logging.Logger: " + \ 6.759 + klass.__name__ 6.760 + global _loggerClass 6.761 + _loggerClass = klass 6.762 + 6.763 +class Manager: 6.764 + """ 6.765 + There is [under normal circumstances] just one Manager instance, which 6.766 + holds the hierarchy of loggers. 6.767 + """ 6.768 + def __init__(self, rootnode): 6.769 + """ 6.770 + Initialize the manager with the root node of the logger hierarchy. 6.771 + """ 6.772 + self.root = rootnode 6.773 + self.disable = 0 6.774 + self.emittedNoHandlerWarning = 0 6.775 + self.loggerDict = {} 6.776 + 6.777 + def getLogger(self, name): 6.778 + """ 6.779 + Get a logger with the specified name (channel name), creating it 6.780 + if it doesn't yet exist. 6.781 + 6.782 + If a PlaceHolder existed for the specified name [i.e. the logger 6.783 + didn't exist but a child of it did], replace it with the created 6.784 + logger and fix up the parent/child references which pointed to the 6.785 + placeholder to now point to the logger. 6.786 + """ 6.787 + rv = None 6.788 + _acquireLock() 6.789 + try: 6.790 + if self.loggerDict.has_key(name): 6.791 + rv = self.loggerDict[name] 6.792 + if isinstance(rv, PlaceHolder): 6.793 + ph = rv 6.794 + rv = _loggerClass(name) 6.795 + rv.manager = self 6.796 + self.loggerDict[name] = rv 6.797 + self._fixupChildren(ph, rv) 6.798 + self._fixupParents(rv) 6.799 + else: 6.800 + rv = _loggerClass(name) 6.801 + rv.manager = self 6.802 + self.loggerDict[name] = rv 6.803 + self._fixupParents(rv) 6.804 + finally: 6.805 + _releaseLock() 6.806 + return rv 6.807 + 6.808 + def _fixupParents(self, alogger): 6.809 + """ 6.810 + Ensure that there are either loggers or placeholders all the way 6.811 + from the specified logger to the root of the logger hierarchy. 6.812 + """ 6.813 + name = alogger.name 6.814 + i = string.rfind(name, ".") 6.815 + rv = None 6.816 + while (i > 0) and not rv: 6.817 + substr = name[:i] 6.818 + if not self.loggerDict.has_key(substr): 6.819 + self.loggerDict[substr] = PlaceHolder(alogger) 6.820 + else: 6.821 + obj = self.loggerDict[substr] 6.822 + if isinstance(obj, Logger): 6.823 + rv = obj 6.824 + else: 6.825 + assert isinstance(obj, PlaceHolder) 6.826 + obj.append(alogger) 6.827 + i = string.rfind(name, ".", 0, i - 1) 6.828 + if not rv: 6.829 + rv = self.root 6.830 + alogger.parent = rv 6.831 + 6.832 + def _fixupChildren(self, ph, alogger): 6.833 + """ 6.834 + Ensure that children of the placeholder ph are connected to the 6.835 + specified logger. 6.836 + """ 6.837 + for c in ph.loggers: 6.838 + if string.find(c.parent.name, alogger.name) <> 0: 6.839 + alogger.parent = c.parent 6.840 + c.parent = alogger 6.841 + 6.842 +#--------------------------------------------------------------------------- 6.843 +# Logger classes and functions 6.844 +#--------------------------------------------------------------------------- 6.845 + 6.846 +class Logger(Filterer): 6.847 + """ 6.848 + Instances of the Logger class represent a single logging channel. A 6.849 + "logging channel" indicates an area of an application. Exactly how an 6.850 + "area" is defined is up to the application developer. Since an 6.851 + application can have any number of areas, logging channels are identified 6.852 + by a unique string. Application areas can be nested (e.g. an area 6.853 + of "input processing" might include sub-areas "read CSV files", "read 6.854 + XLS files" and "read Gnumeric files"). To cater for this natural nesting, 6.855 + channel names are organized into a namespace hierarchy where levels are 6.856 + separated by periods, much like the Java or Python package namespace. So 6.857 + in the instance given above, channel names might be "input" for the upper 6.858 + level, and "input.csv", "input.xls" and "input.gnu" for the sub-levels. 6.859 + There is no arbitrary limit to the depth of nesting. 6.860 + """ 6.861 + def __init__(self, name, level=NOTSET): 6.862 + """ 6.863 + Initialize the logger with a name and an optional level. 6.864 + """ 6.865 + Filterer.__init__(self) 6.866 + self.name = name 6.867 + self.level = level 6.868 + self.parent = None 6.869 + self.propagate = 1 6.870 + self.handlers = [] 6.871 + self.disabled = 0 6.872 + 6.873 + def setLevel(self, level): 6.874 + """ 6.875 + Set the logging level of this logger. 6.876 + """ 6.877 + self.level = level 6.878 + 6.879 +# def getRoot(self): 6.880 +# """ 6.881 +# Get the root of the logger hierarchy. 6.882 +# """ 6.883 +# return Logger.root 6.884 + 6.885 + def debug(self, msg, *args, **kwargs): 6.886 + """ 6.887 + Log 'msg % args' with severity 'DEBUG'. 6.888 + 6.889 + To pass exception information, use the keyword argument exc_info with 6.890 + a true value, e.g. 6.891 + 6.892 + logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) 6.893 + """ 6.894 + if self.manager.disable >= DEBUG: 6.895 + return 6.896 + if DEBUG >= self.getEffectiveLevel(): 6.897 + apply(self._log, (DEBUG, msg, args), kwargs) 6.898 + 6.899 + def info(self, msg, *args, **kwargs): 6.900 + """ 6.901 + Log 'msg % args' with severity 'INFO'. 6.902 + 6.903 + To pass exception information, use the keyword argument exc_info with 6.904 + a true value, e.g. 6.905 + 6.906 + logger.info("Houston, we have a %s", "interesting problem", exc_info=1) 6.907 + """ 6.908 + if self.manager.disable >= INFO: 6.909 + return 6.910 + if INFO >= self.getEffectiveLevel(): 6.911 + apply(self._log, (INFO, msg, args), kwargs) 6.912 + 6.913 + def warning(self, msg, *args, **kwargs): 6.914 + """ 6.915 + Log 'msg % args' with severity 'WARNING'. 6.916 + 6.917 + To pass exception information, use the keyword argument exc_info with 6.918 + a true value, e.g. 6.919 + 6.920 + logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1) 6.921 + """ 6.922 + if self.manager.disable >= WARNING: 6.923 + return 6.924 + if self.isEnabledFor(WARNING): 6.925 + apply(self._log, (WARNING, msg, args), kwargs) 6.926 + 6.927 + warn = warning 6.928 + 6.929 + def error(self, msg, *args, **kwargs): 6.930 + """ 6.931 + Log 'msg % args' with severity 'ERROR'. 6.932 + 6.933 + To pass exception information, use the keyword argument exc_info with 6.934 + a true value, e.g. 6.935 + 6.936 + logger.error("Houston, we have a %s", "major problem", exc_info=1) 6.937 + """ 6.938 + if self.manager.disable >= ERROR: 6.939 + return 6.940 + if self.isEnabledFor(ERROR): 6.941 + apply(self._log, (ERROR, msg, args), kwargs) 6.942 + 6.943 + def exception(self, msg, *args): 6.944 + """ 6.945 + Convenience method for logging an ERROR with exception information. 6.946 + """ 6.947 + apply(self.error, (msg,) + args, {'exc_info': 1}) 6.948 + 6.949 + def critical(self, msg, *args, **kwargs): 6.950 + """ 6.951 + Log 'msg % args' with severity 'CRITICAL'. 6.952 + 6.953 + To pass exception information, use the keyword argument exc_info with 6.954 + a true value, e.g. 6.955 + 6.956 + logger.critical("Houston, we have a %s", "major disaster", exc_info=1) 6.957 + """ 6.958 + if self.manager.disable >= CRITICAL: 6.959 + return 6.960 + if CRITICAL >= self.getEffectiveLevel(): 6.961 + apply(self._log, (CRITICAL, msg, args), kwargs) 6.962 + 6.963 + fatal = critical 6.964 + 6.965 + def log(self, level, msg, *args, **kwargs): 6.966 + """ 6.967 + Log 'msg % args' with the severity 'level'. 6.968 + 6.969 + To pass exception information, use the keyword argument exc_info with 6.970 + a true value, e.g. 6.971 + 6.972 + logger.log(level, "We have a %s", "mysterious problem", exc_info=1) 6.973 + """ 6.974 + if self.manager.disable >= level: 6.975 + return 6.976 + if self.isEnabledFor(level): 6.977 + apply(self._log, (level, msg, args), kwargs) 6.978 + 6.979 + def findCaller(self): 6.980 + """ 6.981 + Find the stack frame of the caller so that we can note the source 6.982 + file name and line number. 6.983 + """ 6.984 + f = sys._getframe(1) 6.985 + while 1: 6.986 + co = f.f_code 6.987 + filename = os.path.normcase(co.co_filename) 6.988 + if filename == _srcfile: 6.989 + f = f.f_back 6.990 + continue 6.991 + return filename, f.f_lineno 6.992 + 6.993 + def makeRecord(self, name, level, fn, lno, msg, args, exc_info): 6.994 + """ 6.995 + A factory method which can be overridden in subclasses to create 6.996 + specialized LogRecords. 6.997 + """ 6.998 + return LogRecord(name, level, fn, lno, msg, args, exc_info) 6.999 + 6.1000 + def _log(self, level, msg, args, exc_info=None): 6.1001 + """ 6.1002 + Low-level logging routine which creates a LogRecord and then calls 6.1003 + all the handlers of this logger to handle the record. 6.1004 + """ 6.1005 + if _srcfile: 6.1006 + fn, lno = self.findCaller() 6.1007 + else: 6.1008 + fn, lno = "<unknown file>", 0 6.1009 + if exc_info: 6.1010 + if type(exc_info) != types.TupleType: 6.1011 + exc_info = sys.exc_info() 6.1012 + record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info) 6.1013 + self.handle(record) 6.1014 + 6.1015 + def handle(self, record): 6.1016 + """ 6.1017 + Call the handlers for the specified record. 6.1018 + 6.1019 + This method is used for unpickled records received from a socket, as 6.1020 + well as those created locally. Logger-level filtering is applied. 6.1021 + """ 6.1022 + if (not self.disabled) and self.filter(record): 6.1023 + self.callHandlers(record) 6.1024 + 6.1025 + def addHandler(self, hdlr): 6.1026 + """ 6.1027 + Add the specified handler to this logger. 6.1028 + """ 6.1029 + if not (hdlr in self.handlers): 6.1030 + self.handlers.append(hdlr) 6.1031 + 6.1032 + def removeHandler(self, hdlr): 6.1033 + """ 6.1034 + Remove the specified handler from this logger. 6.1035 + """ 6.1036 + if hdlr in self.handlers: 6.1037 + #hdlr.close() 6.1038 + self.handlers.remove(hdlr) 6.1039 + 6.1040 + def callHandlers(self, record): 6.1041 + """ 6.1042 + Pass a record to all relevant handlers. 6.1043 + 6.1044 + Loop through all handlers for this logger and its parents in the 6.1045 + logger hierarchy. If no handler was found, output a one-off error 6.1046 + message to sys.stderr. Stop searching up the hierarchy whenever a 6.1047 + logger with the "propagate" attribute set to zero is found - that 6.1048 + will be the last logger whose handlers are called. 6.1049 + """ 6.1050 + c = self 6.1051 + found = 0 6.1052 + while c: 6.1053 + for hdlr in c.handlers: 6.1054 + found = found + 1 6.1055 + if record.levelno >= hdlr.level: 6.1056 + hdlr.handle(record) 6.1057 + if not c.propagate: 6.1058 + c = None #break out 6.1059 + else: 6.1060 + c = c.parent 6.1061 + if (found == 0) and not self.manager.emittedNoHandlerWarning: 6.1062 + sys.stderr.write("No handlers could be found for logger" 6.1063 + " \"%s\"\n" % self.name) 6.1064 + self.manager.emittedNoHandlerWarning = 1 6.1065 + 6.1066 + def getEffectiveLevel(self): 6.1067 + """ 6.1068 + Get the effective level for this logger. 6.1069 + 6.1070 + Loop through this logger and its parents in the logger hierarchy, 6.1071 + looking for a non-zero logging level. Return the first one found. 6.1072 + """ 6.1073 + logger = self 6.1074 + while logger: 6.1075 + if logger.level: 6.1076 + return logger.level 6.1077 + logger = logger.parent 6.1078 + return NOTSET 6.1079 + 6.1080 + def isEnabledFor(self, level): 6.1081 + """ 6.1082 + Is this logger enabled for level 'level'? 6.1083 + """ 6.1084 + if self.manager.disable >= level: 6.1085 + return 0 6.1086 + return level >= self.getEffectiveLevel() 6.1087 + 6.1088 +class RootLogger(Logger): 6.1089 + """ 6.1090 + A root logger is not that different to any other logger, except that 6.1091 + it must have a logging level and there is only one instance of it in 6.1092 + the hierarchy. 6.1093 + """ 6.1094 + def __init__(self, level): 6.1095 + """ 6.1096 + Initialize the logger with the name "root". 6.1097 + """ 6.1098 + Logger.__init__(self, "root", level) 6.1099 + 6.1100 +_loggerClass = Logger 6.1101 + 6.1102 +root = RootLogger(WARNING) 6.1103 +Logger.root = root 6.1104 +Logger.manager = Manager(Logger.root) 6.1105 + 6.1106 +#--------------------------------------------------------------------------- 6.1107 +# Configuration classes and functions 6.1108 +#--------------------------------------------------------------------------- 6.1109 + 6.1110 +BASIC_FORMAT = "%(levelname)s:%(name)s:%(message)s" 6.1111 + 6.1112 +def basicConfig(): 6.1113 + """ 6.1114 + Do basic configuration for the logging system by creating a 6.1115 + StreamHandler with a default Formatter and adding it to the 6.1116 + root logger. 6.1117 + """ 6.1118 + if len(root.handlers) == 0: 6.1119 + hdlr = StreamHandler() 6.1120 + fmt = Formatter(BASIC_FORMAT) 6.1121 + hdlr.setFormatter(fmt) 6.1122 + root.addHandler(hdlr) 6.1123 + 6.1124 +#--------------------------------------------------------------------------- 6.1125 +# Utility functions at module level. 6.1126 +# Basically delegate everything to the root logger. 6.1127 +#--------------------------------------------------------------------------- 6.1128 + 6.1129 +def getLogger(name=None): 6.1130 + """ 6.1131 + Return a logger with the specified name, creating it if necessary. 6.1132 + 6.1133 + If no name is specified, return the root logger. 6.1134 + """ 6.1135 + if name: 6.1136 + return Logger.manager.getLogger(name) 6.1137 + else: 6.1138 + return root 6.1139 + 6.1140 +#def getRootLogger(): 6.1141 +# """ 6.1142 +# Return the root logger. 6.1143 +# 6.1144 +# Note that getLogger('') now does the same thing, so this function is 6.1145 +# deprecated and may disappear in the future. 6.1146 +# """ 6.1147 +# return root 6.1148 + 6.1149 +def critical(msg, *args, **kwargs): 6.1150 + """ 6.1151 + Log a message with severity 'CRITICAL' on the root logger. 6.1152 + """ 6.1153 + if len(root.handlers) == 0: 6.1154 + basicConfig() 6.1155 + apply(root.critical, (msg,)+args, kwargs) 6.1156 + 6.1157 +fatal = critical 6.1158 + 6.1159 +def error(msg, *args, **kwargs): 6.1160 + """ 6.1161 + Log a message with severity 'ERROR' on the root logger. 6.1162 + """ 6.1163 + if len(root.handlers) == 0: 6.1164 + basicConfig() 6.1165 + apply(root.error, (msg,)+args, kwargs) 6.1166 + 6.1167 +def exception(msg, *args): 6.1168 + """ 6.1169 + Log a message with severity 'ERROR' on the root logger, 6.1170 + with exception information. 6.1171 + """ 6.1172 + apply(error, (msg,)+args, {'exc_info': 1}) 6.1173 + 6.1174 +def warning(msg, *args, **kwargs): 6.1175 + """ 6.1176 + Log a message with severity 'WARNING' on the root logger. 6.1177 + """ 6.1178 + if len(root.handlers) == 0: 6.1179 + basicConfig() 6.1180 + apply(root.warning, (msg,)+args, kwargs) 6.1181 + 6.1182 +warn = warning 6.1183 + 6.1184 +def info(msg, *args, **kwargs): 6.1185 + """ 6.1186 + Log a message with severity 'INFO' on the root logger. 6.1187 + """ 6.1188 + if len(root.handlers) == 0: 6.1189 + basicConfig() 6.1190 + apply(root.info, (msg,)+args, kwargs) 6.1191 + 6.1192 +def debug(msg, *args, **kwargs): 6.1193 + """ 6.1194 + Log a message with severity 'DEBUG' on the root logger. 6.1195 + """ 6.1196 + if len(root.handlers) == 0: 6.1197 + basicConfig() 6.1198 + apply(root.debug, (msg,)+args, kwargs) 6.1199 + 6.1200 +def disable(level): 6.1201 + """ 6.1202 + Disable all logging calls less severe than 'level'. 6.1203 + """ 6.1204 + root.manager.disable = level 6.1205 + 6.1206 +def shutdown(): 6.1207 + """ 6.1208 + Perform any cleanup actions in the logging system (e.g. flushing 6.1209 + buffers). 6.1210 + 6.1211 + Should be called at application exit. 6.1212 + """ 6.1213 + for h in _handlers.keys(): 6.1214 + h.flush() 6.1215 + h.close() 6.1216 + 6.1217 +#Let's try and shutdown automatically on application exit... 6.1218 +try: 6.1219 + import atexit 6.1220 + atexit.register(shutdown) 6.1221 +except ImportError: # for Python versions < 2.0 6.1222 + def exithook(status, old_exit=sys.exit): 6.1223 + try: 6.1224 + shutdown() 6.1225 + finally: 6.1226 + old_exit(status) 6.1227 + 6.1228 + sys.exit = exithook
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/tools/python/logging/logging-0.4.9.2/logging/config.py Thu Jul 22 13:42:39 2004 +0000 7.3 @@ -0,0 +1,301 @@ 7.4 +# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. 7.5 +# 7.6 +# Permission to use, copy, modify, and distribute this software and its 7.7 +# documentation for any purpose and without fee is hereby granted, 7.8 +# provided that the above copyright notice appear in all copies and that 7.9 +# both that copyright notice and this permission notice appear in 7.10 +# supporting documentation, and that the name of Vinay Sajip 7.11 +# not be used in advertising or publicity pertaining to distribution 7.12 +# of the software without specific, written prior permission. 7.13 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 7.14 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 7.15 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 7.16 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 7.17 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 7.18 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 7.19 + 7.20 +""" 7.21 +Configuration functions for the logging package for Python. The core package 7.22 +is based on PEP 282 and comments thereto in comp.lang.python, and influenced 7.23 +by Apache's log4j system. 7.24 + 7.25 +Should work under Python versions >= 1.5.2, except that source line 7.26 +information is not available unless 'sys._getframe()' is. 7.27 + 7.28 +Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. 7.29 + 7.30 +To use, simply 'import logging' and log away! 7.31 +""" 7.32 + 7.33 +import sys, logging, logging.handlers, string, thread, threading, socket, struct, os 7.34 + 7.35 +from SocketServer import ThreadingTCPServer, StreamRequestHandler 7.36 + 7.37 + 7.38 +DEFAULT_LOGGING_CONFIG_PORT = 9030 7.39 + 7.40 +if sys.platform == "win32": 7.41 + RESET_ERROR = 10054 #WSAECONNRESET 7.42 +else: 7.43 + RESET_ERROR = 104 #ECONNRESET 7.44 + 7.45 +# 7.46 +# The following code implements a socket listener for on-the-fly 7.47 +# reconfiguration of logging. 7.48 +# 7.49 +# _listener holds the server object doing the listening 7.50 +_listener = None 7.51 + 7.52 +def fileConfig(fname, defaults=None): 7.53 + """ 7.54 + Read the logging configuration from a ConfigParser-format file. 7.55 + 7.56 + This can be called several times from an application, allowing an end user 7.57 + the ability to select from various pre-canned configurations (if the 7.58 + developer provides a mechanism to present the choices and load the chosen 7.59 + configuration). 7.60 + In versions of ConfigParser which have the readfp method [typically 7.61 + shipped in 2.x versions of Python], you can pass in a file-like object 7.62 + rather than a filename, in which case the file-like object will be read 7.63 + using readfp. 7.64 + """ 7.65 + import ConfigParser 7.66 + 7.67 + cp = ConfigParser.ConfigParser(defaults) 7.68 + if hasattr(cp, 'readfp') and hasattr(fname, 'readline'): 7.69 + cp.readfp(fname) 7.70 + else: 7.71 + cp.read(fname) 7.72 + #first, do the formatters... 7.73 + flist = cp.get("formatters", "keys") 7.74 + if len(flist): 7.75 + flist = string.split(flist, ",") 7.76 + formatters = {} 7.77 + for form in flist: 7.78 + sectname = "formatter_%s" % form 7.79 + opts = cp.options(sectname) 7.80 + if "format" in opts: 7.81 + fs = cp.get(sectname, "format", 1) 7.82 + else: 7.83 + fs = None 7.84 + if "datefmt" in opts: 7.85 + dfs = cp.get(sectname, "datefmt", 1) 7.86 + else: 7.87 + dfs = None 7.88 + f = logging.Formatter(fs, dfs) 7.89 + formatters[form] = f 7.90 + #next, do the handlers... 7.91 + #critical section... 7.92 + logging._acquireLock() 7.93 + try: 7.94 + try: 7.95 + #first, lose the existing handlers... 7.96 + logging._handlers.clear() 7.97 + #now set up the new ones... 7.98 + hlist = cp.get("handlers", "keys") 7.99 + if len(hlist): 7.100 + hlist = string.split(hlist, ",") 7.101 + handlers = {} 7.102 + fixups = [] #for inter-handler references 7.103 + for hand in hlist: 7.104 + sectname = "handler_%s" % hand 7.105 + klass = cp.get(sectname, "class") 7.106 + opts = cp.options(sectname) 7.107 + if "formatter" in opts: 7.108 + fmt = cp.get(sectname, "formatter") 7.109 + else: 7.110 + fmt = "" 7.111 + klass = eval(klass, vars(logging)) 7.112 + args = cp.get(sectname, "args") 7.113 + args = eval(args, vars(logging)) 7.114 + h = apply(klass, args) 7.115 + if "level" in opts: 7.116 + level = cp.get(sectname, "level") 7.117 + h.setLevel(logging._levelNames[level]) 7.118 + if len(fmt): 7.119 + h.setFormatter(formatters[fmt]) 7.120 + #temporary hack for FileHandler and MemoryHandler. 7.121 + if klass == logging.handlers.MemoryHandler: 7.122 + if "target" in opts: 7.123 + target = cp.get(sectname,"target") 7.124 + else: 7.125 + target = "" 7.126 + if len(target): #the target handler may not be loaded yet, so keep for later... 7.127 + fixups.append((h, target)) 7.128 + handlers[hand] = h 7.129 + #now all handlers are loaded, fixup inter-handler references... 7.130 + for fixup in fixups: 7.131 + h = fixup[0] 7.132 + t = fixup[1] 7.133 + h.setTarget(handlers[t]) 7.134 + #at last, the loggers...first the root... 7.135 + llist = cp.get("loggers", "keys") 7.136 + llist = string.split(llist, ",") 7.137 + llist.remove("root") 7.138 + sectname = "logger_root" 7.139 + root = logging.root 7.140 + log = root 7.141 + opts = cp.options(sectname) 7.142 + if "level" in opts: 7.143 + level = cp.get(sectname, "level") 7.144 + log.setLevel(logging._levelNames[level]) 7.145 + for h in root.handlers[:]: 7.146 + root.removeHandler(h) 7.147 + hlist = cp.get(sectname, "handlers") 7.148 + if len(hlist): 7.149 + hlist = string.split(hlist, ",") 7.150 + for hand in hlist: 7.151 + log.addHandler(handlers[hand]) 7.152 + #and now the others... 7.153 + #we don't want to lose the existing loggers, 7.154 + #since other threads may have pointers to them. 7.155 + #existing is set to contain all existing loggers, 7.156 + #and as we go through the new configuration we 7.157 + #remove any which are configured. At the end, 7.158 + #what's left in existing is the set of loggers 7.159 + #which were in the previous configuration but 7.160 + #which are not in the new configuration. 7.161 + existing = root.manager.loggerDict.keys() 7.162 + #now set up the new ones... 7.163 + for log in llist: 7.164 + sectname = "logger_%s" % log 7.165 + qn = cp.get(sectname, "qualname") 7.166 + opts = cp.options(sectname) 7.167 + if "propagate" in opts: 7.168 + propagate = cp.getint(sectname, "propagate") 7.169 + else: 7.170 + propagate = 1 7.171 + logger = logging.getLogger(qn) 7.172 + if qn in existing: 7.173 + existing.remove(qn) 7.174 + if "level" in opts: 7.175 + level = cp.get(sectname, "level") 7.176 + logger.setLevel(logging._levelNames[level]) 7.177 + for h in logger.handlers[:]: 7.178 + logger.removeHandler(h) 7.179 + logger.propagate = propagate 7.180 + logger.disabled = 0 7.181 + hlist = cp.get(sectname, "handlers") 7.182 + if len(hlist): 7.183 + hlist = string.split(hlist, ",") 7.184 + for hand in hlist: 7.185 + logger.addHandler(handlers[hand]) 7.186 + #Disable any old loggers. There's no point deleting 7.187 + #them as other threads may continue to hold references 7.188 + #and by disabling them, you stop them doing any logging. 7.189 + for log in existing: 7.190 + root.manager.loggerDict[log].disabled = 1 7.191 + except: 7.192 + import traceback 7.193 + ei = sys.exc_info() 7.194 + traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) 7.195 + del ei 7.196 + finally: 7.197 + logging._releaseLock() 7.198 + 7.199 +def listen(port=DEFAULT_LOGGING_CONFIG_PORT): 7.200 + """ 7.201 + Start up a socket server on the specified port, and listen for new 7.202 + configurations. 7.203 + 7.204 + These will be sent as a file suitable for processing by fileConfig(). 7.205 + Returns a Thread object on which you can call start() to start the server, 7.206 + and which you can join() when appropriate. To stop the server, call 7.207 + stopListening(). 7.208 + """ 7.209 + if not thread: 7.210 + raise NotImplementedError, "listen() needs threading to work" 7.211 + 7.212 + class ConfigStreamHandler(StreamRequestHandler): 7.213 + """ 7.214 + Handler for a logging configuration request. 7.215 + 7.216 + It expects a completely new logging configuration and uses fileConfig 7.217 + to install it. 7.218 + """ 7.219 + def handle(self): 7.220 + """ 7.221 + Handle a request. 7.222 + 7.223 + Each request is expected to be a 4-byte length, 7.224 + followed by the config file. Uses fileConfig() to do the 7.225 + grunt work. 7.226 + """ 7.227 + import tempfile 7.228 + try: 7.229 + conn = self.connection 7.230 + chunk = conn.recv(4) 7.231 + if len(chunk) == 4: 7.232 + slen = struct.unpack(">L", chunk)[0] 7.233 + chunk = self.connection.recv(slen) 7.234 + while len(chunk) < slen: 7.235 + chunk = chunk + conn.recv(slen - len(chunk)) 7.236 + #Apply new configuration. We'd like to be able to 7.237 + #create a StringIO and pass that in, but unfortunately 7.238 + #1.5.2 ConfigParser does not support reading file 7.239 + #objects, only actual files. So we create a temporary 7.240 + #file and remove it later. 7.241 + file = tempfile.mktemp(".ini") 7.242 + f = open(file, "w") 7.243 + f.write(chunk) 7.244 + f.close() 7.245 + fileConfig(file) 7.246 + os.remove(file) 7.247 + except socket.error, e: 7.248 + if type(e.args) != types.TupleType: 7.249 + raise 7.250 + else: 7.251 + errcode = e.args[0] 7.252 + if errcode != RESET_ERROR: 7.253 + raise 7.254 + 7.255 + class ConfigSocketReceiver(ThreadingTCPServer): 7.256 + """ 7.257 + A simple TCP socket-based logging config receiver. 7.258 + """ 7.259 + 7.260 + allow_reuse_address = 1 7.261 + 7.262 + def __init__(self, host='localhost', port=DEFAULT_LOGGING_CONFIG_PORT, 7.263 + handler=None): 7.264 + ThreadingTCPServer.__init__(self, (host, port), handler) 7.265 + logging._acquireLock() 7.266 + self.abort = 0 7.267 + logging._releaseLock() 7.268 + self.timeout = 1 7.269 + 7.270 + def serve_until_stopped(self): 7.271 + import select 7.272 + abort = 0 7.273 + while not abort: 7.274 + rd, wr, ex = select.select([self.socket.fileno()], 7.275 + [], [], 7.276 + self.timeout) 7.277 + if rd: 7.278 + self.handle_request() 7.279 + logging._acquireLock() 7.280 + abort = self.abort 7.281 + logging._releaseLock() 7.282 + 7.283 + def serve(rcvr, hdlr, port): 7.284 + server = rcvr(port=port, handler=hdlr) 7.285 + global _listener 7.286 + logging._acquireLock() 7.287 + _listener = server 7.288 + logging._releaseLock() 7.289 + server.serve_until_stopped() 7.290 + 7.291 + return threading.Thread(target=serve, 7.292 + args=(ConfigSocketReceiver, 7.293 + ConfigStreamHandler, port)) 7.294 + 7.295 +def stopListening(): 7.296 + """ 7.297 + Stop the listening server which was created with a call to listen(). 7.298 + """ 7.299 + global _listener 7.300 + if _listener: 7.301 + logging._acquireLock() 7.302 + _listener.abort = 1 7.303 + _listener = None 7.304 + logging._releaseLock()
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/tools/python/logging/logging-0.4.9.2/logging/handlers.py Thu Jul 22 13:42:39 2004 +0000 8.3 @@ -0,0 +1,787 @@ 8.4 +# Copyright 2001-2004 by Vinay Sajip. All Rights Reserved. 8.5 +# 8.6 +# Permission to use, copy, modify, and distribute this software and its 8.7 +# documentation for any purpose and without fee is hereby granted, 8.8 +# provided that the above copyright notice appear in all copies and that 8.9 +# both that copyright notice and this permission notice appear in 8.10 +# supporting documentation, and that the name of Vinay Sajip 8.11 +# not be used in advertising or publicity pertaining to distribution 8.12 +# of the software without specific, written prior permission. 8.13 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 8.14 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 8.15 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 8.16 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 8.17 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 8.18 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8.19 + 8.20 +""" 8.21 +Additional handlers for the logging package for Python. The core package is 8.22 +based on PEP 282 and comments thereto in comp.lang.python, and influenced by 8.23 +Apache's log4j system. 8.24 + 8.25 +Should work under Python versions >= 1.5.2, except that source line 8.26 +information is not available unless 'sys._getframe()' is. 8.27 + 8.28 +Copyright (C) 2001-2004 Vinay Sajip. All Rights Reserved. 8.29 + 8.30 +To use, simply 'import logging' and log away! 8.31 +""" 8.32 + 8.33 +import sys, logging, socket, types, os, string, cPickle, struct, time 8.34 + 8.35 +from SocketServer import ThreadingTCPServer, StreamRequestHandler 8.36 + 8.37 +# 8.38 +# Some constants... 8.39 +# 8.40 + 8.41 +DEFAULT_TCP_LOGGING_PORT = 9020 8.42 +DEFAULT_UDP_LOGGING_PORT = 9021 8.43 +DEFAULT_HTTP_LOGGING_PORT = 9022 8.44 +DEFAULT_SOAP_LOGGING_PORT = 9023 8.45 +SYSLOG_UDP_PORT = 514 8.46 + 8.47 + 8.48 +class RotatingFileHandler(logging.FileHandler): 8.49 + def __init__(self, filename, mode="a", maxBytes=0, backupCount=0): 8.50 + """ 8.51 + Open the specified file and use it as the stream for logging. 8.52 + 8.53 + By default, the file grows indefinitely. You can specify particular 8.54 + values of maxBytes and backupCount to allow the file to rollover at 8.55 + a predetermined size. 8.56 + 8.57 + Rollover occurs whenever the current log file is nearly maxBytes in 8.58 + length. If backupCount is >= 1, the system will successively create 8.59 + new files with the same pathname as the base file, but with extensions 8.60 + ".1", ".2" etc. appended to it. For example, with a backupCount of 5 8.61 + and a base file name of "app.log", you would get "app.log", 8.62 + "app.log.1", "app.log.2", ... through to "app.log.5". The file being 8.63 + written to is always "app.log" - when it gets filled up, it is closed 8.64 + and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc. 8.65 + exist, then they are renamed to "app.log.2", "app.log.3" etc. 8.66 + respectively. 8.67 + 8.68 + If maxBytes is zero, rollover never occurs. 8.69 + """ 8.70 + logging.FileHandler.__init__(self, filename, mode) 8.71 + self.maxBytes = maxBytes 8.72 + self.backupCount = backupCount 8.73 + if maxBytes > 0: 8.74 + self.mode = "a" 8.75 + 8.76 + def doRollover(self): 8.77 + """ 8.78 + Do a rollover, as described in __init__(). 8.79 + """ 8.80 + 8.81 + self.stream.close() 8.82 + if self.backupCount > 0: 8.83 + for i in range(self.backupCount - 1, 0, -1): 8.84 + sfn = "%s.%d" % (self.baseFilename, i) 8.85 + dfn = "%s.%d" % (self.baseFilename, i + 1) 8.86 + if os.path.exists(sfn): 8.87 + #print "%s -> %s" % (sfn, dfn) 8.88 + if os.path.exists(dfn): 8.89 + os.remove(dfn) 8.90 + os.rename(sfn, dfn) 8.91 + dfn = self.baseFilename + ".1" 8.92 + if os.path.exists(dfn): 8.93 + os.remove(dfn) 8.94 + os.rename(self.baseFilename, dfn) 8.95 + #print "%s -> %s" % (self.baseFilename, dfn) 8.96 + self.stream = open(self.baseFilename, "w") 8.97 + 8.98 + def emit(self, record): 8.99 + """ 8.100 + Emit a record. 8.101 + 8.102 + Output the record to the file, catering for rollover as described 8.103 + in doRollover(). 8.104 + """ 8.105 + if self.maxBytes > 0: # are we rolling over? 8.106 + msg = "%s\n" % self.format(record) 8.107 + self.stream.seek(0, 2) #due to non-posix-compliant Windows feature 8.108 + if self.stream.tell() + len(msg) >= self.maxBytes: 8.109 + self.doRollover() 8.110 + logging.FileHandler.emit(self, record) 8.111 + 8.112 + 8.113 +class SocketHandler(logging.Handler): 8.114 + """ 8.115 + A handler class which writes logging records, in pickle format, to 8.116 + a streaming socket. The socket is kept open across logging calls. 8.117 + If the peer resets it, an attempt is made to reconnect on the next call. 8.118 + The pickle which is sent is that of the LogRecord's attribute dictionary 8.119 + (__dict__), so that the receiver does not need to have the logging module 8.120 + installed in order to process the logging event. 8.121 + 8.122 + To unpickle the record at the receiving end into a LogRecord, use the 8.123 + makeLogRecord function. 8.124 + """ 8.125 + 8.126 + def __init__(self, host, port): 8.127 + """ 8.128 + Initializes the handler with a specific host address and port. 8.129 + 8.130 + The attribute 'closeOnError' is set to 1 - which means that if 8.131 + a socket error occurs, the socket is silently closed and then 8.132 + reopened on the next logging call. 8.133 + """ 8.134 + logging.Handler.__init__(self) 8.135 + self.host = host 8.136 + self.port = port 8.137 + self.sock = None 8.138 + self.closeOnError = 0 8.139 + self.retryTime = None 8.140 + # 8.141 + # Exponential backoff parameters. 8.142 + # 8.143 + self.retryStart = 1.0 8.144 + self.retryMax = 30.0 8.145 + self.retryFactor = 2.0 8.146 + 8.147 + def makeSocket(self): 8.148 + """ 8.149 + A factory method which allows subclasses to define the precise 8.150 + type of socket they want. 8.151 + """ 8.152 + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8.153 + s.connect((self.host, self.port)) 8.154 + return s 8.155 + 8.156 + def createSocket(self): 8.157 + """ 8.158 + Try to create a socket, using an exponential backoff with 8.159 + a max retry time. Thanks to Robert Olson for the original patch 8.160 + (SF #815911) which has been slightly refactored. 8.161 + """ 8.162 + now = time.time() 8.163 + # Either retryTime is None, in which case this 8.164 + # is the first time back after a disconnect, or 8.165 + # we've waited long enough. 8.166 + if self.retryTime is None: 8.167 + attempt = 1 8.168 + else: 8.169 + attempt = (now >= self.retryTime) 8.170 + if attempt: 8.171 + try: 8.172 + self.sock = self.makeSocket() 8.173 + self.retryTime = None # next time, no delay before trying 8.174 + except: 8.175 + #Creation failed, so set the retry time and return. 8.176 + if self.retryTime is None: 8.177 + self.retryPeriod = self.retryStart 8.178 + else: 8.179 + self.retryPeriod = self.retryPeriod * self.retryFactor 8.180 + if self.retryPeriod > self.retryMax: 8.181 + self.retryPeriod = self.retryMax 8.182 + self.retryTime = now + self.retryPeriod 8.183 + 8.184 + def send(self, s): 8.185 + """ 8.186 + Send a pickled string to the socket. 8.187 + 8.188 + This function allows for partial sends which can happen when the 8.189 + network is busy. 8.190 + """ 8.191 + if self.sock is None: 8.192 + self.createSocket() 8.193 + #self.sock can be None either because we haven't reached the retry 8.194 + #time yet, or because we have reached the retry time and retried, 8.195 + #but are still unable to connect. 8.196 + if self.sock: 8.197 + try: 8.198 + if hasattr(self.sock, "sendall"): 8.199 + self.sock.sendall(s) 8.200 + else: 8.201 + sentsofar = 0 8.202 + left = len(s) 8.203 + while left > 0: 8.204 + sent = self.sock.send(s[sentsofar:]) 8.205 + sentsofar = sentsofar + sent 8.206 + left = left - sent 8.207 + except socket.error: 8.208 + self.sock.close() 8.209 + self.sock = None # so we can call createSocket next time 8.210 + 8.211 + def makePickle(self, record): 8.212 + """ 8.213 + Pickles the record in binary format with a length prefix, and 8.214 + returns it ready for transmission across the socket. 8.215 + """ 8.216 + ei = record.exc_info 8.217 + if ei: 8.218 + dummy = self.format(record) # just to get traceback text into record.exc_text 8.219 + record.exc_info = None # to avoid Unpickleable error 8.220 + s = cPickle.dumps(record.__dict__, 1) 8.221 + if ei: 8.222 + record.exc_info = ei # for next handler 8.223 + slen = struct.pack(">L", len(s)) 8.224 + return slen + s 8.225 + 8.226 + def handleError(self, record): 8.227 + """ 8.228 + Handle an error during logging. 8.229 + 8.230 + An error has occurred during logging. Most likely cause - 8.231 + connection lost. Close the socket so that we can retry on the 8.232 + next event. 8.233 + """ 8.234 + if self.closeOnError and self.sock: 8.235 + self.sock.close() 8.236 + self.sock = None #try to reconnect next time 8.237 + else: 8.238 + logging.Handler.handleError(self, record) 8.239 + 8.240 + def emit(self, record): 8.241 + """ 8.242 + Emit a record. 8.243 + 8.244 + Pickles the record and writes it to the socket in binary format. 8.245 + If there is an error with the socket, silently drop the packet. 8.246 + If there was a problem with the socket, re-establishes the 8.247 + socket. 8.248 + """ 8.249 + try: 8.250 + s = self.makePickle(record) 8.251 + self.send(s) 8.252 + except: 8.253 + self.handleError(record) 8.254 + 8.255 + def close(self): 8.256 + """ 8.257 + Closes the socket. 8.258 + """ 8.259 + if self.sock: 8.260 + self.sock.close() 8.261 + self.sock = None 8.262 + logging.Handler.close(self) 8.263 + 8.264 +class DatagramHandler(SocketHandler): 8.265 + """ 8.266 + A handler class which writes logging records, in pickle format, to 8.267 + a datagram socket. The pickle which is sent is that of the LogRecord's 8.268 + attribute dictionary (__dict__), so that the receiver does not need to 8.269 + have the logging module installed in order to process the logging event. 8.270 + 8.271 + To unpickle the record at the receiving end into a LogRecord, use the 8.272 + makeLogRecord function. 8.273 + 8.274 + """ 8.275 + def __init__(self, host, port): 8.276 + """ 8.277 + Initializes the handler with a specific host address and port. 8.278 + """ 8.279 + SocketHandler.__init__(self, host, port) 8.280 + self.closeOnError = 0 8.281 + 8.282 + def makeSocket(self): 8.283 + """ 8.284 + The factory method of SocketHandler is here overridden to create 8.285 + a UDP socket (SOCK_DGRAM). 8.286 + """ 8.287 + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 8.288 + return s 8.289 + 8.290 + def send(self, s): 8.291 + """ 8.292 + Send a pickled string to a socket. 8.293 + 8.294 + This function no longer allows for partial sends which can happen 8.295 + when the network is busy - UDP does not guarantee delivery and 8.296 + can deliver packets out of sequence. 8.297 + """ 8.298 + self.sock.sendto(s, (self.host, self.port)) 8.299 + 8.300 +class SysLogHandler(logging.Handler): 8.301 + """ 8.302 + A handler class which sends formatted logging records to a syslog 8.303 + server. Based on Sam Rushing's syslog module: 8.304 + http://www.nightmare.com/squirl/python-ext/misc/syslog.py 8.305 + Contributed by Nicolas Untz (after which minor refactoring changes 8.306 + have been made). 8.307 + """ 8.308 + 8.309 + # from <linux/sys/syslog.h>: 8.310 + # ====================================================================== 8.311 + # priorities/facilities are encoded into a single 32-bit quantity, where 8.312 + # the bottom 3 bits are the priority (0-7) and the top 28 bits are the 8.313 + # facility (0-big number). Both the priorities and the facilities map 8.314 + # roughly one-to-one to strings in the syslogd(8) source code. This 8.315 + # mapping is included in this file. 8.316 + # 8.317 + # priorities (these are ordered) 8.318 + 8.319 + LOG_EMERG = 0 # system is unusable 8.320 + LOG_ALERT = 1 # action must be taken immediately 8.321 + LOG_CRIT = 2 # critical conditions 8.322 + LOG_ERR = 3 # error conditions 8.323 + LOG_WARNING = 4 # warning conditions 8.324 + LOG_NOTICE = 5 # normal but significant condition 8.325 + LOG_INFO = 6 # informational 8.326 + LOG_DEBUG = 7 # debug-level messages 8.327 + 8.328 + # facility codes 8.329 + LOG_KERN = 0 # kernel messages 8.330 + LOG_USER = 1 # random user-level messages 8.331 + LOG_MAIL = 2 # mail system 8.332 + LOG_DAEMON = 3 # system daemons 8.333 + LOG_AUTH = 4 # security/authorization messages 8.334 + LOG_SYSLOG = 5 # messages generated internally by syslogd 8.335 + LOG_LPR = 6 # line printer subsystem 8.336 + LOG_NEWS = 7 # network news subsystem 8.337 + LOG_UUCP = 8 # UUCP subsystem 8.338 + LOG_CRON = 9 # clock daemon 8.339 + LOG_AUTHPRIV = 10 # security/authorization messages (private) 8.340 + 8.341 + # other codes through 15 reserved for system use 8.342 + LOG_LOCAL0 = 16 # reserved for local use 8.343 + LOG_LOCAL1 = 17 # reserved for local use 8.344 + LOG_LOCAL2 = 18 # reserved for local use 8.345 + LOG_LOCAL3 = 19 # reserved for local use 8.346 + LOG_LOCAL4 = 20 # reserved for local use 8.347 + LOG_LOCAL5 = 21 # reserved for local use 8.348 + LOG_LOCAL6 = 22 # reserved for local use 8.349 + LOG_LOCAL7 = 23 # reserved for local use 8.350 + 8.351 + priority_names = { 8.352 + "alert": LOG_ALERT, 8.353 + "crit": LOG_CRIT, 8.354 + "critical": LOG_CRIT, 8.355 + "debug": LOG_DEBUG, 8.356 + "emerg": LOG_EMERG, 8.357 + "err": LOG_ERR, 8.358 + "error": LOG_ERR, # DEPRECATED 8.359 + "info": LOG_INFO, 8.360 + "notice": LOG_NOTICE, 8.361 + "panic": LOG_EMERG, # DEPRECATED 8.362 + "warn": LOG_WARNING, # DEPRECATED 8.363 + "warning": LOG_WARNING, 8.364 + } 8.365 + 8.366 + facility_names = { 8.367 + "auth": LOG_AUTH, 8.368 + "authpriv": LOG_AUTHPRIV, 8.369 + "cron": LOG_CRON, 8.370 + "daemon": LOG_DAEMON, 8.371 + "kern": LOG_KERN, 8.372 + "lpr": LOG_LPR, 8.373 + "mail": LOG_MAIL, 8.374 + "news": LOG_NEWS, 8.375 + "security": LOG_AUTH, # DEPRECATED 8.376 + "syslog": LOG_SYSLOG, 8.377 + "user": LOG_USER, 8.378 + "uucp": LOG_UUCP, 8.379 + "local0": LOG_LOCAL0, 8.380 + "local1": LOG_LOCAL1, 8.381 + "local2": LOG_LOCAL2, 8.382 + "local3": LOG_LOCAL3, 8.383 + "local4": LOG_LOCAL4, 8.384 + "local5": LOG_LOCAL5, 8.385 + "local6": LOG_LOCAL6, 8.386 + "local7": LOG_LOCAL7, 8.387 + } 8.388 + 8.389 + def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER): 8.390 + """ 8.391 + Initialize a handler. 8.392 + 8.393 + If address is specified as a string, UNIX socket is used. 8.394 + If facility is not specified, LOG_USER is used. 8.395 + """ 8.396 + logging.Handler.__init__(self) 8.397 + 8.398 + self.address = address 8.399 + self.facility = facility 8.400 + if type(address) == types.StringType: 8.401 + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) 8.402 + # syslog may require either DGRAM or STREAM sockets 8.403 + try: 8.404 + self.socket.connect(address) 8.405 + except socket.error: 8.406 + self.socket.close() 8.407 + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 8.408 + self.socket.connect(address) 8.409 + self.unixsocket = 1 8.410 + else: 8.411 + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 8.412 + self.unixsocket = 0 8.413 + 8.414 + self.formatter = None 8.415 + 8.416 + # curious: when talking to the unix-domain '/dev/log' socket, a 8.417 + # zero-terminator seems to be required. this string is placed 8.418 + # into a class variable so that it can be overridden if 8.419 + # necessary. 8.420 + log_format_string = '<%d>%s\000' 8.421 + 8.422 + def encodePriority (self, facility, priority): 8.423 + """ 8.424 + Encode the facility and priority. You can pass in strings or 8.425 + integers - if strings are passed, the facility_names and 8.426 + priority_names mapping dictionaries are used to convert them to 8.427 + integers. 8.428 + """ 8.429 + if type(facility) == types.StringType: 8.430 + facility = self.facility_names[facility] 8.431 + if type(priority) == types.StringType: 8.432 + priority = self.priority_names[priority] 8.433 + return (facility << 3) | priority 8.434 + 8.435 + def close (self): 8.436 + """ 8.437 + Closes the socket. 8.438 + """ 8.439 + if self.unixsocket: 8.440 + self.socket.close() 8.441 + logging.Handler.close(self) 8.442 + 8.443 + def emit(self, record): 8.444 + """ 8.445 + Emit a record. 8.446 + 8.447 + The record is formatted, and then sent to the syslog server. If 8.448 + exception information is present, it is NOT sent to the server. 8.449 + """ 8.450 + msg = self.format(record) 8.451 + """ 8.452 + We need to convert record level to lowercase, maybe this will 8.453 + change in the future. 8.454 + """ 8.455 + msg = self.log_format_string % ( 8.456 + self.encodePriority(self.facility, 8.457 + string.lower(record.levelname)), 8.458 + msg) 8.459 + try: 8.460 + if self.unixsocket: 8.461 + self.socket.send(msg) 8.462 + else: 8.463 + self.socket.sendto(msg, self.address) 8.464 + except: 8.465 + self.handleError(record) 8.466 + 8.467 +class SMTPHandler(logging.Handler): 8.468 + """ 8.469 + A handler class which sends an SMTP email for each logging event. 8.470 + """ 8.471 + def __init__(self, mailhost, fromaddr, toaddrs, subject): 8.472 + """ 8.473 + Initialize the handler. 8.474 + 8.475 + Initialize the instance with the from and to addresses and subject 8.476 + line of the email. To specify a non-standard SMTP port, use the 8.477 + (host, port) tuple format for the mailhost argument. 8.478 + """ 8.479 + logging.Handler.__init__(self) 8.480 + if type(mailhost) == types.TupleType: 8.481 + host, port = mailhost 8.482 + self.mailhost = host 8.483 + self.mailport = port 8.484 + else: 8.485 + self.mailhost = mailhost 8.486 + self.mailport = None 8.487 + self.fromaddr = fromaddr 8.488 + if type(toaddrs) == types.StringType: 8.489 + toaddrs = [toaddrs] 8.490 + self.toaddrs = toaddrs 8.491 + self.subject = subject 8.492 + 8.493 + def getSubject(self, record): 8.494 + """ 8.495 + Determine the subject for the email. 8.496 + 8.497 + If you want to specify a subject line which is record-dependent, 8.498 + override this method. 8.499 + """ 8.500 + return self.subject 8.501 + 8.502 + weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 8.503 + 8.504 + monthname = [None, 8.505 + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 8.506 + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 8.507 + 8.508 + def date_time(self): 8.509 + """Return the current date and time formatted for a MIME header.""" 8.510 + year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time()) 8.511 + s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( 8.512 + self.weekdayname[wd], 8.513 + day, self.monthname[month], year, 8.514 + hh, mm, ss) 8.515 + return s 8.516 + 8.517 + def emit(self, record): 8.518 + """ 8.519 + Emit a record. 8.520 + 8.521 + Format the record and send it to the specified addressees. 8.522 + """ 8.523 + try: 8.524 + import smtplib 8.525 + port = self.mailport 8.526 + if not port: 8.527 + port = smtplib.SMTP_PORT 8.528 + smtp = smtplib.SMTP(self.mailhost, port) 8.529 + msg = self.format(record) 8.530 + msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % ( 8.531 + self.fromaddr, 8.532 + string.join(self.toaddrs, ","), 8.533 + self.getSubject(record), 8.534 + self.date_time(), msg) 8.535 + smtp.sendmail(self.fromaddr, self.toaddrs, msg) 8.536 + smtp.quit() 8.537 + except: 8.538 + self.handleError(record) 8.539 + 8.540 +class NTEventLogHandler(logging.Handler): 8.541 + """ 8.542 + A handler class which sends events to the NT Event Log. Adds a 8.543 + registry entry for the specified application name. If no dllname is 8.544 + provided, win32service.pyd (which contains some basic message 8.545 + placeholders) is used. Note that use of these placeholders will make 8.546 + your event logs big, as the entire message source is held in the log. 8.547 + If you want slimmer logs, you have to pass in the name of your own DLL 8.548 + which contains the message definitions you want to use in the event log. 8.549 + """ 8.550 + def __init__(self, appname, dllname=None, logtype="Application"): 8.551 + logging.Handler.__init__(self) 8.552 + try: 8.553 + import win32evtlogutil, win32evtlog 8.554 + self.appname = appname 8.555 + self._welu = win32evtlogutil 8.556 + if not dllname: 8.557 + dllname = os.path.split(self._welu.__file__) 8.558 + dllname = os.path.split(dllname[0]) 8.559 + dllname = os.path.join(dllname[0], r'win32service.pyd') 8.560 + self.dllname = dllname 8.561 + self.logtype = logtype 8.562 + self._welu.AddSourceToRegistry(appname, dllname, logtype) 8.563 + self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE 8.564 + self.typemap = { 8.565 + logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE, 8.566 + logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE, 8.567 + logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE, 8.568 + logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE, 8.569 + logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE, 8.570 + } 8.571 + except ImportError: 8.572 + print "The Python Win32 extensions for NT (service, event "\ 8.573 + "logging) appear not to be available." 8.574 + self._welu = None 8.575 + 8.576 + def getMessageID(self, record): 8.577 + """ 8.578 + Return the message ID for the event record. If you are using your 8.579 + own messages, you could do this by having the msg passed to the 8.580 + logger being an ID rather than a formatting string. Then, in here, 8.581 + you could use a dictionary lookup to get the message ID. This 8.582 + version returns 1, which is the base message ID in win32service.pyd. 8.583 + """ 8.584 + return 1 8.585 + 8.586 + def getEventCategory(self, record): 8.587 + """ 8.588 + Return the event category for the record. 8.589 + 8.590 + Override this if you want to specify your own categories. This version 8.591 + returns 0. 8.592 + """ 8.593 + return 0 8.594 + 8.595 + def getEventType(self, record): 8.596 + """ 8.597 + Return the event type for the record. 8.598 + 8.599 + Override this if you want to specify your own types. This version does 8.600 + a mapping using the handler's typemap attribute, which is set up in 8.601 + __init__() to a dictionary which contains mappings for DEBUG, INFO, 8.602 + WARNING, ERROR and CRITICAL. If you are using your own levels you will 8.603 + either need to override this method or place a suitable dictionary in 8.604 + the handler's typemap attribute. 8.605 + """ 8.606 + return self.typemap.get(record.levelno, self.deftype) 8.607 + 8.608 + def emit(self, record): 8.609 + """ 8.610 + Emit a record. 8.611 + 8.612 + Determine the message ID, event category and event type. Then 8.613 + log the message in the NT event log. 8.614 + """ 8.615 + if self._welu: 8.616 + try: 8.617 + id = self.getMessageID(record) 8.618 + cat = self.getEventCategory(record) 8.619 + type = self.getEventType(record) 8.620 + msg = self.format(record) 8.621 + self._welu.ReportEvent(self.appname, id, cat, type, [msg]) 8.622 + except: 8.623 + self.handleError(record) 8.624 + 8.625 + def close(self): 8.626 + """ 8.627 + Clean up this handler. 8.628 + 8.629 + You can remove the application name from the registry as a 8.630 + source of event log entries. However, if you do this, you will 8.631 + not be able to see the events as you intended in the Event Log 8.632 + Viewer - it needs to be able to access the registry to get the 8.633 + DLL name. 8.634 + """ 8.635 + #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype) 8.636 + logging.Handler.close(self) 8.637 + 8.638 +class HTTPHandler(logging.Handler): 8.639 + """ 8.640 + A class which sends records to a Web server, using either GET or 8.641 + POST semantics. 8.642 + """ 8.643 + def __init__(self, host, url, method="GET"): 8.644 + """ 8.645 + Initialize the instance with the host, the request URL, and the method 8.646 + ("GET" or "POST") 8.647 + """ 8.648 + logging.Handler.__init__(self) 8.649 + method = string.upper(method) 8.650 + if method not in ["GET", "POST"]: 8.651 + raise ValueError, "method must be GET or POST" 8.652 + self.host = host 8.653 + self.url = url 8.654 + self.method = method 8.655 + 8.656 + def mapLogRecord(self, record): 8.657 + """ 8.658 + Default implementation of mapping the log record into a dict 8.659 + that is sent as the CGI data. Overwrite in your class. 8.660 + Contributed by Franz Glasner. 8.661 + """ 8.662 + return record.__dict__ 8.663 + 8.664 + def emit(self, record): 8.665 + """ 8.666 + Emit a record. 8.667 + 8.668 + Send the record to the Web server as an URL-encoded dictionary 8.669 + """ 8.670 + try: 8.671 + import httplib, urllib 8.672 + h = httplib.HTTP(self.host) 8.673 + url = self.url 8.674 + data = urllib.urlencode(self.mapLogRecord(record)) 8.675 + if self.method == "GET": 8.676 + if (string.find(url, '?') >= 0): 8.677 + sep = '&' 8.678 + else: 8.679 + sep = '?' 8.680 + url = url + "%c%s" % (sep, data) 8.681 + h.putrequest(self.method, url) 8.682 + if self.method == "POST": 8.683 + h.putheader("Content-length", str(len(data))) 8.684 + h.endheaders() 8.685 + if self.method == "POST": 8.686 + h.send(data) 8.687 + h.getreply() #can't do anything with the result 8.688 + except: 8.689 + self.handleError(record) 8.690 + 8.691 +class BufferingHandler(logging.Handler): 8.692 + """ 8.693 + A handler class which buffers logging records in memory. Whenever each 8.694 + record is added to the buffer, a check is made to see if the buffer should 8.695 + be flushed. If it should, then flush() is expected to do what's needed. 8.696 + """ 8.697 + def __init__(self, capacity): 8.698 + """ 8.699 + Initialize the handler with the buffer size. 8.700 + """ 8.701 + logging.Handler.__init__(self) 8.702 + self.capacity = capacity 8.703 + self.buffer = [] 8.704 + 8.705 + def shouldFlush(self, record): 8.706 + """ 8.707 + Should the handler flush its buffer? 8.708 + 8.709 + Returns true if the buffer is up to capacity. This method can be 8.710 + overridden to implement custom flushing strategies. 8.711 + """ 8.712 + return (len(self.buffer) >= self.capacity) 8.713 + 8.714 + def emit(self, record): 8.715 + """ 8.716 + Emit a record. 8.717 + 8.718 + Append the record. If shouldFlush() tells us to, call flush() to process 8.719 + the buffer. 8.720 + """ 8.721 + self.buffer.append(record) 8.722 + if self.shouldFlush(record): 8.723 + self.flush() 8.724 + 8.725 + def flush(self): 8.726 + """ 8.727 + Override to implement custom flushing behaviour. 8.728 + 8.729 + This version just zaps the buffer to empty. 8.730 + """ 8.731 + self.buffer = [] 8.732 + 8.733 + def close(self): 8.734 + """ 8.735 + Close the handler. 8.736 + 8.737 + This version just flushes and chains to the parent class' close(). 8.738 + """ 8.739 + self.flush() 8.740 + logging.Handler.close(self) 8.741 + 8.742 +class MemoryHandler(BufferingHandler): 8.743 + """ 8.744 + A handler class which buffers logging records in memory, periodically 8.745 + flushing them to a target handler. Flushing occurs whenever the buffer 8.746 + is full, or when an event of a certain severity or greater is seen. 8.747 + """ 8.748 + def __init__(self, capacity, flushLevel=logging.ERROR, target=None): 8.749 + """ 8.750 + Initialize the handler with the buffer size, the level at which 8.751 + flushing should occur and an optional target. 8.752 + 8.753 + Note that without a target being set either here or via setTarget(), 8.754 + a MemoryHandler is no use to anyone! 8.755 + """ 8.756 + BufferingHandler.__init__(self, capacity) 8.757 + self.flushLevel = flushLevel 8.758 + self.target = target 8.759 + 8.760 + def shouldFlush(self, record): 8.761 + """ 8.762 + Check for buffer full or a record at the flushLevel or higher. 8.763 + """ 8.764 + return (len(self.buffer) >= self.capacity) or \ 8.765 + (record.levelno >= self.flushLevel) 8.766 + 8.767 + def setTarget(self, target): 8.768 + """ 8.769 + Set the target handler for this handler. 8.770 + """ 8.771 + self.target = target 8.772 + 8.773 + def flush(self): 8.774 + """ 8.775 + For a MemoryHandler, flushing means just sending the buffered 8.776 + records to the target, if there is one. Override if you want 8.777 + different behaviour. 8.778 + """ 8.779 + if self.target: 8.780 + for record in self.buffer: 8.781 + self.target.handle(record) 8.782 + self.buffer = [] 8.783 + 8.784 + def close(self): 8.785 + """ 8.786 + Flush, set the target to None and lose the buffer. 8.787 + """ 8.788 + self.flush() 8.789 + self.target = None 8.790 + BufferingHandler.close(self)
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 9.2 +++ b/tools/python/logging/logging-0.4.9.2/python_logging.html Thu Jul 22 13:42:39 2004 +0000 9.3 @@ -0,0 +1,1183 @@ 9.4 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" 9.5 +"http://www.w3.org/TR/REC-html40/loose.dtd"> 9.6 +<html> 9.7 + 9.8 +<head> 9.9 +<meta name="AUTHOR" content="Vinay Sajip"> 9.10 +<meta name="COPYRIGHT" content="© 2002 Red Dove Consultants Limited"> 9.11 +<meta name="KEYWORDS" content="Red Dove Consultants, Python, logging, PEP 282"> 9.12 +<meta name="DESCRIPTION" content="A logging system for Python"> 9.13 +<meta name="summary" content="A logging system for Python"> 9.14 +<meta name="publisher" content="Red Dove Consultants Limited"> 9.15 +<meta name="identifier" content="http://www.red-dove.com"> 9.16 +<title>A Logging System for Python</title> 9.17 +<link title="Default Style" rel="stylesheet" href="default.css" type="text/css"> 9.18 +</head> 9.19 + 9.20 +<body style="margin: 5px" marginheight="0"> 9.21 +<table border="0" width="100%" cellspacing="0" cellpadding="0"> 9.22 + <tr> 9.23 + <td class="bigtd">A Logging System for Python</td> 9.24 + <td rowspan="2" align="right" style="text-align: right; vertical-align: top"><a href="/index.html">Home</a><br> 9.25 + <a href="#download">Download</a><br> 9.26 + <a href="#license">Copyright & License</a><br> 9.27 + <a href="#changes">Recent Changes</a><br> 9.28 + <!-- a href="logging_manual.html">Online Manual</a --></td> 9.29 + </tr> 9.30 + <tr> 9.31 + <td>"Oh, I'm a lumberjack and I'm okay..." <small>(Monty Python, <a href="http://www.montypython.net/scripts/lumberj.php">The Lumberjack Song</a>)</small></td> 9.32 + </tr> 9.33 +</table> 9.34 + 9.35 +<h4>Table of Contents</h4> 9.36 +<a href="#abstract">Abstract</a><br> 9.37 +<a href="#motivation">Motivation</a><br> 9.38 +<a href="#influences">Influences</a><br> 9.39 +<a href="#simplest">A Simple Example</a><br> 9.40 +<a href="#ctrlflow">Control Flow</a><br> 9.41 +<a href="#levels">Levels</a><br> 9.42 +<a href="#loggers">Loggers</a><br> 9.43 +<a href="#handlers">Handlers</a><br> 9.44 +<a href="#formatters">Formatters</a><br> 9.45 +<a href="#filters">Filters</a><br> 9.46 +<a href="#config">Configuration</a><br> 9.47 +<a href="#guiconf">The GUI Configurator</a><br> 9.48 +<a href="#scenarios">Case Scenarios</a><br> 9.49 +<a href="#threadsafe">Thread Safety</a><br> 9.50 +<a href="#onthefly">On-The-Fly Reconfiguration</a><br> 9.51 +<a href="#mlcf">Module-Level Convenience Functions</a><br> 9.52 +<a href="#perf">Performance</a><br> 9.53 +<a href="#impstatus">Implementation Status</a><br> 9.54 +<a href="#acks">Acknowledgements</a><br> 9.55 +<a href="#todo">Still To Do</a><br> 9.56 +<a href="#download">Download and Installation</a><br> 9.57 +<a href="#changes">Change History</a><br> 9.58 +<a href="#license">Copyright and License</a><br> 9.59 + 9.60 +<a name="abstract"></a><h4>Abstract</h4> 9.61 + 9.62 +<p>There is a need for a standard logging system in Python, as comprehensively documented 9.63 +in <a href="http://www.python.org/peps/pep-0282.html">PEP 282</a> and 9.64 +enthusiastically endorsed by the BDFL in the <a 9.65 +href="http://www.python.org/doc/essays/pepparade.html">Parade of the PEPs</a>. By a happy 9.66 +coincidence, the package described here was already in development and fairly close in 9.67 +intent and design to the description in the aforementioned PEP, borrowing as it did 9.68 +heavily from JSR-47 (now JDK 1.4's java.util.logging package) and <a 9.69 +href="http://jakarta.apache.org/log4j/">log4j</a>. This page describes it in more detail. 9.70 +As I have tweaked the package to meet comments on PEP 282, I have structured this page in 9.71 +the same way as the original PEP. </p> 9.72 + 9.73 +<a name="motivation"></a><h4>Motivation</h4> 9.74 + 9.75 +<p>The Python community has been incredibly helpful to me, a relative newcomer to the 9.76 +language. Python and its community has certainly saved me much time and effort, and it 9.77 +seems appropriate to give something back to the community by offering up this package for 9.78 +people to try. Any <a href="mailto:vinay_sajip@red-dove.com">feedback</a> will be gratefully accepted. </p> 9.79 + 9.80 +<a name="influences"></a><h4>Influences</h4> 9.81 + 9.82 +<p>This package owes its greatest debt to Apache <a 9.83 +href="http://jakarta.apache.org/log4j/">log4j</a>. Due notice was also taken of log4j's 9.84 +comprehensive <a href="http://jakarta.apache.org/log4j/docs/critique.html">critique</a> of 9.85 +JSR47. This package bears a close resemblance to log4j, but is not a close translation 9.86 +(as, for example, <a href="http://log4p.sourceforge.net/">log4p</a> appears to be). I have 9.87 +attempted to be more minimalist (and hopefully more Pythonic) in my approach. You be the 9.88 +judge! </p> 9.89 + 9.90 +<a name="simplest"></a><h4>A Simple Example</h4> 9.91 + 9.92 +<p>Using the package doesn't get much simpler. It is packaged as a standard Python package called (unsurprisingly) <code>logging</code>. You just need to <code>import logging</code> and you're ready to go. Minimal example: </p> 9.93 + 9.94 +<pre class="program"> 9.95 +# --- app.py -------------------------------------------------------------------- 9.96 +import logging 9.97 + 9.98 +logging.warn("Hello") 9.99 +logging.error("Still here...") 9.100 +logging.warn("Goodbye") 9.101 +</pre> 9.102 + 9.103 +<p>When you run <code>app.py</code>, the results are: </p> 9.104 + 9.105 +<pre class="output"> 9.106 +WARN:root:Hello 9.107 +ERROR:root:Still here... 9.108 +WARN:root:Goodbye 9.109 +</pre> 9.110 + 9.111 +<p>Don't worry about the format of the output - it's all configurable. Here's a slightly 9.112 +more involved example; if you've just looked at PEP 282 you will probably get a feeling of 9.113 +dejà vu. (This is intentional.)</p> 9.114 + 9.115 +<a name="nextsimplest"></a><pre class="program"> 9.116 +# --- mymodule.py -------------------------------------------------------------------- 9.117 +import logging 9.118 +log = logging.getLogger("MyModule") 9.119 + 9.120 +def doIt(): 9.121 + log.debug("doin' stuff") 9.122 + <span class="comment">#do stuff...but suppose an error occurs?</span> 9.123 + raise TypeError, "bogus type error for testing" 9.124 + 9.125 +# --- myapp.py ----------------------------------------------------------------------- 9.126 +import logging, mymodule 9.127 + 9.128 +logging.basicConfig() 9.129 + 9.130 +log = logging.getLogger("MyApp") 9.131 +log.setLevel(logging.DEBUG) <span class="comment">#set verbosity to show all messages of severity >= DEBUG</span> 9.132 +log.info("Starting my app") 9.133 +try: 9.134 + mymodule.doIt() 9.135 +except Exception, e: 9.136 + log.exception("There was a problem.") 9.137 +log.info("Ending my app") 9.138 +</pre> 9.139 + 9.140 +<p>When you run <code>myapp.py</code>, the results are: </p> 9.141 + 9.142 +<pre class="output"> 9.143 +INFO:MyApp:Starting my app 9.144 +ERROR:MyApp:There was a problem. 9.145 +Traceback (most recent call last): 9.146 + File "myapp.py", line 9, in ? 9.147 + mymodule.doIt() 9.148 + File "mymodule.py", line 7, in doIt 9.149 + raise TypeError, "Bogus type error for testing" 9.150 +TypeError: Bogus type error for testing 9.151 +INFO:MyApp:Ending my app 9.152 +</pre> 9.153 + 9.154 +<p>But don't worry - the above output is not hardcoded into the package. It's just an 9.155 +example of what you can do with very little work. As you can see, exceptions are handled 9.156 +as one would expect. </p> 9.157 + 9.158 +<a name="ctrlflow"></a><h4>Control Flow</h4> 9.159 + 9.160 +<p>The package pretty much matches the PEP regarding control flow. The user of the package 9.161 +makes logging calls on instances of <code>Logger</code>, which are organized into a 9.162 +hierarchy based on a "dotted name" namespace. This hierarchy is embodied in an 9.163 +encapsulated singleton <code>Manager</code> instance (which can be ignored by users of the 9.164 +package, for most purposes). Based on the type of logging call and the logging 9.165 +configuration (see below), the call may be passed through a set of <code>Filter</code> 9.166 +instances to decide whether it should be dropped. If not, then the logger consults a set 9.167 +of <code>Handler</code> instances which are associated with it, and asks each handler 9.168 +instance to "handle" the logging event. By default, the system moves up the 9.169 +namespace hierarchy and invokes handlers on all loggers at or above the level of the 9.170 +logger on which the logging call was made. (You can override this by setting a logger's 9.171 +"propagate" attribute to 0 - no traversal up the hierarchy is made from such a 9.172 +logger. But I'm getting ahead of myself...) </p> 9.173 + 9.174 +<p>Handlers are passed <code>LogRecord</code> instances which (should) contain all the 9.175 +information we're interested in logging. Handlers, too, can invoke filters to determine 9.176 +whether a record should be dropped. If not, then the handler takes a handler-specific 9.177 +action to actually log the record to a file, the console or whatever.</p> 9.178 + 9.179 +<a name="levels"></a><h4>Levels</h4> 9.180 + 9.181 +<p>The following levels are implemented by default: </p> 9.182 + 9.183 +<pre> 9.184 +DEBUG 9.185 +INFO 9.186 +WARN 9.187 +ERROR 9.188 +CRITICAL 9.189 +</pre> 9.190 + 9.191 +<p>The <code>CRITICAL</code> level replaces the earlier <code>FATAL</code> level. You can use either (for now), but <code>CRITICAL</code> is preferred since <code>FATAL</code> implies that the application is about to terminate. This is not true for many systems which use logging - for example, a Web server application which encounters a <code>CRITICAL</code> condition (e.g. running out of resources) will still try to keep going as best it can.</p> 9.192 +<p><code>FATAL</code> (and the corresponding <code>fatal()</code> methods) may be removed in future versions of the package. Currently, <code>CRITICAL</code> is synonymous with <code>FATAL</code> and <code>critical()</code> methods are synonymous with <code>fatal()</code>.</p> 9.193 +<p>Exceptions logged via <code>exception()</code> use the <code>ERROR</code> level for logging. If it is desired to log exception information with arbitrary logging levels, this can be done by passing a keyword argument <code>exc_info</code> with a true value to the logging methods (see the pydoc for more details).</p> 9.194 +<p>The levels are not deeply hardcoded into the package - the number of levels, their numeric values and their textual representation are all configurable. The above levels represent the experience of the log4j community and so are provided as the default levels for users who do not have very specific requirements in this area.</p> 9.195 +<p>The example script <code>log_test4.py</code> shows the use of bespoke logging levels (as well as filtering by level at logger and handler, as well as use of filter classes).</p> 9.196 + 9.197 +<a name="loggers"></a><h4>Loggers</h4> 9.198 + 9.199 +<p>The package implements loggers pretty much as mentioned in the PEP, except that the manager class is called <code>Manager</code> rather than <code>LogManager</code>.</p> 9.200 +<p>Each Logger instance represents "an area" of the application. This somewhat nebulous definition is needed because it's entirely up to each application developer to define an application's "areas".</p><p>For example, an application which reads and processes spreadsheet-type data in different formats might have an overall area "input", concerned with reading input files; and areas "input.csv", "input.xls" and "input.gnu", related to processing comma-separated-value, Excel and Gnumeric input files. Logging messages relating to the overall input function (e.g. deciding which files to process) might be logged used the logger named "input"; logging messages relating to reading individual files might be sent to any of "input.csv", "input.xls" or "input.gnu" depending on the type of file being read.</p><p>The advantage of the hierarchical structure is that logging verbosity may be controlled either at the high level or the low level. The levels are loosely coupled and new levels can easily be added at a later date, e.g."input.wks" for reading Lotus-123 format files. It's also possible to do things like routing messages relating to Excel file input to whoever is working on Excel imports, messages related to Gnumeric file processing to a different developer, and so on. Even if the same person works on both, they can at different times focus logging verbosity on particular areas of interest - for example, when debugging Excel imports, they can set the "input.xls" logger's verbosity to DEBUG and others to CRITICAL, and when moving to debug Gnumeric imports, they can reduce the "input.xls" verbosity by setting the level to CRITICAL, while increasing "input.gnu"'s verbosity by setting the level to DEBUG.</p> 9.201 + 9.202 +<a name="handlers"></a><h4>Handlers</h4> 9.203 + 9.204 +<p>The following handlers are implemented. I guess they could use more testing ;-) 9.205 + 9.206 +<ul> 9.207 + <li>StreamHandler - logging to a stream, defaulting to sys.stderr.</li> 9.208 + <li>FileHandler - logging to disk files.</li> 9.209 + <li>RotatingFileHandler - logging to disk files with support for rollover, rotating files.</li> 9.210 + <li>SocketHandler - logging to a streaming socket.</li> 9.211 + <li>DatagramHandler - logging to a UDP socket.</li> 9.212 + <li>SMTPHandler - logging to an email address.</li> 9.213 + <li>SysLogHandler - logging to Unix syslog. Contributed by Nicolas Untz, based on <a href="http://www.nightmare.com/squirl/python-ext/misc/syslog.py 9.214 +">Sam Rushing's syslog module</a>.</li> 9.215 + <li>MemoryHandler - buffering records in memory until a specific trigger occurs (or until the buffer gets full).</li> 9.216 + <li>NTEventLogHandler - writes events to the NT event log. For this to work, you need to have Mark Hammond's Win32 extensions installed. (Though of course you can still log to NT from other platforms - just use SocketHandler to redirect to an NT machine).</li> 9.217 + <li>HTTPHandler - sends events to a Web server using either GET or POST semantics.</li> 9.218 +</ul> 9.219 +<p>All of these except the first two are defined in a sub-module, handlers.py. (To use these handlers, you'll need to <code>import logging.handlers</code>. In addition to the above list, there are example implementations of <code>XMLHandler</code> (see <code>log_test9.py</code>), <code>BufferingSMTPHandler</code> (see <code>log_test11.py</code>) and <code>DBHandler</code> (see <code>log_test14.py</code>) in the test harnesses, on which you can base more specific classes. There is also a class called <code>SLHandler</code> (see <code>log_test1.py</code>) which implements an alternative SysLogHandler - one which uses the syslog module in the standard library (and which is therefore only available on Unix).</p> 9.220 +<p>SOAPHandler, which sends events to a SOAP server, has moved (as of release 0.4.4) from the core to an example script (log_test13.py). The SOAP message is packaged as a function call to a single <code>log()</code> function on the remote server, which takes each relevant member of the LogRecord as a positional parameter. This is perhaps not ideal - but then this SOAPHandler is just a proof-of-concept example to get you started ;-)</p> 9.221 +<p>Note that the handlers are specifically intended <I>not</I> to raise exceptions when errors occur at runtime. This is to avoid error messages from the logging infrastructure polluting logging messages from the application being logged. If, for example, a SocketHandler sees a connection reset by the remote endpoint, it will silently drop all records passed to it (but it will try to connect each time). It may be that due to bugs there are some exceptions incorrectly raised by the logging system, I will try to rectify this kind of problem as soon as it is found and reported!</p> 9.222 + 9.223 +<a name="formatters"></a><h4>Formatters</h4> 9.224 + 9.225 +<p>A basic Formatter has been implemented, which should cater for most immediate 9.226 +requirements. You basically initialize the Formatter with a format string which knows how the attribute dictionary of a LogRecord looks. For example, the output in the example above was produced 9.227 +with a format string of <code>"%(asctime)s %(name)-19s %(levelname)-5s - 9.228 +%(message)s"</code>. Note that the "message" attribute of the <code>LogRecord</code> 9.229 +is derived from <code>"msg % args"</code> where <code>msg</code> and <code>args</code> 9.230 +are passed by the the user in a logging call.</p> 9.231 + 9.232 +<a name="filters"></a><h4>Filters</h4> 9.233 + 9.234 +<p>Filters are used to refine logging output at either logger or handler level with a finer control than is available by just using logging levels. The basic Filter class takes an optional name argument and passes all logging records from loggers which are at or below the specified name.</p> 9.235 +<p>For example, a <code>Filter</code> initialized with "A.B" will allow events logged by loggers "A.B", "A.B.C", "A.B.C.D", "A.B.D" but not "A.BB", "B.A.B". If no name is specified, all events are passed by the filter.</p> 9.236 + 9.237 +<a name="config"></a><h4>Configuration</h4> 9.238 + 9.239 +<p>A basic configuration is provided via a module-level function, <code>basicConfig()</code>. 9.240 +If you want to use very simple logging, you can just call the <a href="#mlcf">module-level 9.241 +convenience functions</a> and they will call <code>basicConfig()</code> for you if 9.242 +necessary. It (basically) adds a <code>StreamHandler</code> (which writes to <code>sys.stderr</code>)to the root <code>Logger</code>.</p> 9.243 + 9.244 +<p>There are numerous examples of configuration in the test/example scripts included in the distribution. For example, <code>log_test8.py</code> has an example of using a file-based logger.</p> 9.245 +<p>An alternative using ConfigParser-based configuration files is also available (the older, dict-based function is no more). To use this functionality, you'll need to <code>import logging.config</code>. Here is an example of such a config file - it's a bit long, but a full working example so bear with me. I've annotated it as best I can :-): </p> 9.246 +<p> 9.247 +In the listing below, some values are used by both the logging configuration API 9.248 +and the GUI configurator, while others are used only by the GUI configurator. To 9.249 +make it clearer which values you absolutely need to have in the .ini file for it to be useful even if you hand-code it, the values used by the configuration API are shown 9.250 +<span class="program"><span class="strong">like this</span></span>. (The other ones are used by the GUI configurator, but ignored by the configuration API.) 9.251 +</p> 9.252 +<pre class="program"> 9.253 +# --- logconf.ini ----------------------------------------------------------- 9.254 +#The "loggers" section contains the key names for all the loggers in this 9.255 +#configuration. These are not the actual channel names, but values used to 9.256 +#identify where the parameters for each logger are found in this file. 9.257 +#The section for an individual logger is named "logger_xxx" where the "key" 9.258 +#for a logger is "xxx". So ... "logger_root", "logger_log02", etc. further 9.259 +#down the file, indicate how the root logger is set up, logger "log_02" is set 9.260 +#up, and so on. 9.261 +#Logger key names can be any identifier, except "root" which is reserved for 9.262 +#the root logger. (The names "lognn" are generated by the GUI configurator.) 9.263 + 9.264 +<span class="strong">[loggers] 9.265 +keys=root,log02,log03,log04,log05,log06,log07</span> 9.266 + 9.267 +#The "handlers" section contains the key names for all the handlers in this 9.268 +#configuration. Just as for loggers above, the key names are values used to 9.269 +#identify where the parameters for each handler are found in this file. 9.270 +#The section for an individual handler is named "handler_xxx" where the "key" 9.271 +#for a handler is "xxx". So sections "handler_hand01", "handler_hand02", etc. 9.272 +#further down the file, indicate how the handlers "hand01", "hand02" etc. 9.273 +#are set up. 9.274 +#Handler key names can be any identifier. (The names "handnn" are generated 9.275 +#by the GUI configurator.) 9.276 + 9.277 +<span class="strong">[handlers] 9.278 +keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09</span> 9.279 + 9.280 +#The "formatters" section contains the key names for all the formatters in 9.281 +#this configuration. Just as for loggers and handlers above, the key names 9.282 +#are values used to identify where the parameters for each formatter are found 9.283 +#in this file. 9.284 +#The section for an individual formatter is named "formatter_xxx" where the 9.285 +#"key" for a formatter is "xxx". So sections "formatter_form01", 9.286 +#"formatter_form02", etc. further down the file indicate how the formatters 9.287 +#"form01", "form02" etc. are set up. 9.288 +#Formatter key names can be any identifier. (The names "formnn" are generated 9.289 +#by the GUI configurator.) 9.290 + 9.291 +<span class="strong">[formatters] 9.292 +keys=form01,form02,form03,form04,form05,form06,form07,form08,form09</span> 9.293 + 9.294 +#The section below indicates the information relating to the root logger. 9.295 +# 9.296 +#The level value needs to be one of DEBUG, INFO, WARN, ERROR, CRITICAL or NOTSET. 9.297 +#In the root logger, NOTSET indicates that all messages will be logged. 9.298 +#Level values are eval()'d in the context of the logging package's namespace. 9.299 +# 9.300 +#The propagate value indicates whether or not parents of this loggers will 9.301 +#be traversed when looking for handlers. It doesn't really make sense in the 9.302 +#root logger - it's just there because a root logger is almost like any other 9.303 +#logger. 9.304 +# 9.305 +#The channel value indicates the lowest portion of the channel name of the 9.306 +#logger. For a logger called "a.b.c", this value would be "c". 9.307 +# 9.308 +#The parent value indicates the key name of the parent logger, except that 9.309 +#root is shown as "(root)" rather than "root". 9.310 +# 9.311 +#The qualname value is the fully qualified channel name of the logger. For a 9.312 +#logger called "a.b.c", this value would be "a.b.c". 9.313 +# 9.314 +#The handlers value is a comma-separated list of the key names of the handlers 9.315 +#attached to this logger. 9.316 +# 9.317 +<span class="strong">[logger_root] 9.318 +level=NOTSET 9.319 +handlers=hand01</span> 9.320 +qualname=(root) <span class="comment"># note - this is used in non-root loggers</span> 9.321 +propagate=1 <span class="comment"># note - this is used in non-root loggers</span> 9.322 +channel= 9.323 +parent= 9.324 + 9.325 +# 9.326 +#The explanation for the values in this section is analogous to the above. The 9.327 +#logger is named "log02" and coincidentally has a key name of "log02". It has 9.328 +#a level of DEBUG and handler with key name "hand02". (See section 9.329 +#"handler_hand02" for handler details.) If the level value were NOTSET, this tells 9.330 +#the logging package to consult the parent (as long as propagate is 1) for the 9.331 +#effective level of this logger. If propagate is 0, this level is treated as for 9.332 +#the root logger - a value of NOTSET means "pass everything", and other values are 9.333 +#interpreted at face value. 9.334 +# 9.335 +<span class="strong">[logger_log02] 9.336 +level=DEBUG 9.337 +propagate=1 9.338 +qualname=log02 9.339 +handlers=hand02</span> 9.340 +channel=log02 9.341 +parent=(root) 9.342 + 9.343 +# 9.344 +#The explanation for the values in this section is analogous to the above. The 9.345 +#logger is named "log02.log03" and has a key name of "log03". 9.346 +#It has a level of INFO and handler with key name "hand03". 9.347 +# 9.348 +<span class="strong">[logger_log03] 9.349 +level=INFO 9.350 +propagate=1 9.351 +qualname=log02.log03 9.352 +handlers=hand03</span> 9.353 +channel=log03 9.354 +parent=log02 9.355 + 9.356 +# 9.357 +#The explanations for the values in this section and subsequent logger sections 9.358 +#are analogous to the above. 9.359 +# 9.360 +<span class="strong">[logger_log04] 9.361 +level=WARN 9.362 +propagate=0 9.363 +qualname=log02.log03.log04 9.364 +handlers=hand04</span> 9.365 +channel=log04 9.366 +parent=log03 9.367 + 9.368 +<span class="strong">[logger_log05] 9.369 +level=ERROR 9.370 +propagate=1 9.371 +qualname=log02.log03.log04.log05 9.372 +handlers=hand05</span> 9.373 +channel=log05 9.374 +parent=log04 9.375 + 9.376 +<span class="strong">[logger_log06] 9.377 +level=CRITICAL 9.378 +propagate=1 9.379 +qualname=log02.log03.log04.log05.log06 9.380 +handlers=hand06</span> 9.381 +channel=log06 9.382 +parent=log05 9.383 + 9.384 +<span class="strong">[logger_log07] 9.385 +level=WARN 9.386 +propagate=1 9.387 +qualname=log02.log03.log04.log05.log06.log07 9.388 +handlers=hand07</span> 9.389 +channel=log07 9.390 +parent=log06 9.391 + 9.392 +#The section below indicates the information relating to handler "hand01". 9.393 +#The first three keys (class, level and formatter) are common to all handlers. 9.394 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.395 +#is the list of arguments to the constructor for the handler class. 9.396 +# 9.397 +#The class value indicates the handler's class (as determined by eval() in 9.398 +#the logging package's namespace). 9.399 +# 9.400 +#The level value needs to be one of DEBUG, INFO, WARN, ERROR, CRITICAL or NOTSET. 9.401 +#NOTSET means "use the parent's level". 9.402 +# 9.403 +#The formatter value indicates the key name of the formatter for this handler. 9.404 +#If blank, a default formatter (logging._defaultFormatter) is used. 9.405 +# 9.406 +#The stream value indicates the stream for this StreamHandler. It is computed 9.407 +#by doing eval() on the string value in the context of the logging package's 9.408 +#namespace. 9.409 +# 9.410 +#The args value is a tuple of arguments which is passed to the constructor for 9.411 +#this handler's class in addition to the "self" argument. 9.412 +# 9.413 +<span class="strong">[handler_hand01] 9.414 +class=StreamHandler 9.415 +level=NOTSET 9.416 +formatter=form01 9.417 +args=(sys.stdout,)</span> 9.418 +stream=sys.stdout 9.419 + 9.420 +#The section below indicates the information relating to handler "hand02". 9.421 +#The first three keys are common to all handlers. 9.422 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.423 +#is the list of arguments to the constructor for the handler class. 9.424 +# 9.425 +#The filename value is the name of the file to write logging information to. 9.426 +#The mode value is the mode used to open() the file. The maxsize and backcount 9.427 +#values control rollover as described in the package's pydoc. 9.428 +# 9.429 +<span class="strong">[handler_hand02] 9.430 +class=FileHandler 9.431 +level=DEBUG 9.432 +formatter=form02 9.433 +args=('python.log', 'w')</span> 9.434 +filename=python.log 9.435 +mode=w 9.436 + 9.437 +#The section below indicates the information relating to handler "hand03". 9.438 +#The first three keys are common to all handlers. 9.439 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.440 +#is the list of arguments to the constructor for the handler class. 9.441 +# 9.442 +#The host value is the name of the host to send logging information to. 9.443 +#The port value is the port number to use for the socket connection. 9.444 +# 9.445 +<span class="strong">[handler_hand03] 9.446 +class=handlers.SocketHandler 9.447 +level=INFO 9.448 +formatter=form03 9.449 +args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT)</span> 9.450 +host=localhost 9.451 +port=DEFAULT_TCP_LOGGING_PORT 9.452 + 9.453 +#The section below indicates the information relating to handler "hand04". 9.454 +#The first three keys are common to all handlers. 9.455 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.456 +#is the list of arguments to the constructor for the handler class. 9.457 +# 9.458 +#The host value is the name of the host to send logging information to. 9.459 +#The port value is the port number to use for the socket connection. 9.460 +# 9.461 +<span class="strong">[handler_hand04] 9.462 +class=handlers.DatagramHandler 9.463 +level=WARN 9.464 +formatter=form04 9.465 +args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT)</span> 9.466 +host=localhost 9.467 +port=DEFAULT_UDP_LOGGING_PORT 9.468 + 9.469 +#The section below indicates the information relating to handler "hand05". 9.470 +#The first three keys are common to all handlers. 9.471 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.472 +#is the list of arguments to the constructor for the handler class. 9.473 +# 9.474 +#The host value is the name of the host to send logging information to. 9.475 +#The port value is the port number to use for the socket connection. 9.476 +#The facility is the syslog facility to use for logging. 9.477 +# 9.478 +<span class="strong">[handler_hand05] 9.479 +class=handlers.SysLogHandler 9.480 +level=ERROR 9.481 +formatter=form05 9.482 +args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER)</span> 9.483 +host=localhost 9.484 +port=SYSLOG_UDP_PORT 9.485 +facility=LOG_USER 9.486 + 9.487 +#The section below indicates the information relating to handler "hand06". 9.488 +#The first three keys are common to all handlers. 9.489 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.490 +#is the list of arguments to the constructor for the handler class. 9.491 +# 9.492 +#The appname value is the name of the application which appears in the 9.493 +#NT event log. 9.494 +#The dllname value is the pathname of a DLL to use for message definitions. 9.495 +#The logtype is the type of NT event log to write to - Application, Security 9.496 +#or System. 9.497 +# 9.498 +<span class="strong">[handler_hand06] 9.499 +class=NTEventLogHandler 9.500 +level=CRITICAL 9.501 +formatter=form06 9.502 +args=('Python Application', '', 'Application')</span> 9.503 +appname=Python Application 9.504 +dllname= 9.505 +logtype=Application 9.506 + 9.507 +#The section below indicates the information relating to handler "hand07". 9.508 +#The first three keys are common to all handlers. 9.509 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.510 +#is the list of arguments to the constructor for the handler class. 9.511 +# 9.512 +#The host value is the name of the SMTP server to connect to. 9.513 +#The port value is the port number to use for the SMTP connection. 9.514 +#The from value is the "From" value in emails. 9.515 +#The to value is a comma-separated list of email addresses. 9.516 +#The subject value is the subject of the email. 9.517 +# 9.518 +<span class="strong">[handler_hand07] 9.519 +class=SMTPHandler 9.520 +level=WARN 9.521 +formatter=form07 9.522 +args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject')</span> 9.523 +host=localhost 9.524 +port=25 9.525 +from=from@abc 9.526 +to=user1@abc,user2@xyz 9.527 +subject=Logger Subject 9.528 + 9.529 +#The section below indicates the information relating to handler "hand08". 9.530 +#The first three keys are common to all handlers. 9.531 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.532 +#is the list of arguments to the constructor for the handler class. 9.533 +# 9.534 +#The capacity value is the size of this handler's buffer. 9.535 +#The flushlevel value is the logging level at which the buffer is flushed. 9.536 +#The from value is the "From" value in emails. 9.537 +#The target value is the key name of the handler which messages are flushed 9.538 +#to (i.e. sent to when flushing). 9.539 +# 9.540 +<span class="strong">[handler_hand08] 9.541 +class=MemoryHandler 9.542 +level=NOTSET 9.543 +formatter=form08 9.544 +target= 9.545 +args=(10, ERROR)</span> 9.546 +capacity=10 9.547 +flushlevel=ERROR 9.548 + 9.549 +#The section below indicates the information relating to handler "hand09". 9.550 +#The first three keys are common to all handlers. 9.551 +#Any other values are handler-specific, except that "args", when eval()'ed, 9.552 +#is the list of arguments to the constructor for the handler class. 9.553 +# 9.554 +#The host value is the name of the HTTP server to connect to. 9.555 +#The port value is the port number to use for the HTTP connection. 9.556 +#The url value is the url to request from the server. 9.557 +#The method value is the HTTP request type (GET or POST). 9.558 +# 9.559 +<span class="strong">[handler_hand09] 9.560 +class=HTTPHandler 9.561 +level=NOTSET 9.562 +formatter=form09 9.563 +args=('localhost:9022', '/log', 'GET')</span> 9.564 +host=localhost 9.565 +port=9022 9.566 +url=/log 9.567 +method=GET 9.568 + 9.569 +#The sections below indicate the information relating to the various 9.570 +#formatters. The format value is the overall format string, and the 9.571 +#datefmt value is the strftime-compatible date/time format string. If 9.572 +#empty, the logging package substitutes ISO8601 format date/times where 9.573 +#needed. See the package pydoc for more details of the format string 9.574 +#structure. 9.575 +# 9.576 +<span class="strong">[formatter_form01] 9.577 +format=F1 %(asctime)s %(levelname)s %(message)s 9.578 +datefmt= 9.579 + 9.580 +[formatter_form02] 9.581 +format=F2 %(asctime)s %(pathname)s(%(lineno)d): %(levelname)s %(message)s 9.582 +datefmt= 9.583 + 9.584 +[formatter_form03] 9.585 +format=F3 %(asctime)s %(levelname)s %(message)s 9.586 +datefmt= 9.587 + 9.588 +[formatter_form04] 9.589 +format=%(asctime)s %(levelname)s %(message)s 9.590 +datefmt= 9.591 + 9.592 +[formatter_form05] 9.593 +format=F5 %(asctime)s %(levelname)s %(message)s 9.594 +datefmt= 9.595 + 9.596 +[formatter_form06] 9.597 +format=F6 %(asctime)s %(levelname)s %(message)s 9.598 +datefmt= 9.599 + 9.600 +[formatter_form07] 9.601 +format=F7 %(asctime)s %(levelname)s %(message)s 9.602 +datefmt= 9.603 + 9.604 +[formatter_form08] 9.605 +format=F8 %(asctime)s %(levelname)s %(message)s 9.606 +datefmt= 9.607 + 9.608 +[formatter_form09] 9.609 +format=F9 %(asctime)s %(levelname)s %(message)s 9.610 +datefmt=</span> 9.611 + 9.612 +# --- end of logconf.ini ---------------------------------------------------- 9.613 +</pre> 9.614 + 9.615 +<p>To use a file like this, you would call <code>logging.config.fileConfig("logconf.ini")</code> 9.616 +whereupon the file is read in and processed. Note that evaluation happens in the context of the logging package (hence the unqualified class names). 9.617 + 9.618 + 9.619 +<a name="guiconf"></a><h4>The GUI Configurator</h4> 9.620 + 9.621 +To create a file like the above, you can use the new GUI configurator, <code>logconf.py</code>, which is invoked either with no arguments or with a single argument giving the name of a configuration file to read. (Or, if you're a masochist/don't have Tk, you can do it by hand. The configurator is a quick hack, which I hope is reasonably intuitive - have a play with it and see what you think. I've used it with 1.5.2 and 2.1.2 on Windows and 1.5.2 on Linux/x86. There's no validation, rudimentary error checking and the usability could be better, but it's something to build on, hey?)</p> 9.622 + 9.623 +<p>Here's a screenshot of the configurator:</p> 9.624 +<img src="logconf.png" border="0"/> 9.625 +<p>Here's a quick guide on how to use it: 9.626 +<ul> 9.627 +<li>The screen is laid out in three panels - for loggers, handlers and formatters (see the yellow rectangles in the screenshot).</li> 9.628 +<li>Each panel consists of a listbox, New and Delete buttons and a "property editor" to set properties for each of the objects.</li> 9.629 +<li>To create loggers, you need to first select the parent logger before clicking the New button. To create handlers and formatters, just click the appropriate New button.</li> 9.630 +<li>To delete an item, just select it in the listbox and click the Delete button.</li> 9.631 +<li>Whenever an item is selected in the listbox, its properties are displayed in the property editor section.</li> 9.632 +<li>Whenever an item is deleted, the property editor section is cleared.</li> 9.633 +<li>To edit a property, just click on the name or value. If the property is read-only, nothing happens. If it's a user-editable value, an entry field appears and you can type into it. If it's a user-selectable value (I mean selectable from a list), a button with ... appears. Clicking on it causes a pseudo-combobox to appear. All such boxes are single-selection, except for the "Handlers" property of loggers, for which several handlers can be selected.</li> 9.634 +<li>To commit the changes, just click elsewhere in the property editor section, e.g. below the last property or on some other property.</li> 9.635 +<li>Formatters are referred to by handlers, and handlers are referred to by loggers. Hence formatters and handlers have names generated for them automatically, for use in cross-referencing. The names of these cannot be changed. The name for loggers is, however, editable (except for the root logger), as it represents the position of the logger in the hierarchy.</li> 9.636 +<li>The Load, Save, Save As and Reset buttons should be reasonably self-explanatory (except perhaps Reset, which just deletes all non-root loggers, handlers and formatters and starts with a clean slate).</li> 9.637 +<li>Filters are not supported in this release, but will be as soon as time permits and if there is enough demand.</li> 9.638 +</ul></p> 9.639 +<a name="scenarios"></a><h4>Case Scenarios</h4> 9.640 + 9.641 +<p>With reference to the <a href="http://www.python.org/peps/pep-0282.html">PEP</a>, 9.642 +here are my comments on the current state of play. 9.643 + 9.644 +<ol> 9.645 + <li>A short simple script. See the example <a href="#simplest"><code>app.py</code></a> 9.646 + above.</li> 9.647 + <li>Medium sized app with C extension module. I have not specifically considered C extension 9.648 + modules but I assume they can just use the standard Python C API to make logging calls.</li> 9.649 + <li>Distutils. I would welcome more specific comments on what kind of configuration people 9.650 + think would be useful. To a certain extent, controlling verbosity levels through setup.py 9.651 + options is, I think, the domain of the app developer rather than the logging package.</li> 9.652 + <li>Large applications. If users can restart a system after changing the logging settings 9.653 + (via some user-friendly or support-desk-friendly interface) then present functionality 9.654 + should cater for this. In the case where the logging behaviour of a (long-)running system needs 9.655 + to be changed, then the functionality (new in 0.4.3) described <a href="#onthefly">below</a> can be used.</li> 9.656 +</ol> 9.657 + 9.658 +<a name="threadsafe"></a><h4>Thread Safety</h4> 9.659 + 9.660 +<p>The package is intended to be threadsafe. Although it makes no use of threads to provide its functionality (except for <a href="#onthefly">on-the-fly reconfiguration</a>), shared data in the package is protected by a thread lock which is acquired before, and released after, modifications to shared data. In addition, the Handler class creates a per-handler instance I/O lock which is acquired before, and released after, calling emit(). If you define your own handlers, in most situations you should not need to take any special precautions, as long as your I/O is called only from emit(). The thread locks impose a slight performance penalty, but it's no worse than for any other use of thread locks in Python applications.</p> 9.661 + 9.662 +<a name="onthefly"></a><h4>On-The-Fly Reconfiguration</h4> 9.663 + 9.664 +<p>The package also allows a program to permit changing of the logging configuration on the fly, i.e. <em>while the program is still running</em>. This should be a help for developers of long-running programs such as servers (e.g. Zope, Webware). At this stage, the on-the-fly configurability is fairly basic - to use it, two new module-level functions are provided (in the <code>logging.config</code> module). 9.665 +<ul> 9.666 + <li><code>listen([port])</code> is used to create a thread which, when started, opens a socket server which listens on the specified port for configuration requests. The socket protocol is very basic - a two-byte length followed by a string of that length is sent to the listener. The string should be in the same format as logging configuration files, i.e. ConfigParser files conforming to the scheme described <a href="#config">above</a>.</li> 9.667 + <li><code>stopListening()</code> tells the thread created by listening to terminate.</li> 9.668 +</ul> 9.669 +Currently, you can't just change part of the logging configuration - the sent configuration completely replaces the existing configuration, and if previously existing loggers are not in the new configurations, they will be disabled after the new configuration takes effect. The script <code>log_test17.py</code> in the distribution illustrates the on-the-fly configuration feature. 9.670 +</p> 9.671 + 9.672 +<a name="mlcf"></a><h4>Module-Level Convenience Functions</h4> 9.673 + 9.674 +<p>For the casual user, there are module-level convenience functions which operate on the 9.675 +root logger. <a href="logging_pydoc.html#functions">Here</a> is the pydoc for them. </p> 9.676 + 9.677 +<a name="perf"></a><h4>Performance</h4> 9.678 + 9.679 +<p>The implementation has not been optimized for performance. This is planned to be done in a later phase, following feature stabilization and benchmarking.</p> 9.680 + 9.681 +<a name="impstatus"></a><h4>Implementation Status</h4> 9.682 + 9.683 +<p>The implementation is what I have termed alpha - mainly because it has not had very wide exposure or extensive testing in many environments. Please try to use it/break it and give me 9.684 +any <a href="mailto:vinay_sajip@red-dove.com">feedback</a> you can! There is no reason I can see why this package should not be ready in time for the Python 2.3 release :-)</p> 9.685 + 9.686 + 9.687 +<a name="#acks"></a><h4>Acknowledgements</h4> 9.688 +<p>The biggest thank you goes to the <a href="http://jakarta.apache.org/log4j/">log4j</a> developers, whom I am attempting to flatter sincerely by imitation ;-) Thanks also to Trent Mick for <a href="http://www.python.org/peps/pep-0282.html">PEP 282</a>, which prompted me to offer this implementation.</p> 9.689 +<p>I'd also like to thank all of the people who have given me feedback, patches and encouragement. In particular (but in no particular order):</p> 9.690 +<ul> 9.691 +<li>Ollie Rutherfurd - patches and suggestions.</li> 9.692 +<li>Greg Ward - for nudging me in the direction of distutils.</li> 9.693 +<li>Hunter Matthews - questions about user-defined logging levels.</li> 9.694 +<li>Nicolas Untz - SysLogHandler implementation.</li> 9.695 +<li>Jeremy Hylton - discussions about logging exceptions.</li> 9.696 +<li>Kevin Butler - discussions about logging exceptions.</li> 9.697 +<li>Richard Jones - lots of positive feedback and ideas.</li> 9.698 +<li>David Goodger - suggestion about using CRITICAL rather than FATAL.</li> 9.699 +<li>Denis S. Otkidach - suggestions on filters and feedback on performance.</li> 9.700 +</ul> 9.701 +<a name="todo"></a><h4>Still To Do</h4> 9.702 +<p>No rest for the wicked...</p> 9.703 +<ul> 9.704 +<li>Improvements to the GUI configurator. Feedback, anyone?</li> 9.705 +<li>Overview-type documentation? The pydoc is reasonably comprehensive (I like to think). Perhaps a slightly formalized version of the information on this page?</li> 9.706 +<li>Testing, and more testing (you could help with this, too ...)</li> 9.707 +</ul> 9.708 +<p>If you can help with any of this, please <a href="mailto:vinay_sajip@red-dove.com">email me</a>.</p> 9.709 +<a name="download"></a><h4>Download and Installation</h4> 9.710 +<p>The current version is 0.4.7. <a href="logging-0.4.7.tar.gz">Here</a> is the latest tarball (also in <a href="logging-0.4.7.zip">zip</a> format or <a href="logging-0.4.7.win32.exe">Windows executable</a> - the latter includes the logging package only). The distribution contains the following files: 9.711 +<hr> 9.712 +<table border="0" cellpadding="0" cellspacing="0" width="100%"> 9.713 +<tr><th>Filename</th><th> </th><th>Contents</th></tr> 9.714 +<tr><td colspan="3"> </td></tr> 9.715 +<tr> 9.716 +<td><code>README.txt</code></td><td> </td> 9.717 +<td>Brief description and change history.</td> 9.718 +</tr> 9.719 +<tr> 9.720 +<td><code>__init__.py</code></td><td> </td> 9.721 +<td>The core logging package itself, including StreamHandler and FileHandler.</td> 9.722 +</tr> 9.723 +<tr> 9.724 +<td><code>handlers.py</code></td><td> </td> 9.725 +<td>The other handlers provided as part of the package.</td> 9.726 +</tr> 9.727 +<tr> 9.728 +<td><code>config.py</code></td><td> </td> 9.729 +<td>The code for configuring the package.</td> 9.730 +</tr> 9.731 +<tr> 9.732 +<td><code>setup.py</code></td><td> </td> 9.733 +<td>The distutils setup script.</td> 9.734 +</tr> 9.735 +<tr> 9.736 +<td><code>logrecv.py</code></td><td> </td> 9.737 +<td>A test server used for testing SocketHandler, DatagramHandler, HTTPHandler and SOAPHandler. Run it with an argument of one of "TCP", "UDP", "HTTP" or "SOAP" before running a test harness which logs to one of these handlers. Note that to use the SOAP handler, you need to have installed <a href="http://sourceforge.net/projects/pyxml">PyXML-0.6.6</a> and the <a href="http://www.zolera.com/opensrc/zsi/zsi.html">Zolera Soap Infrastructure</a>. This is needed for <code>logrecv.py</code> only, and not for the <code>logging</code> module itself. (Note that ZSI requires Python 2.x)</td> 9.738 +</tr> 9.739 +<tr> 9.740 +<td><code>app.py</code></td><td> </td> 9.741 +<td>The minimal example described <a href="#simplest">above</a>.</td> 9.742 +</tr> 9.743 +<tr> 9.744 +<td><code>mymodule.py</code></td><td> </td> 9.745 +<td>Another example described <a href="#nextsimplest">above</a>.</td> 9.746 +</tr> 9.747 +<tr> 9.748 +<td><code>myapp.py</code></td><td> </td> 9.749 +<td>From the second example described <a href="#nextsimplest">above</a>.</td> 9.750 +</tr> 9.751 +<tr> 9.752 +<td><code>log_test.py</code></td><td> </td> 9.753 +<td>A test script intended to work as a regression test harness. Runs a number of the other scripts and generates output to stdout.log and stderr.log.</td> 9.754 +</tr> 9.755 +<tr> 9.756 +<td><code>log_test0.py</code></td><td> </td> 9.757 +<td>A simple test script using <code>basicConfig()</code> only.</td> 9.758 +</tr> 9.759 +<tr> 9.760 +<td><code>log_test1.py</code></td><td> </td> 9.761 +<td>An example showing slightly more involved configuration and 9.762 + exception handling, as well as a Unix syslog handler which uses the standard library's syslog module.</td> 9.763 +</tr> 9.764 +<tr> 9.765 +<td><code>log_test2.py</code></td><td> </td> 9.766 +<td>A version of <code>log_test0.py</code> which only logs to 9.767 + a <code>SocketHandler</code>.</td> 9.768 +</tr> 9.769 +<tr> 9.770 +<td><code>log_test3.py</code></td><td> </td> 9.771 +<td>An example showing use of <code>fileConfig()</code> and 9.772 + logging to various loggers.</td> 9.773 +</tr> 9.774 +<tr> 9.775 +<td><code>log_test4.py</code></td><td> </td> 9.776 +<td>An example showing use of bespoke levels, filtering by level at logger and handler, and use of filter classes (descendants of <code>Filter</code>).</td> 9.777 +</tr> 9.778 +<tr> 9.779 +<td><code>log_test5.py</code></td><td> </td> 9.780 +<td>An example showing use of <code>SMTPHandler</code>. Before running this script, be sure to change the bogus addresses it contains to real ones which you have access to.</td> 9.781 +</tr> 9.782 +<tr> 9.783 +<td><code>log_test6.py</code></td><td> </td> 9.784 +<td>An example showing use of <code>NTEventLogHandler</code>. This script needs to be run on an NT system.</td> 9.785 +</tr> 9.786 +<tr> 9.787 +<td><code>log_test7.py</code></td><td> </td> 9.788 +<td>An example showing use of <code>MemoryHandler</code>.</td> 9.789 +</tr> 9.790 +<tr> 9.791 +<td><code>log_test8.py</code></td><td> </td> 9.792 +<td>An example showing use of <code>FileHandler</code> with rollover across multiple files.</td> 9.793 +</tr> 9.794 +<tr> 9.795 +<td><code>log_test9.py</code></td><td> </td> 9.796 +<td>An example showing use of <code>BufferingHandler</code> and <code>BufferingFormatter</code> through implementing simple <code>XMLFormatter</code> and <code>XMLHandler</code> classes.</td> 9.797 +</tr> 9.798 +<tr> 9.799 +<td><code>log_test10.py</code></td><td> </td> 9.800 +<td>An example showing how to get the logging module to create loggers of your own class (though it needs to be a subclass of <code>Logger</code>).</td> 9.801 +</tr> 9.802 +<tr> 9.803 +<td><code>log_test11.py</code></td><td> </td> 9.804 +<td>An example SMTP handler, called <code>BufferingSMTPHandler</code>, which buffers events and sends them via email in batches.</td> 9.805 +</tr> 9.806 +<tr> 9.807 +<td><code>log_test12.py</code></td><td> </td> 9.808 +<td>An example showing the use of <code>HTTPHandler</code>, for use with <code>logrecv.py</code>.</td> 9.809 +</tr> 9.810 +<tr> 9.811 +<td><code>log_test13.py</code></td><td> </td> 9.812 +<td>An example showing the use of <code>SOAPHandler</code>, for use with <code>logrecv.py</code>.</td> 9.813 +</tr> 9.814 +<tr> 9.815 +<td><code>log_test14.py</code></td><td> </td> 9.816 +<td>An example showing an implementation of <code>DBHandler</code>, showing how to log requests to RDBMS tables using the Python Database API 2.0.</td> 9.817 +</tr> 9.818 +<tr> 9.819 +<td><code>log_test15.py</code></td><td> </td> 9.820 +<td>An example showing the use of the <code>Filter</code> class with a string initializer.</td> 9.821 +</tr> 9.822 +<tr> 9.823 +<td><code>log_test16.py</code></td><td> </td> 9.824 +<td>An example showing the use of logging in a multi-threaded program.</td> 9.825 +</tr> 9.826 +<tr> 9.827 +<td><code>log_test17.py</code></td><td> </td> 9.828 +<td>An example showing the use of logging in a multi-threaded program, together with reconfiguring logging on the fly through the use of <code>listen()</code> and <code>stopListening()</code>. This script serves as both server and client, depending on the arguments it's called with.</td> 9.829 +</tr> 9.830 +<tr> 9.831 +<td><code>log_test18.py</code></td><td> </td> 9.832 +<td>An example showing the use of an example filter, MatchFilter, which offers flexible match-based 9.833 +filtering of LogRecords.</td> 9.834 +</tr> 9.835 +<tr> 9.836 +<td><code>log_test19.py</code></td><td> </td> 9.837 +<td>A basic test of logger parents.</td> 9.838 +</tr> 9.839 +<tr> 9.840 +<td><code>log_test20.py</code></td><td> </td> 9.841 +<td>Demonstrates the use of custom class instances for messages and filtering based on classes.</td> 9.842 +</tr> 9.843 +<tr> 9.844 +<td><code>log_test21.py</code></td><td> </td> 9.845 +<td>Demonstrates the use of a wildcard name-space filter with and without custom message classes.</td> 9.846 +</tr> 9.847 +<tr> 9.848 +<td><code>log_test22.py</code></td><td> </td> 9.849 +<td>Demonstrates the use of either localtime or gmtime to do date/time formatting.</td> 9.850 +</tr> 9.851 +<tr> 9.852 +<td><code>debug.ini</code></td><td> </td> 9.853 +<td>An example configuration for use with log_test17.py.</td> 9.854 +</tr> 9.855 +<tr> 9.856 +<td><code>warn.ini</code></td><td> </td> 9.857 +<td>An example configuration for use with log_test17.py.</td> 9.858 +</tr> 9.859 +<tr> 9.860 +<td><code>error.ini</code></td><td> </td> 9.861 +<td>An example configuration for use with log_test17.py.</td> 9.862 +</tr> 9.863 +<tr> 9.864 +<td><code>critical.ini</code></td><td> </td> 9.865 +<td>An example configuration for use with log_test17.py.</td> 9.866 +</tr> 9.867 +<tr> 9.868 +<td><code>log_test3.ini</code></td><td> </td> 9.869 +<td>An example configuration for use with log_test3.py.</td> 9.870 +</tr> 9.871 +<tr> 9.872 +<td><code>stdout.exp</code></td><td> </td> 9.873 +<td>The expected results of stdout.log after running log_test.py.</td> 9.874 +</tr> 9.875 +<tr> 9.876 +<td><code>stderr.exp</code></td><td> </td> 9.877 +<td>The expected results of stderr.log after running log_test.py.</td> 9.878 +</tr> 9.879 +<tr> 9.880 +<td><code>logconf.py</code></td><td> </td> 9.881 +<td>A Tkinter-based GUI configurator.</td> 9.882 +</tr> 9.883 +<tr> 9.884 +<td><code>logconf.ini</code></td><td> </td> 9.885 +<td>Example configuration file, in ConfigParser format, for use with <code>logconf.py</code> and <code>log_test3.py</code>.</td> 9.886 +</tr> 9.887 +<tr> 9.888 +<td><code>logging.dtd</code></td><td> </td> 9.889 +<td>A simple example DTD for use with <code>log_test9.py</code>.</td> 9.890 +</tr> 9.891 +<tr> 9.892 +<td><code>logging.xml</code></td><td> </td> 9.893 +<td>An example XML file for use with <code>log_test9.py</code>. It references <code>events.xml</code> as external data.</td> 9.894 +</tr> 9.895 +<tr> 9.896 +<td><code>events.xml</code></td><td> </td> 9.897 +<td>An example XML file for use with <code>log_test9.py</code>. It holds the actual events in XML format.</td> 9.898 +</tr> 9.899 +<tr> 9.900 +<td><code>python_logging.html</code></td><td> </td> 9.901 +<td>The page you're reading now.</td> 9.902 +</tr> 9.903 +<td><code>default.css</code></td><td> </td> 9.904 +<td>Stylesheet for use with the HTML pages.</td> 9.905 +</tr> 9.906 +<tr><td colspan="3"> </td></tr> 9.907 +</table> 9.908 +<hr> 9.909 +<p>To install, unpack the archive into any directory, and in that directory invoke the script <code>"setup.py install"</code> to install the module in the default location used by distutils.</p> 9.910 +<p>To use, just put <code>logging.py</code> in your Python path, "<code>import logging</code>" and go. (The installation procedure described above will normally put the logging module in your Python path. If you want to use file-based configuration API, you'll also need to <code>import logging.config</code>. To use the more esoteric handlers, you'll also need to <code>import logging.handlers</code>.)</p> 9.911 + 9.912 +<a name="changes"></a><h4>Change History</h4> 9.913 + 9.914 +<p>The change history is as follows.</p> 9.915 + 9.916 +<pre> 9.917 +Version Date Description 9.918 +============================================================================= 9.919 +0.4.7 15 Nov 2002 Made into a package with three modules: __init__ (the 9.920 + core code), handlers (all handlers other than 9.921 + FileHandler and its bases) and config (all the config 9.922 + stuff). Before doing this: 9.923 + Updated docstrings to include a short line, then a 9.924 + blank line, then more descriptive text. 9.925 + Renamed 'lvl' to 'level' in various functions. 9.926 + Changed FileHandler to use "a" and "w" instead of "a+" 9.927 + and "w+". 9.928 + Moved log file rotation functionality from FileHandler 9.929 + to a new class RotatingFileHandler. 9.930 + Improved docstring describing rollover. 9.931 + Updated makePickle to use 4-byte length and struct 9.932 + module, likewise logrecv.py. Also updated on-the-fly 9.933 + config reader to use 4-byte length/struct module. 9.934 + Altered ConfigParser test to look at 'readline' rather 9.935 + than 'read'. 9.936 + Added optional "defaults" argument to fileConfig, to 9.937 + be passed to ConfigParser. 9.938 + Renamed ALL to NOTSET to avoid confusion. 9.939 + Commented out getRootLogger(), as obsolete. 9.940 + To do regression testing, run log_test.py and compare 9.941 + the created files stdout.log and stderr.log against 9.942 + the files stdout.exp and stderr.exp. They should match 9.943 + except fir a couple of exception messages which give 9.944 + absolute file paths. 9.945 + Updated python_logging.html to remove links to 9.946 + logging_pydoc.html, which has been removed from the 9.947 + distribution. 9.948 + Changed default for raiseExceptions to 1. 9.949 +----------------------------------------------------------------------------- 9.950 +0.4.6 08 Jul 2002 Added raiseExceptions to allow conditional propagation 9.951 + of exceptions which occur during handling. 9.952 + Added converter to Formatter to allow use of any 9.953 + function to convert time from seconds to a tuple. It 9.954 + still defaults to time.localtime but now you can also 9.955 + use time.gmtime. 9.956 + Added log_test22.py to test the conversion feature. 9.957 + Changed rootlogger default level to WARN - was DEBUG. 9.958 + Updated some docstrings. 9.959 + Moved import of threading to where thread is imported. 9.960 + If either is unavailable, threading support is off. 9.961 + Updated minor defects in python_logging.html. 9.962 + Check to see if ConfigParser has readfp method; if it 9.963 + does and an object with a 'read' method is passed in, 9.964 + assumes a file-like object and uses readfp to read it 9.965 + in. 9.966 +----------------------------------------------------------------------------- 9.967 +0.4.5 04 Jun 2002 Fixed bug which caused problem if no args to message 9.968 + (suggested by Hye-Shik Chang). 9.969 + Fixed bug in _fixupParents (thanks to Nicholas Veeser) 9.970 + and added log_test19.py as a test case for this bug. 9.971 + Added getMessage to LogRecord (code was moved here from 9.972 + Formatter.format) 9.973 + Applied str() to record.msg to allow arbitrary classes 9.974 + to determine the formatting (as msg can now be a class 9.975 + instance). 9.976 + Table of Contents added to python_logging.html, the 9.977 + section on Loggers updated, and the logconf.ini file 9.978 + section annotated. 9.979 + Added log_test20.py which demonstrates how to use 9.980 + class instances to provide alternatives to numeric 9.981 + severities as mechanisms for control of logging. 9.982 + Added log_test21.py which builds on log_test20.py to 9.983 + show how you can use a regular expression-based Filter 9.984 + for flexible matching similar to e.g. Protomatter 9.985 + Syslog, where you can filter on e.g. "a.*" or "*.b" or 9.986 + "a.*.c". 9.987 + _levelNames changed to contain reverse mappings as well 9.988 + as forward mappings (leveltext->level as well as level 9.989 + -> leveltext). The reverse mappings are used by 9.990 + fileConfig(). 9.991 + fileConfig() now more forgiving of missing options in 9.992 + .ini file - sensible defaults now used when some 9.993 + options are absent. Also, eval() is used less when 9.994 + interpreting .ini file contents - int() and dict lookup 9.995 + are used in more places. Altered log_test3.py and added 9.996 + log_test3.ini to show a hand-coded configuration file. 9.997 +----------------------------------------------------------------------------- 9.998 +0.4.4 02 May 2002 getEffectiveLevel() returns ALL instead of None when 9.999 + nothing found. Modified references to level=0 to 9.1000 + level=ALL in a couple of places. 9.1001 + SocketHandler now inherits from Handler (it used to 9.1002 + inherit from StreamHandler, for no good reason). 9.1003 + getLock() renamed to createLock(). 9.1004 + Docstring tidy-ups, and some tidying up of 9.1005 + DatagramHandler. 9.1006 + Factored out unpickling in logrecv.py. 9.1007 + Added log_test18.py to illustrate MatchFilter, which is 9.1008 + a general matching filter. 9.1009 + Improved FileHandler.doRollover() so that the base 9.1010 + file name is always the most recent, then .1, then .2 9.1011 + etc. up to the maximum backup count. Renamed formal 9.1012 + args and attributes used in rollover. 9.1013 + Changed LogRecord attributes lvl -> levelno, level -> 9.1014 + levelname (less ambiguity) 9.1015 + Formatter.format searches for "%(asctime)" rather than 9.1016 + "(asctime)" 9.1017 + Renamed _start_time to _startTime 9.1018 + Formatter.formatTime now returns the time 9.1019 + Altered logrecv.py to support stopping servers 9.1020 + programmatically 9.1021 + Added log_test.py as overall test harness 9.1022 + basicConfig() can now be safely called more than once 9.1023 + Modified test scripts to make it easier to call them 9.1024 + from log_test.py 9.1025 + Moved SOAPHandler from core to log_test13.py. It's not 9.1026 + general enough to be in the core; most production use 9.1027 + will have differing RPC signatures. 9.1028 +----------------------------------------------------------------------------- 9.1029 +0.4.3 14 Apr 2002 Bug fix one-off error message to go to sys.stderr 9.1030 + rather than sys.stdout. 9.1031 + logrecv.py fix TCP for busy network. 9.1032 + Thread safety - added locking to Handler and for shared 9.1033 + data in module, and log_test16.py to test it. 9.1034 + Added socket listener to allow on-the-fly configuration 9.1035 + and added log_test17.py to test it. 9.1036 +----------------------------------------------------------------------------- 9.1037 +0.4.2 11 Apr 2002 Bug fix fileConfig() - setup of MemoryHandler target 9.1038 + and errors when loggers have no handlers set or 9.1039 + handlers have no formatters set 9.1040 + logconf.py - seems to hang if window closed when combo 9.1041 + dropdown is showing - added code to close popup on exit 9.1042 + Some tweaks to _srcfile computation (normpath added) 9.1043 + findCaller() optimized, now a lot faster! 9.1044 + Logger.removeHandler now closes the handler before 9.1045 + removing it 9.1046 + fileConfig() removes existing handlers before adding 9.1047 + the new set, to avoid memory leakage when repeated 9.1048 + calls are made 9.1049 + Fixed logrecv.py bug which hogged CPU time when TCP 9.1050 + connection was closed from the client 9.1051 + Added log_test14.py to demonstrate/test a DBHandler 9.1052 + which writes logging records into an RDBMS using the 9.1053 + Python Database API 2.0 (to run, you need something 9.1054 + which supports this already installed - I tested with 9.1055 + mxODBC) 9.1056 + Made getLogger name argument optional - returns root 9.1057 + logger if omitted 9.1058 + Altered Filter to take a string initializer, filtering 9.1059 + a sub-hierarchy rooted at a particular point (idea from 9.1060 + Denis S. Otkidach). 9.1061 + Added log_test15.py to test Filter initializer 9.1062 +----------------------------------------------------------------------------- 9.1063 +0.4.1 03 Apr 2002 Bug fix SMTPHandler - extra \r\n needed (Oleg Orlov) 9.1064 + Added BufferingHandler, BufferingFormatter 9.1065 + Renamed getChainedPriority to getEffectiveLevel 9.1066 + Removed Logger.getRoot as it is redundant 9.1067 + Added log_test9.py to test Buffering classes and 9.1068 + to show an XMLFormatter example. 9.1069 + Added setLoggerClass. 9.1070 + Added log_test10.py to test setLoggerClass, using an 9.1071 + example Logger-derived class which outputs exception 9.1072 + info even for DEBUG level logging calls 9.1073 + Added log_test11.py to test a buffering implementation 9.1074 + of SMTPHandler 9.1075 + Changed logging call implementation to allow keyword 9.1076 + arguments (Kevin Butler and others) 9.1077 + Changed default SysLogHandler implementation. 9.1078 + Renamed "additive" to "propagate" as it better 9.1079 + describes the attribute. 9.1080 + Added HTTPHandler. 9.1081 + Modified logrecv.py to remove "both" option and to add 9.1082 + "HTTP" and "SOAP" options (SOAP option needs you to 9.1083 + have PyXML-0.6.6 and ZSI installed - for logrecv.py 9.1084 + only, and not for the core logging module itself). 9.1085 + Added log_test12.py to test HTTPHandler. 9.1086 + Added log_test13.py to test SOAPHandler. 9.1087 + Formatted to Python source guidelines (spaces, indent 9.1088 + of 4, within 80 columns). 9.1089 + More method renamings (result of feedback) - _handle() 9.1090 + renamed to emit(), _logRecord() renamed to handle(). 9.1091 + Renamed FATAL to CRITICAL (David Goodger), but left 9.1092 + fatal() and FATAL in (until PEP is changed) 9.1093 + Changed configuration file format to ConfigParser 9.1094 + format. 9.1095 + Factored filter application functionality out to a new 9.1096 + Filterer class. The isLoggable() method is renamed to 9.1097 + filter() in both Filter and Filterer classes. 9.1098 + Altered SMTPHandler __init__ to accept (host, port) 9.1099 + for the mail internet address. 9.1100 + Added GUI configurator which uses Tkinter and the new 9.1101 + configuration file format. (See logconf.py and an 9.1102 + example configuration file in logconf.ini) 9.1103 + Altered log_test3.py to test with the new file format. 9.1104 +----------------------------------------------------------------------------- 9.1105 +0.4 21 Mar 2002 Incorporated comments/patches from Ollie Rutherfurd: 9.1106 + -Added level filtering for handlers. 9.1107 + -Return root logger if no name specified in getLogger. 9.1108 + Incorporated comments from Greg Ward: 9.1109 + -Added distutils setup.py script. 9.1110 + Added formatter initialization in Handler.__init__. 9.1111 + Tidied up docstrings. 9.1112 + Added removeHandler to Logger. 9.1113 + Added removeFilter to Logger and Handler. 9.1114 + logrecv.py modified to keep connection alive until 9.1115 + client closes it. 9.1116 + SocketHandler modified to not reset connection after 9.1117 + each logging event. 9.1118 + Added shutdown function which closes open sockets 9.1119 + Renamed DEFAULT_LOGGING_PORT->DEFAULT_TCP_LOGGING_PORT 9.1120 + Added DEFAULT_UDP_LOGGING_PORT 9.1121 + Added log_test4.py (example of arbitrary levels) 9.1122 + Added addLevelName, changed behaviour of getLevelName 9.1123 + Fixed bugs in DatagramHandler 9.1124 + Added SMTPHandler implementation 9.1125 + Added log_test5.py to test SMTPHandler 9.1126 + Added SysLogHandler (contribution from Nicolas Untz 9.1127 + based on Sam Rushing's syslog.py) 9.1128 + Modified log_test1.py to add a SysLogHandler 9.1129 + Added rollover functionality to FileHandler 9.1130 + Added NTEventLogHandler (based on Win32 extensions) 9.1131 + Added MemoryHandler implementation 9.1132 + Added log_test7.py to test MemoryHandler 9.1133 + Added log_test8.py to test FileHandler rollover 9.1134 + Added logException method to Logger 9.1135 + Added formatException method to Formatter 9.1136 + Added log_test6.py to test NTEventHandler and 9.1137 + logException 9.1138 + Numerous internal method renamings (sorry - but better 9.1139 + to do this now, rather than when we enter beta status). 9.1140 +----------------------------------------------------------------------------- 9.1141 +0.3 14 Mar 2002 First public release, for early feedback 9.1142 +----------------------------------------------------------------------------- 9.1143 +0.2 Consolidated into single file (for internal use only) 9.1144 +----------------------------------------------------------------------------- 9.1145 +0.1 Initial implementation (for internal use only) 9.1146 +----------------------------------------------------------------------------- 9.1147 +</pre> 9.1148 + 9.1149 +<a name="license"></a><h4>Copyright and License</h4> 9.1150 + 9.1151 +<p>The copyright statement follows. </p> 9.1152 + 9.1153 +<pre> 9.1154 +Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 9.1155 + 9.1156 +Permission to use, copy, modify, and distribute this software and its 9.1157 +documentation for any purpose and without fee is hereby granted, 9.1158 +provided that the above copyright notice appear in all copies and that 9.1159 +both that copyright notice and this permission notice appear in 9.1160 +supporting documentation, and that the name of Vinay Sajip 9.1161 +not be used in advertising or publicity pertaining to distribution 9.1162 +of the software without specific, written prior permission. 9.1163 +VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 9.1164 +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 9.1165 +VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 9.1166 +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 9.1167 +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 9.1168 +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 9.1169 + 9.1170 + 9.1171 + 9.1172 + 9.1173 + 9.1174 + 9.1175 + 9.1176 + 9.1177 + 9.1178 + 9.1179 + 9.1180 + 9.1181 + 9.1182 + 9.1183 + 9.1184 +</pre> 9.1185 +</body> 9.1186 +</html>
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/tools/python/logging/logging-0.4.9.2/setup.py Thu Jul 22 13:42:39 2004 +0000 10.3 @@ -0,0 +1,29 @@ 10.4 +from distutils.core import setup 10.5 + 10.6 +setup(name = "logging", 10.7 + description="A logging module for Python", 10.8 + long_description = """This module is intended to provide a standard error logging mechanism in Python as per PEP 282.""", 10.9 + license="""Copyright (C) 2001-2004 by Vinay Sajip. All Rights Reserved. 10.10 + 10.11 +Permission to use, copy, modify, and distribute this software and its 10.12 +documentation for any purpose and without fee is hereby granted, 10.13 +provided that the above copyright notice appear in all copies and that 10.14 +both that copyright notice and this permission notice appear in 10.15 +supporting documentation, and that the name of Vinay Sajip 10.16 +not be used in advertising or publicity pertaining to distribution 10.17 +of the software without specific, written prior permission. 10.18 + 10.19 +VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 10.20 +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 10.21 +VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 10.22 +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 10.23 +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 10.24 +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.""", 10.25 + version = "0.4.9.2", 10.26 + author = "Vinay Sajip", 10.27 + author_email = "vinay_sajip@red-dove.com", 10.28 + maintainer = "Vinay Sajip", 10.29 + maintainer_email = "vinay_sajip@red-dove.com", 10.30 + url = "http://www.red-dove.com/python_logging.html", 10.31 + packages = ["logging"], 10.32 + )
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/tools/python/logging/logging-0.4.9.2/test/app.py Thu Jul 22 13:42:39 2004 +0000 11.3 @@ -0,0 +1,5 @@ 11.4 +import logging 11.5 + 11.6 +logging.warning("Hello") 11.7 +logging.error("Still here...") 11.8 +logging.warning("Goodbye") 11.9 \ No newline at end of file
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/tools/python/logging/logging-0.4.9.2/test/critical.ini Thu Jul 22 13:42:39 2004 +0000 12.3 @@ -0,0 +1,60 @@ 12.4 +[loggers] 12.5 +keys=root,log02,log03,log04,log05 12.6 + 12.7 +[handlers] 12.8 +keys=hand01 12.9 + 12.10 +[formatters] 12.11 +keys=form01 12.12 + 12.13 +[logger_root] 12.14 +level=WARN 12.15 +propagate=1 12.16 +channel= 12.17 +parent= 12.18 +qualname=(root) 12.19 +handlers=hand01 12.20 + 12.21 +[logger_log02] 12.22 +level=CRITICAL 12.23 +propagate=1 12.24 +channel=A 12.25 +parent=(root) 12.26 +qualname=A 12.27 +handlers= 12.28 + 12.29 +[logger_log03] 12.30 +level=CRITICAL 12.31 +propagate=1 12.32 +channel=B 12.33 +parent=log02 12.34 +qualname=A.B 12.35 +handlers= 12.36 + 12.37 +[logger_log04] 12.38 +level=CRITICAL 12.39 +propagate=1 12.40 +channel=C 12.41 +parent=log03 12.42 +qualname=A.B.C 12.43 +handlers= 12.44 + 12.45 +[logger_log05] 12.46 +level=CRITICAL 12.47 +propagate=1 12.48 +channel=D 12.49 +parent=log04 12.50 +qualname=A.B.C.D 12.51 +handlers= 12.52 + 12.53 +[handler_hand01] 12.54 +class=StreamHandler 12.55 +level=NOTSET 12.56 +formatter=form01 12.57 +stream=sys.stderr 12.58 +args=(sys.stderr,) 12.59 + 12.60 +[formatter_form01] 12.61 +format=critical.ini %(name)s %(levelname)s %(message)s 12.62 +datefmt= 12.63 +
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/tools/python/logging/logging-0.4.9.2/test/debug.ini Thu Jul 22 13:42:39 2004 +0000 13.3 @@ -0,0 +1,60 @@ 13.4 +[loggers] 13.5 +keys=root,log02,log03,log04,log05 13.6 + 13.7 +[handlers] 13.8 +keys=hand01 13.9 + 13.10 +[formatters] 13.11 +keys=form01 13.12 + 13.13 +[logger_root] 13.14 +level=WARN 13.15 +propagate=1 13.16 +channel= 13.17 +parent= 13.18 +qualname=(root) 13.19 +handlers=hand01 13.20 + 13.21 +[logger_log02] 13.22 +level=DEBUG 13.23 +propagate=1 13.24 +channel=A 13.25 +parent=(root) 13.26 +qualname=A 13.27 +handlers= 13.28 + 13.29 +[logger_log03] 13.30 +level=DEBUG 13.31 +propagate=1 13.32 +channel=B 13.33 +parent=log02 13.34 +qualname=A.B 13.35 +handlers= 13.36 + 13.37 +[logger_log04] 13.38 +level=DEBUG 13.39 +propagate=1 13.40 +channel=C 13.41 +parent=log03 13.42 +qualname=A.B.C 13.43 +handlers= 13.44 + 13.45 +[logger_log05] 13.46 +level=DEBUG 13.47 +propagate=1 13.48 +channel=D 13.49 +parent=log04 13.50 +qualname=A.B.C.D 13.51 +handlers= 13.52 + 13.53 +[handler_hand01] 13.54 +class=StreamHandler 13.55 +level=NOTSET 13.56 +formatter=form01 13.57 +stream=sys.stderr 13.58 +args=(sys.stderr,) 13.59 + 13.60 +[formatter_form01] 13.61 +format=debug.ini %(name)s %(levelname)s %(message)s 13.62 +datefmt= 13.63 +
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/tools/python/logging/logging-0.4.9.2/test/error.ini Thu Jul 22 13:42:39 2004 +0000 14.3 @@ -0,0 +1,60 @@ 14.4 +[loggers] 14.5 +keys=root,log02,log03,log04,log05 14.6 + 14.7 +[handlers] 14.8 +keys=hand01 14.9 + 14.10 +[formatters] 14.11 +keys=form01 14.12 + 14.13 +[logger_root] 14.14 +level=WARN 14.15 +propagate=1 14.16 +channel= 14.17 +parent= 14.18 +qualname=(root) 14.19 +handlers=hand01 14.20 + 14.21 +[logger_log02] 14.22 +level=ERROR 14.23 +propagate=1 14.24 +channel=A 14.25 +parent=(root) 14.26 +qualname=A 14.27 +handlers= 14.28 + 14.29 +[logger_log03] 14.30 +level=ERROR 14.31 +propagate=1 14.32 +channel=B 14.33 +parent=log02 14.34 +qualname=A.B 14.35 +handlers= 14.36 + 14.37 +[logger_log04] 14.38 +level=ERROR 14.39 +propagate=1 14.40 +channel=C 14.41 +parent=log03 14.42 +qualname=A.B.C 14.43 +handlers= 14.44 + 14.45 +[logger_log05] 14.46 +level=ERROR 14.47 +propagate=1 14.48 +channel=D 14.49 +parent=log04 14.50 +qualname=A.B.C.D 14.51 +handlers= 14.52 + 14.53 +[handler_hand01] 14.54 +class=StreamHandler 14.55 +level=NOTSET 14.56 +formatter=form01 14.57 +stream=sys.stderr 14.58 +args=(sys.stderr,) 14.59 + 14.60 +[formatter_form01] 14.61 +format=error.ini %(name)s %(levelname)s %(message)s 14.62 +datefmt= 14.63 +
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/tools/python/logging/logging-0.4.9.2/test/events.xml Thu Jul 22 13:42:39 2004 +0000 15.3 @@ -0,0 +1,31 @@ 15.4 + 15.5 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.6 + <message>Info index = 90</message> 15.7 + </event> 15.8 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.9 + <message>Info index = 91</message> 15.10 + </event> 15.11 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.12 + <message>Info index = 92</message> 15.13 + </event> 15.14 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.15 + <message>Info index = 93</message> 15.16 + </event> 15.17 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.18 + <message>Info index = 94</message> 15.19 + </event> 15.20 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.21 + <message>Info index = 95</message> 15.22 + </event> 15.23 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.24 + <message>Info index = 96</message> 15.25 + </event> 15.26 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.27 + <message>Info index = 97</message> 15.28 + </event> 15.29 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.30 + <message>Info index = 98</message> 15.31 + </event> 15.32 + <event name="root" level="20" filename="log_test9.py" lineno="66"> 15.33 + <message>Info index = 99</message> 15.34 + </event> 15.35 \ No newline at end of file
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 16.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test.py Thu Jul 22 13:42:39 2004 +0000 16.3 @@ -0,0 +1,158 @@ 16.4 +#!/usr/bin/env python 16.5 +# 16.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 16.7 +# 16.8 +# Permission to use, copy, modify, and distribute this software and its 16.9 +# documentation for any purpose and without fee is hereby granted, 16.10 +# provided that the above copyright notice appear in all copies and that 16.11 +# both that copyright notice and this permission notice appear in 16.12 +# supporting documentation, and that the name of Vinay Sajip 16.13 +# not be used in advertising or publicity pertaining to distribution 16.14 +# of the software without specific, written prior permission. 16.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 16.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 16.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 16.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 16.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 16.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16.21 +# 16.22 +# This file is part of the Python logging distribution. See 16.23 +# http://www.red-dove.com/python_logging.html 16.24 +# 16.25 +"""Test harness for the logging module. Run all tests. 16.26 + 16.27 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 16.28 +""" 16.29 + 16.30 +import os, sys, logging, threading, time 16.31 + 16.32 +BANNER = "-- %-10s %-6s --------------------------------------------------------\n" 16.33 + 16.34 +def banner(nm, typ): 16.35 + sep = BANNER % (nm, typ) 16.36 + sys.stdout.write(sep) 16.37 + sys.stdout.flush() 16.38 + sys.stderr.write(sep) 16.39 + sys.stderr.flush() 16.40 + 16.41 +def main(): 16.42 + oldout = sys.stdout 16.43 + olderr = sys.stderr 16.44 + sys.stdout = open("stdout.log", "w") 16.45 + sys.stderr = open("stderr.log", "w") 16.46 + logging.basicConfig() 16.47 + root = logging.getLogger("") 16.48 + hdlr0 = root.handlers[0] 16.49 + #Set up servers 16.50 + import logrecv 16.51 + threads = [] 16.52 + tcpserver = logrecv.LogRecordSocketReceiver() 16.53 + tcpserver.logname = "" 16.54 + threads.append(threading.Thread(target=logrecv.runTCP, args=(tcpserver,))) 16.55 + udpserver = logrecv.LogRecordDatagramReceiver() 16.56 + udpserver.logname = "" 16.57 + threads.append(threading.Thread(target=logrecv.runUDP, args=(udpserver,))) 16.58 + httpserver = logrecv.LogRecordHTTPReceiver() 16.59 + httpserver.logname = "" 16.60 + threads.append(threading.Thread(target=logrecv.runHTTP, args=(httpserver,))) 16.61 + soapserver = None 16.62 + if logrecv.SOAPServer: 16.63 + soapserver = logrecv.SOAPServer() 16.64 + soapserver.modules = (sys.modules["logrecv"],) 16.65 + soapserver.logname = "" 16.66 + threads.append(threading.Thread(target=logrecv.runSOAP, args=(soapserver,))) 16.67 + 16.68 + for thread in threads: 16.69 + thread.start() 16.70 + try: 16.71 + import log_test0 16.72 + banner("log_test0", "begin") 16.73 + log_test0.main() 16.74 + banner("log_test0", "end") 16.75 + try: 16.76 + import log_test1 16.77 + banner("log_test1", "begin") 16.78 + log_test1.main() 16.79 + banner("log_test1", "end") 16.80 + except ImportError: 16.81 + pass 16.82 + 16.83 + import log_test2 16.84 + banner("log_test2", "begin") 16.85 + log_test2.main() 16.86 + banner("log_test2", "end") 16.87 + time.sleep(3) 16.88 + 16.89 + #Skip 3 as it tests fileConfig 16.90 + 16.91 + banner("log_test4", "begin") 16.92 + import log_test4 16.93 + banner("log_test4", "end") 16.94 + 16.95 + #Skip 5 as it tests SMTPHandler, can't easily check results automatically 16.96 + 16.97 + #Skip 6 as it tests NTEventLogHandler, can't easily check results automatically 16.98 + 16.99 + banner("log_test7", "begin") 16.100 + root.removeHandler(hdlr0) 16.101 + import log_test7 16.102 + root.addHandler(hdlr0) 16.103 + banner("log_test7", "end") 16.104 + 16.105 + banner("log_test8", "begin") 16.106 + import log_test8 16.107 + root.removeHandler(hdlr0) 16.108 + log_test8.main() 16.109 + root.addHandler(hdlr0) 16.110 + banner("log_test8", "end") 16.111 + 16.112 + banner("log_test9", "begin") 16.113 + import log_test9 16.114 + root.removeHandler(hdlr0) 16.115 + log_test9.main() 16.116 + root.addHandler(hdlr0) 16.117 + banner("log_test9", "end") 16.118 + 16.119 + banner("log_test10", "begin") 16.120 + import log_test10 16.121 + log_test10.main() 16.122 + banner("log_test10", "end") 16.123 + 16.124 + #Skip 11 as it tests SMTPHandler, can't easily check results automatically 16.125 + 16.126 + import log_test12 16.127 + banner("log_test12", "begin") 16.128 + log_test12.main() 16.129 + banner("log_test12", "end") 16.130 + 16.131 + import log_test13 16.132 + banner("log_test13", "begin") 16.133 + log_test13.main() 16.134 + banner("log_test13", "end") 16.135 + 16.136 + import log_test15 16.137 + banner("log_test15", "begin") 16.138 + log_test15.test(log_test15.FILTER) 16.139 + banner("log_test15", "end") 16.140 + 16.141 + time.sleep(3) 16.142 + 16.143 + finally: 16.144 + #shut down servers 16.145 + olderr.write("Tidying up...") 16.146 + tcpserver.abort = 1 16.147 + udpserver.abort = 1 16.148 + httpserver.abort = 1 16.149 + if soapserver: 16.150 + soapserver.abort = 1 16.151 + for thread in threads: 16.152 + thread.join() 16.153 + sys.stdout.close() 16.154 + sys.stdout = oldout 16.155 + #don't close this, as hdlr0 references it. hdlr0 will be closed at application exit 16.156 + #sys.stderr.close() 16.157 + sys.stderr = olderr 16.158 + print "Test run completed." 16.159 + 16.160 +if __name__ == "__main__": 16.161 + main()
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test0.py Thu Jul 22 13:42:39 2004 +0000 17.3 @@ -0,0 +1,118 @@ 17.4 +#!/usr/bin/env python 17.5 +# 17.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 17.7 +# 17.8 +# Permission to use, copy, modify, and distribute this software and its 17.9 +# documentation for any purpose and without fee is hereby granted, 17.10 +# provided that the above copyright notice appear in all copies and that 17.11 +# both that copyright notice and this permission notice appear in 17.12 +# supporting documentation, and that the name of Vinay Sajip 17.13 +# not be used in advertising or publicity pertaining to distribution 17.14 +# of the software without specific, written prior permission. 17.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 17.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 17.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 17.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 17.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 17.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17.21 +# 17.22 +# This file is part of the Python logging distribution. See 17.23 +# http://www.red-dove.com/python_logging.html 17.24 +# 17.25 +"""Test harness for the logging module. A basic test of levels. 17.26 + 17.27 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 17.28 +""" 17.29 + 17.30 +import logging 17.31 +msgcount = 0 17.32 + 17.33 +def nextmessage(): 17.34 + global msgcount 17.35 + rv = "Message %d" % msgcount 17.36 + msgcount = msgcount + 1 17.37 + return rv 17.38 + 17.39 +def main(): 17.40 + logging.basicConfig() 17.41 + logging.getLogger("").setLevel(logging.DEBUG) 17.42 + ERR = logging.getLogger("ERR") 17.43 + ERR.setLevel(logging.ERROR) 17.44 + INF = logging.getLogger("INF") 17.45 + INF.setLevel(logging.INFO) 17.46 + INF_ERR = logging.getLogger("INF.ERR") 17.47 + INF_ERR.setLevel(logging.ERROR) 17.48 + DEB = logging.getLogger("DEB") 17.49 + DEB.setLevel(logging.DEBUG) 17.50 + 17.51 + INF_UNDEF = logging.getLogger("INF.UNDEF") 17.52 + INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF") 17.53 + UNDEF = logging.getLogger("UNDEF") 17.54 + 17.55 + GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") 17.56 + CHILD = logging.getLogger("INF.BADPARENT") 17.57 + 17.58 + #These should log 17.59 + ERR.log(logging.CRITICAL, nextmessage()) 17.60 + ERR.error(nextmessage()) 17.61 + 17.62 + INF.log(logging.CRITICAL, nextmessage()) 17.63 + INF.error(nextmessage()) 17.64 + INF.warning(nextmessage()) 17.65 + INF.info(nextmessage()) 17.66 + 17.67 + INF_UNDEF.log(logging.CRITICAL, nextmessage()) 17.68 + INF_UNDEF.error(nextmessage()) 17.69 + INF_UNDEF.warning(nextmessage()) 17.70 + INF_UNDEF.info(nextmessage()) 17.71 + 17.72 + INF_ERR.log(logging.CRITICAL, nextmessage()) 17.73 + INF_ERR.error(nextmessage()) 17.74 + 17.75 + INF_ERR_UNDEF.log(logging.CRITICAL, nextmessage()) 17.76 + INF_ERR_UNDEF.error(nextmessage()) 17.77 + 17.78 + DEB.log(logging.CRITICAL, nextmessage()) 17.79 + DEB.error(nextmessage()) 17.80 + DEB.warning(nextmessage()) 17.81 + DEB.info(nextmessage()) 17.82 + DEB.debug(nextmessage()) 17.83 + 17.84 + UNDEF.log(logging.CRITICAL, nextmessage()) 17.85 + UNDEF.error(nextmessage()) 17.86 + UNDEF.warning(nextmessage()) 17.87 + UNDEF.info(nextmessage()) 17.88 + 17.89 + GRANDCHILD.log(logging.CRITICAL, nextmessage()) 17.90 + CHILD.log(logging.CRITICAL, nextmessage()) 17.91 + 17.92 + #These should not log 17.93 + ERR.warning(nextmessage()) 17.94 + ERR.info(nextmessage()) 17.95 + ERR.debug(nextmessage()) 17.96 + 17.97 + INF.debug(nextmessage()) 17.98 + INF_UNDEF.debug(nextmessage()) 17.99 + 17.100 + INF_ERR.warning(nextmessage()) 17.101 + INF_ERR.info(nextmessage()) 17.102 + INF_ERR.debug(nextmessage()) 17.103 + INF_ERR_UNDEF.warning(nextmessage()) 17.104 + INF_ERR_UNDEF.info(nextmessage()) 17.105 + INF_ERR_UNDEF.debug(nextmessage()) 17.106 + 17.107 + INF.info("Messages should bear numbers 0 through 24.") 17.108 + 17.109 +if __name__ == "__main__": 17.110 + import sys 17.111 + #print sys.argv[0] 17.112 + args = sys.argv[1:] 17.113 + if "-profile" in args: 17.114 + import profile, pstats 17.115 + args.remove("-profile") 17.116 + statf = "log_test0.pro" 17.117 + profile.run("main()", statf) 17.118 + stats = pstats.Stats(statf) 17.119 + stats.strip_dirs().sort_stats('time').print_stats() 17.120 + else: 17.121 + main()
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test1.py Thu Jul 22 13:42:39 2004 +0000 18.3 @@ -0,0 +1,85 @@ 18.4 +#!/usr/bin/env python 18.5 +# 18.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 18.7 +# 18.8 +# Permission to use, copy, modify, and distribute this software and its 18.9 +# documentation for any purpose and without fee is hereby granted, 18.10 +# provided that the above copyright notice appear in all copies and that 18.11 +# both that copyright notice and this permission notice appear in 18.12 +# supporting documentation, and that the name of Vinay Sajip 18.13 +# not be used in advertising or publicity pertaining to distribution 18.14 +# of the software without specific, written prior permission. 18.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 18.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 18.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 18.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 18.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 18.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18.21 +# 18.22 +# This file is part of the Python logging distribution. See 18.23 +# http://www.red-dove.com/python_logging.html 18.24 +# 18.25 +"""Test harness for the logging module. A test showing exception handling and use of SysLogHandler. 18.26 + 18.27 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 18.28 +""" 18.29 +import sys, logging 18.30 +import syslog 18.31 + 18.32 +class SLHandler(logging.Handler): 18.33 + def __init__(self, ident, logopt=0, facility=syslog.LOG_USER): 18.34 + logging.Handler.__init__(self) 18.35 + self.ident = ident 18.36 + self.logopt = logopt 18.37 + self.facility = facility 18.38 + self.mappings = { 18.39 + logging.DEBUG: syslog.LOG_DEBUG, 18.40 + logging.INFO: syslog.LOG_INFO, 18.41 + logging.WARNING: syslog.LOG_WARNING, 18.42 + logging.ERROR: syslog.LOG_ERR, 18.43 + logging.CRITICAL: syslog.LOG_CRIT, 18.44 + } 18.45 + 18.46 + def encodeLevel(self, level): 18.47 + return self.mappings.get(level, syslog.LOG_INFO) 18.48 + 18.49 + def emit(self, record): 18.50 + syslog.openlog(self.ident, self.logopt, self.facility) 18.51 + msg = self.format(record) 18.52 + prio = self.encodeLevel(record.levelno) 18.53 + syslog.syslog(prio, msg) 18.54 + syslog.closelog() 18.55 + 18.56 +def config(): 18.57 + logging.basicConfig() 18.58 + logging.getLogger("").setLevel(logging.DEBUG) 18.59 + if __name__ == "__main__": 18.60 + fmt = logging.Formatter("%(asctime)s %(filename)s:%(lineno)d %(levelname)-5s - %(message)s") 18.61 + hdlr = logging.FileHandler("tmp.tmp") 18.62 + hdlr.setFormatter(fmt) 18.63 + logging.getLogger("").addHandler(hdlr) 18.64 + else: 18.65 + fmt = None 18.66 + hdlr = SLHandler("log_test1") 18.67 + if fmt: 18.68 + hdlr.setFormatter(fmt) 18.69 + logging.getLogger("").addHandler(hdlr) 18.70 + return hdlr 18.71 + 18.72 +def run(): 18.73 + logging.info("Starting...") 18.74 + try: 18.75 + print "7" + 4 18.76 + except Exception, e: 18.77 + logging.error("Problem %s (%d)", "ERROR", logging.ERROR, exc_info=1) 18.78 + logging.debug("Problem %s (%d)", "DEBUG", logging.DEBUG, exc_info=1) 18.79 + logging.info("Done.") 18.80 + 18.81 + 18.82 +def main(): 18.83 + hdlr = config() 18.84 + run() 18.85 + logging.getLogger("").removeHandler(hdlr) 18.86 + 18.87 +if __name__ == "__main__": 18.88 + main()
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test10.py Thu Jul 22 13:42:39 2004 +0000 19.3 @@ -0,0 +1,87 @@ 19.4 +#!/usr/bin/env python 19.5 +# 19.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 19.7 +# 19.8 +# Permission to use, copy, modify, and distribute this software and its 19.9 +# documentation for any purpose and without fee is hereby granted, 19.10 +# provided that the above copyright notice appear in all copies and that 19.11 +# both that copyright notice and this permission notice appear in 19.12 +# supporting documentation, and that the name of Vinay Sajip 19.13 +# not be used in advertising or publicity pertaining to distribution 19.14 +# of the software without specific, written prior permission. 19.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 19.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 19.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 19.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 19.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19.21 +# 19.22 +# This file is part of the Python logging distribution. See 19.23 +# http://www.red-dove.com/python_logging.html 19.24 +# 19.25 +"""Test harness for the logging module. Shows use of a user-defined Logger subclass. 19.26 + 19.27 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 19.28 +""" 19.29 +import sys 19.30 +import locale 19.31 + 19.32 +locale.setlocale(locale.LC_ALL, '') 19.33 + 19.34 +from logging import * 19.35 + 19.36 +LOG_FORMAT = "%(asctime)s %(levelname)-5s %(message)s" 19.37 +DATE_FORMAT = "%x %X" 19.38 + 19.39 +class MyLogger(Logger): 19.40 + """ 19.41 + A simple example of a logger extension. 19.42 + """ 19.43 + def debug(self, msg, *args, **kwargs): 19.44 + """ 19.45 + This overridden method passes exception information for DEBUG level calls 19.46 + """ 19.47 + if self.manager.disable >= DEBUG: 19.48 + return 19.49 + if DEBUG >= self.getEffectiveLevel(): 19.50 + exc_info = kwargs.get("exc_info", 0) 19.51 + ei = None 19.52 + if exc_info: 19.53 + ei = sys.exc_info() 19.54 + if not ei[1]: 19.55 + ei = None 19.56 + self._log(DEBUG, msg, args, ei) 19.57 + del ei 19.58 + 19.59 +class NotALogger: 19.60 + pass 19.61 + 19.62 +def config(): 19.63 + try: 19.64 + setLoggerClass(NotALogger) 19.65 + except Exception, e: 19.66 + sys.stderr.write("%s\n" % e) 19.67 + setLoggerClass(MyLogger) 19.68 + if __name__ == "__main__": 19.69 + basicConfig() 19.70 + if __name__ == "__main__": 19.71 + getLogger("").handlers[0].setFormatter(Formatter(LOG_FORMAT, DATE_FORMAT)) 19.72 + 19.73 +def run(): 19.74 + getLogger("").setLevel(DEBUG) 19.75 + logger = getLogger("mylogger") 19.76 + logger.info("Starting...") 19.77 + logger.debug("Debug message not in exception handler (no traceback)") 19.78 + logger.info("About to throw exception...") 19.79 + try: 19.80 + print "7" + 4 19.81 + except Exception, e: 19.82 + logger.debug("Debug message inside exception handler (traceback)",exc_info=1) 19.83 + logger.info("Done.") 19.84 + 19.85 +def main(): 19.86 + config() 19.87 + run() 19.88 + 19.89 +if __name__ == "__main__": 19.90 + main()
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test11.py Thu Jul 22 13:42:39 2004 +0000 20.3 @@ -0,0 +1,72 @@ 20.4 +#!/usr/bin/env python 20.5 +# 20.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 20.7 +# 20.8 +# Permission to use, copy, modify, and distribute this software and its 20.9 +# documentation for any purpose and without fee is hereby granted, 20.10 +# provided that the above copyright notice appear in all copies and that 20.11 +# both that copyright notice and this permission notice appear in 20.12 +# supporting documentation, and that the name of Vinay Sajip 20.13 +# not be used in advertising or publicity pertaining to distribution 20.14 +# of the software without specific, written prior permission. 20.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 20.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 20.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 20.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 20.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20.21 +# 20.22 +# This file is part of the Python logging distribution. See 20.23 +# http://www.red-dove.com/python_logging.html 20.24 +# 20.25 +"""Test harness for the logging module. Tests BufferingSMTPHandler, an alternative implementation 20.26 +of SMTPHandler. 20.27 + 20.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 20.29 +""" 20.30 +import string, logging, logging.handlers 20.31 + 20.32 +MAILHOST = 'beta' 20.33 +FROM = 'log_test11@red-dove.com' 20.34 +TO = ['arkadi_renko'] 20.35 +SUBJECT = 'Test Logging email from Python logging module (buffering)' 20.36 + 20.37 +class BufferingSMTPHandler(logging.handlers.BufferingHandler): 20.38 + def __init__(self, mailhost, fromaddr, toaddrs, subject, capacity): 20.39 + logging.handlers.BufferingHandler.__init__(self, capacity) 20.40 + self.mailhost = mailhost 20.41 + self.mailport = None 20.42 + self.fromaddr = fromaddr 20.43 + self.toaddrs = toaddrs 20.44 + self.subject = subject 20.45 + self.setFormatter(logging.Formatter("%(asctime)s %(levelname)-5s %(message)s")) 20.46 + 20.47 + def flush(self): 20.48 + if len(self.buffer) > 0: 20.49 + try: 20.50 + import smtplib 20.51 + port = self.mailport 20.52 + if not port: 20.53 + port = smtplib.SMTP_PORT 20.54 + smtp = smtplib.SMTP(self.mailhost, port) 20.55 + msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" % (self.fromaddr, string.join(self.toaddrs, ","), self.subject) 20.56 + for record in self.buffer: 20.57 + s = self.format(record) 20.58 + print s 20.59 + msg = msg + s + "\r\n" 20.60 + smtp.sendmail(self.fromaddr, self.toaddrs, msg) 20.61 + smtp.quit() 20.62 + except: 20.63 + self.handleError(None) # no particular record 20.64 + self.buffer = [] 20.65 + 20.66 +def test(): 20.67 + logger = logging.getLogger("") 20.68 + logger.setLevel(logging.DEBUG) 20.69 + logger.addHandler(BufferingSMTPHandler(MAILHOST, FROM, TO, SUBJECT, 10)) 20.70 + for i in xrange(102): 20.71 + logger.info("Info index = %d", i) 20.72 + logging.shutdown() 20.73 + 20.74 +if __name__ == "__main__": 20.75 + test() 20.76 \ No newline at end of file
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test12.py Thu Jul 22 13:42:39 2004 +0000 21.3 @@ -0,0 +1,47 @@ 21.4 +#!/usr/bin/env python 21.5 +# 21.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 21.7 +# 21.8 +# Permission to use, copy, modify, and distribute this software and its 21.9 +# documentation for any purpose and without fee is hereby granted, 21.10 +# provided that the above copyright notice appear in all copies and that 21.11 +# both that copyright notice and this permission notice appear in 21.12 +# supporting documentation, and that the name of Vinay Sajip 21.13 +# not be used in advertising or publicity pertaining to distribution 21.14 +# of the software without specific, written prior permission. 21.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 21.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 21.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 21.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 21.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21.21 +# 21.22 +# This file is part of the Python logging distribution. See 21.23 +# http://www.red-dove.com/python_logging.html 21.24 +# 21.25 +""" 21.26 +A test harness for the logging module. Tests HTTPHandler. 21.27 + 21.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 21.29 +""" 21.30 +import sys, string, logging, logging.handlers 21.31 + 21.32 +def main(): 21.33 + import pdb 21.34 + host = "localhost:%d" % logging.handlers.DEFAULT_HTTP_LOGGING_PORT 21.35 + gh = logging.handlers.HTTPHandler(host, '/log', 'GET') 21.36 + ph = logging.handlers.HTTPHandler(host, '/log', 'POST') 21.37 + logger = logging.getLogger("log_test12") 21.38 + logger.propagate = 0 21.39 + logger.addHandler(gh) 21.40 + logger.addHandler(ph) 21.41 + logging.getLogger("").setLevel(logging.DEBUG) 21.42 + logger.info("Jackdaws love my big %s of %s", "sphinx", "quartz") 21.43 + logger.debug("Pack my %s with twelve dozen %s", "box", "liquor jugs") 21.44 + gh.close() 21.45 + ph.close() 21.46 + logger.removeHandler(gh) 21.47 + logger.removeHandler(ph) 21.48 + 21.49 +if __name__ == "__main__": 21.50 + main()
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test13.py Thu Jul 22 13:42:39 2004 +0000 22.3 @@ -0,0 +1,106 @@ 22.4 +#!/usr/bin/env python 22.5 +# 22.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 22.7 +# 22.8 +# Permission to use, copy, modify, and distribute this software and its 22.9 +# documentation for any purpose and without fee is hereby granted, 22.10 +# provided that the above copyright notice appear in all copies and that 22.11 +# both that copyright notice and this permission notice appear in 22.12 +# supporting documentation, and that the name of Vinay Sajip 22.13 +# not be used in advertising or publicity pertaining to distribution 22.14 +# of the software without specific, written prior permission. 22.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 22.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 22.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 22.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 22.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 22.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22.21 +# 22.22 +# This file is part of the Python logging distribution. See 22.23 +# http://www.red-dove.com/python_logging.html 22.24 +# 22.25 +""" 22.26 +A test harness for the logging module. Implements a SOAPHandler class which 22.27 +can be used to form the basis of extended SOAP functionality. 22.28 + 22.29 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 22.30 +""" 22.31 +import string, logging, logging.handlers, types 22.32 + 22.33 +SOAP_MESSAGE = """<SOAP-ENV:Envelope 22.34 + xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 22.35 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 22.36 + xmlns:xsd="http://www.w3.org/2001/XMLSchema" 22.37 + xmlns:logging="http://www.red-dove.com/logging" 22.38 + SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 22.39 +> 22.40 + <SOAP-ENV:Body> 22.41 + <logging:log> 22.42 +%s 22.43 + </logging:log> 22.44 + </SOAP-ENV:Body> 22.45 +</SOAP-ENV:Envelope> 22.46 +""" 22.47 + 22.48 +class SOAPHandler(logging.Handler): 22.49 + """ 22.50 + A class which sends records to a SOAP server. 22.51 + """ 22.52 + def __init__(self, host, url): 22.53 + """ 22.54 + Initialize the instance with the host and the request URL 22.55 + """ 22.56 + logging.Handler.__init__(self) 22.57 + self.host = host 22.58 + self.url = url 22.59 + 22.60 + def emit(self, record): 22.61 + """ 22.62 + Send the record to the Web server as a SOAP message 22.63 + """ 22.64 + try: 22.65 + import httplib 22.66 + h = httplib.HTTP(self.host) 22.67 + h.putrequest("POST", self.url) 22.68 + keys = record.__dict__.keys() 22.69 + keys.sort() 22.70 + args = "" 22.71 + for key in keys: 22.72 + v = record.__dict__[key] 22.73 + if type(v) == types.StringType: 22.74 + t = "string" 22.75 + elif (type(v) == types.IntType) or (type(v) == types.LongType): 22.76 + t = "integer" 22.77 + elif type(v) == types.FloatType: 22.78 + t = "float" 22.79 + else: 22.80 + t = "string" 22.81 + args = args + "%12s<logging:%s xsi:type=\"xsd:%s\">%s</logging:%s>\n" % ("", 22.82 + key, t, str(v), key) 22.83 + data = SOAP_MESSAGE % args[:-1] 22.84 + h.putheader("Content-type", "text/plain; charset=\"utf-8\"") 22.85 + h.putheader("Content-length", str(len(data))) 22.86 + h.endheaders() 22.87 + #print data 22.88 + h.send(data) 22.89 + r = h.getreply() #can't do anything with the result 22.90 + f = h.getfile() 22.91 + if f: 22.92 + #print f.read() 22.93 + f.close() 22.94 + except: 22.95 + self.handleError(record) 22.96 + 22.97 +def main(): 22.98 + sh = SOAPHandler('localhost:%d' % logging.handlers.DEFAULT_SOAP_LOGGING_PORT, '/log') 22.99 + logger = logging.getLogger("log_test13") 22.100 + logging.getLogger("").setLevel(logging.DEBUG) 22.101 + logger.propagate = 0 22.102 + logger.addHandler(sh) 22.103 + logger.info("Jackdaws love my big %s of %s", "sphinx", "quartz") 22.104 + logger.debug("Pack my %s with five dozen %s", "box", "liquor jugs") 22.105 + logger.removeHandler(sh) 22.106 + 22.107 +if __name__ == "__main__": 22.108 + main() 22.109 +
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test14.py Thu Jul 22 13:42:39 2004 +0000 23.3 @@ -0,0 +1,108 @@ 23.4 +#!/usr/bin/env python 23.5 +# 23.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 23.7 +# 23.8 +# Permission to use, copy, modify, and distribute this software and its 23.9 +# documentation for any purpose and without fee is hereby granted, 23.10 +# provided that the above copyright notice appear in all copies and that 23.11 +# both that copyright notice and this permission notice appear in 23.12 +# supporting documentation, and that the name of Vinay Sajip 23.13 +# not be used in advertising or publicity pertaining to distribution 23.14 +# of the software without specific, written prior permission. 23.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 23.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 23.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 23.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 23.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 23.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23.21 +# 23.22 +# This file is part of the Python logging distribution. See 23.23 +# http://www.red-dove.com/python_logging.html 23.24 +# 23.25 +""" 23.26 +A test harness for the logging module. An example handler - DBHandler - 23.27 +which writes to an Python DB API 2.0 data source. You'll need to set this 23.28 +source up before you run the test. 23.29 + 23.30 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 23.31 +""" 23.32 +import sys, string, time, logging 23.33 + 23.34 +class DBHandler(logging.Handler): 23.35 + def __init__(self, dsn, uid='', pwd=''): 23.36 + logging.Handler.__init__(self) 23.37 + import mx.ODBC.Windows 23.38 + self.dsn = dsn 23.39 + self.uid = uid 23.40 + self.pwd = pwd 23.41 + self.conn = mx.ODBC.Windows.connect(self.dsn, self.uid, self.pwd) 23.42 + self.SQL = """INSERT INTO Events ( 23.43 + Created, 23.44 + RelativeCreated, 23.45 + Name, 23.46 + LogLevel, 23.47 + LevelText, 23.48 + Message, 23.49 + Filename, 23.50 + Pathname, 23.51 + Lineno, 23.52 + Milliseconds, 23.53 + Exception, 23.54 + Thread 23.55 + ) 23.56 + VALUES ( 23.57 + %(dbtime)s, 23.58 + %(relativeCreated)d, 23.59 + '%(name)s', 23.60 + %(levelno)d, 23.61 + '%(levelname)s', 23.62 + '%(message)s', 23.63 + '%(filename)s', 23.64 + '%(pathname)s', 23.65 + %(lineno)d, 23.66 + %(msecs)d, 23.67 + '%(exc_text)s', 23.68 + '%(thread)s' 23.69 + ); 23.70 + """ 23.71 + self.cursor = self.conn.cursor() 23.72 + 23.73 + def formatDBTime(self, record): 23.74 + record.dbtime = time.strftime("#%m/%d/%Y#", time.localtime(record.created)) 23.75 + 23.76 + def emit(self, record): 23.77 + try: 23.78 + #use default formatting 23.79 + self.format(record) 23.80 + #now set the database time up 23.81 + self.formatDBTime(record) 23.82 + if record.exc_info: 23.83 + record.exc_text = logging._defaultFormatter.formatException(record.exc_info) 23.84 + else: 23.85 + record.exc_text = "" 23.86 + sql = self.SQL % record.__dict__ 23.87 + self.cursor.execute(sql) 23.88 + self.conn.commit() 23.89 + except: 23.90 + import traceback 23.91 + ei = sys.exc_info() 23.92 + traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr) 23.93 + del ei 23.94 + 23.95 + def close(self): 23.96 + self.cursor.close() 23.97 + self.conn.close() 23.98 + logging.Handler.close(self) 23.99 + 23.100 +dh = DBHandler('Logging') 23.101 +logger = logging.getLogger("") 23.102 +logger.setLevel(logging.DEBUG) 23.103 +logger.addHandler(dh) 23.104 +logger.info("Jackdaws love my big %s of %s", "sphinx", "quartz") 23.105 +logger.debug("Pack my %s with five dozen %s", "box", "liquor jugs") 23.106 +try: 23.107 + import math 23.108 + math.exp(1000) 23.109 +except: 23.110 + logger.exception("Problem with %s", "math.exp") 23.111 +logging.shutdown()
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test15.py Thu Jul 22 13:42:39 2004 +0000 24.3 @@ -0,0 +1,70 @@ 24.4 +#!/usr/bin/env python 24.5 +# 24.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 24.7 +# 24.8 +# Permission to use, copy, modify, and distribute this software and its 24.9 +# documentation for any purpose and without fee is hereby granted, 24.10 +# provided that the above copyright notice appear in all copies and that 24.11 +# both that copyright notice and this permission notice appear in 24.12 +# supporting documentation, and that the name of Vinay Sajip 24.13 +# not be used in advertising or publicity pertaining to distribution 24.14 +# of the software without specific, written prior permission. 24.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 24.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 24.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 24.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 24.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 24.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 24.21 +# 24.22 +# This file is part of the Python logging distribution. See 24.23 +# http://www.red-dove.com/python_logging.html 24.24 +# 24.25 +""" 24.26 +A test harness for the logging module. Tests Filter. 24.27 + 24.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 24.29 +""" 24.30 + 24.31 +import sys, logging 24.32 + 24.33 +FILTER = "a.b" 24.34 + 24.35 +def message(s): 24.36 + sys.stderr.write("%s\n" % s) 24.37 + 24.38 +def doLog(): 24.39 + logging.getLogger("a").info("Info 1") 24.40 + logging.getLogger("a.b").info("Info 2") 24.41 + logging.getLogger("a.c").info("Info 3") 24.42 + logging.getLogger("a.b.c").info("Info 4") 24.43 + logging.getLogger("a.b.c.d").info("Info 5") 24.44 + logging.getLogger("a.bb.c").info("Info 6") 24.45 + logging.getLogger("b").info("Info 7") 24.46 + logging.getLogger("b.a").info("Info 8") 24.47 + logging.getLogger("c.a.b").info("Info 9") 24.48 + logging.getLogger("a.bb").info("Info 10") 24.49 + 24.50 +def test(fs): 24.51 + root = logging.getLogger() 24.52 + root.setLevel(logging.DEBUG) 24.53 + if __name__ == "__main__": 24.54 + hand = logging.StreamHandler() 24.55 + hand.setFormatter(logging.Formatter("%(name)-10s %(message)s")) 24.56 + root.addHandler(hand) 24.57 + else: 24.58 + hand = root.handlers[0] 24.59 + message("Unfiltered...") 24.60 + doLog() 24.61 + message("Filtered with '%s'..." % fs) 24.62 + filt = logging.Filter(fs) 24.63 + hand.addFilter(filt) 24.64 + doLog() 24.65 + hand.removeFilter(filt) 24.66 + 24.67 +if __name__ == "__main__": 24.68 + import sys 24.69 + if len(sys.argv) > 1: 24.70 + fs = sys.argv[1] 24.71 + else: 24.72 + fs = FILTER 24.73 + test(fs)
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 25.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test16.py Thu Jul 22 13:42:39 2004 +0000 25.3 @@ -0,0 +1,73 @@ 25.4 +#!/usr/bin/env python 25.5 +# 25.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 25.7 +# 25.8 +# Permission to use, copy, modify, and distribute this software and its 25.9 +# documentation for any purpose and without fee is hereby granted, 25.10 +# provided that the above copyright notice appear in all copies and that 25.11 +# both that copyright notice and this permission notice appear in 25.12 +# supporting documentation, and that the name of Vinay Sajip 25.13 +# not be used in advertising or publicity pertaining to distribution 25.14 +# of the software without specific, written prior permission. 25.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 25.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 25.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 25.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 25.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 25.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25.21 +# 25.22 +# This file is part of the Python logging distribution. See 25.23 +# http://www.red-dove.com/python_logging.html 25.24 +# 25.25 +""" 25.26 +A test harness for the logging module. Tests thread safety. 25.27 + 25.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 25.29 +""" 25.30 + 25.31 +import logging, logging.handlers, thread, threading, random 25.32 + 25.33 +logging.raiseExceptions = 1 25.34 + 25.35 +NUM_THREADS = 10 25.36 +LOOP_COUNT = 10000 25.37 + 25.38 +LOG_MESSAGES = [ 25.39 + (logging.DEBUG, "%3d This is a %s message", "debug"), 25.40 + (logging.INFO, "%3d This is an %s message", "informational"), 25.41 + (logging.WARNING, "%3d This is a %s message", "warning"), 25.42 + (logging.ERROR, "%3d This is an %s message", "error"), 25.43 + (logging.CRITICAL, "%3d This is a %s message", "critical"), 25.44 +] 25.45 + 25.46 +LOG_NAMES = ["A", "A.B", "A.B.C", "A.B.C.D"] 25.47 + 25.48 +def doLog(num): 25.49 + logger = logging.getLogger('') 25.50 + logger.info("*** thread %s started (%d)", thread.get_ident(), num) 25.51 + for i in xrange(LOOP_COUNT): 25.52 + logger = logging.getLogger(random.choice(LOG_NAMES)) 25.53 + a = random.choice(LOG_MESSAGES) 25.54 + args = a[0:2] + (num,) + a[2:] 25.55 + apply(logger.log, args) 25.56 + 25.57 +def test(): 25.58 + f = logging.Formatter("%(asctime)s %(levelname)-9s %(name)-8s %(thread)5s %(message)s") 25.59 + root = logging.getLogger('') 25.60 + root.setLevel(logging.DEBUG) 25.61 + h = logging.FileHandler('thread.log', 'w') 25.62 + root.addHandler(h) 25.63 + h.setFormatter(f) 25.64 + h = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT) 25.65 + #h = logging.handlers.DatagramHandler('localhost', logging.handlers.DEFAULT_UDP_LOGGING_PORT) 25.66 + root.addHandler(h) 25.67 + threads = [] 25.68 + for i in xrange(NUM_THREADS): 25.69 + threads.append(threading.Thread(target=doLog, args=(len(threads),))) 25.70 + for t in threads: 25.71 + t.start() 25.72 + for t in threads: 25.73 + t.join() 25.74 + 25.75 +if __name__ == "__main__": 25.76 + test()
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test17.py Thu Jul 22 13:42:39 2004 +0000 26.3 @@ -0,0 +1,111 @@ 26.4 +#!/usr/bin/env python 26.5 +# 26.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 26.7 +# 26.8 +# Permission to use, copy, modify, and distribute this software and its 26.9 +# documentation for any purpose and without fee is hereby granted, 26.10 +# provided that the above copyright notice appear in all copies and that 26.11 +# both that copyright notice and this permission notice appear in 26.12 +# supporting documentation, and that the name of Vinay Sajip 26.13 +# not be used in advertising or publicity pertaining to distribution 26.14 +# of the software without specific, written prior permission. 26.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 26.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 26.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 26.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 26.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 26.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 26.21 +# 26.22 +# This file is part of the Python logging distribution. See 26.23 +# http://www.red-dove.com/python_logging.html 26.24 +# 26.25 +""" 26.26 +A test harness for the logging module. Tests thread safety. 26.27 + 26.28 +To use as a server, run with no arguments in one process. 26.29 +To use as a client, run with arguments "-client <filename>" where <filename> 26.30 +is the name of a file containing a logging configuration. 26.31 +The example files debug.ini, warn.ini, error.ini and critical.ini are 26.32 +provided to use in the test. They each have a customized message format 26.33 +(prefixed with their name) and the loggers have their levels set to the 26.34 +value implied by their name. 26.35 + 26.36 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 26.37 +""" 26.38 + 26.39 +import sys, logging, logging.config, thread, threading, random, time, struct 26.40 + 26.41 +NUM_THREADS = 10 26.42 +LOOP_COUNT = 10 26.43 + 26.44 +CONFIG_PORT = 9077 26.45 + 26.46 +logging.raiseExceptions = 1 26.47 + 26.48 +LOG_MESSAGES = [ 26.49 + (logging.DEBUG, "%3d This is a %s message", "debug"), 26.50 + (logging.INFO, "%3d This is an %s message", "informational"), 26.51 + (logging.WARNING, "%3d This is a %s message", "warning"), 26.52 + (logging.ERROR, "%3d This is an %s message", "error"), 26.53 + (logging.CRITICAL, "%3d This is a %s message", "critical"), 26.54 +] 26.55 + 26.56 +LOG_NAMES = ["A", "A.B", "A.B.C", "A.B.C.D"] 26.57 + 26.58 +def doLog(num): 26.59 + logger = logging.getLogger('') 26.60 + logger.setLevel(logging.DEBUG) 26.61 + logger.info("*** thread %s started (%d)", thread.get_ident(), num) 26.62 + for i in xrange(LOOP_COUNT): 26.63 + logger = logging.getLogger(random.choice(LOG_NAMES)) 26.64 + a = random.choice(LOG_MESSAGES) 26.65 + args = a[0:2] + (num,) + a[2:] 26.66 + time.sleep(random.random() * 3) 26.67 + apply(logger.log, args) 26.68 + 26.69 +def runserver(): 26.70 + f = logging.Formatter("%(asctime)s %(levelname)-9s %(name)-8s %(thread)5s %(message)s") 26.71 + root = logging.getLogger('') 26.72 + h = logging.StreamHandler() 26.73 + root.addHandler(h) 26.74 + h.setFormatter(f) 26.75 + threads = [] 26.76 + for i in xrange(NUM_THREADS): 26.77 + threads.append(threading.Thread(target=doLog, args=(len(threads),))) 26.78 + threads.append(logging.config.listen(CONFIG_PORT)) #don't use default port 26.79 + for t in threads: 26.80 + t.start() 26.81 + for t in threads[:-1]: 26.82 + t.join() 26.83 + logging.config.stopListening() 26.84 + threads[-1].join() 26.85 + 26.86 +def runclient(fname): 26.87 + import socket 26.88 + 26.89 + print "configuring with '%s'" % fname 26.90 + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 26.91 + sock.connect(('localhost', CONFIG_PORT)) 26.92 + f = open(fname, "r") 26.93 + s = f.read() 26.94 + f.close() 26.95 + slen = struct.pack(">L", len(s)) 26.96 + s = slen + s 26.97 + sentsofar = 0 26.98 + left = len(s) 26.99 + while left > 0: 26.100 + sent = sock.send(s[sentsofar:]) 26.101 + sentsofar = sentsofar + sent 26.102 + left = left - sent 26.103 + sock.close() 26.104 + 26.105 +if __name__ == "__main__": 26.106 + if "-client" not in sys.argv: 26.107 + runserver() 26.108 + else: 26.109 + sys.argv.remove("-client") 26.110 + if len(sys.argv) > 1: 26.111 + fname = sys.argv[1] 26.112 + else: 26.113 + fname = "warn.ini" 26.114 + runclient(fname) 26.115 \ No newline at end of file
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 27.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test18.py Thu Jul 22 13:42:39 2004 +0000 27.3 @@ -0,0 +1,102 @@ 27.4 +#!/usr/bin/env python 27.5 +# 27.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 27.7 +# 27.8 +# Permission to use, copy, modify, and distribute this software and its 27.9 +# documentation for any purpose and without fee is hereby granted, 27.10 +# provided that the above copyright notice appear in all copies and that 27.11 +# both that copyright notice and this permission notice appear in 27.12 +# supporting documentation, and that the name of Vinay Sajip 27.13 +# not be used in advertising or publicity pertaining to distribution 27.14 +# of the software without specific, written prior permission. 27.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 27.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 27.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 27.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 27.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 27.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 27.21 +# 27.22 +# This file is part of the Python logging distribution. See 27.23 +# http://www.red-dove.com/python_logging.html 27.24 +# 27.25 +""" 27.26 +A test harness for the logging module. Tests MatchFilter. 27.27 + 27.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 27.29 +""" 27.30 + 27.31 +import logging, types, string 27.32 + 27.33 +class MatchFilter(logging.Filter): 27.34 + def __init__(self, **kwargs): 27.35 + self.dict = kwargs 27.36 + 27.37 + def matchOne(self, key, value, record): 27.38 + rv = getattr(record, key) 27.39 + if key != "name": 27.40 + if key not in ["message", "msg"]: 27.41 + return rv == value 27.42 + else: 27.43 + return string.find(str(rv), value) >= 0 27.44 + else: 27.45 + if rv == value: 27.46 + return 1 27.47 + nlen = len(value) 27.48 + if string.find(rv, value, 0, nlen) != 0: 27.49 + return 0 27.50 + if rv[nlen] == ".": 27.51 + return 1 27.52 + 27.53 + def matchValue(self, key, record): 27.54 + vl = self.dict [key] 27.55 + if type(vl) != types.ListType: 27.56 + rv = self.matchOne(key, vl, record) 27.57 + else: 27.58 + for v in vl: 27.59 + rv = self.matchOne(key, v, record) 27.60 + if rv: 27.61 + break 27.62 + return rv 27.63 + 27.64 + def filter(self, record): 27.65 + rv = 1 27.66 + for k in self.dict.keys(): 27.67 + if self.matchValue(k, record): 27.68 + rv = 0 27.69 + break 27.70 + return rv 27.71 + 27.72 +def doLog(logger, n): 27.73 + logger.debug("Debug %d" % n) 27.74 + logger.info("Info %d" % n) 27.75 + logger.warning("Warning %d" % n) 27.76 + logger.error("Error %d" % n) 27.77 + logger.critical("Critical %d" % n) 27.78 + 27.79 +def test(): 27.80 + fmt = logging.Formatter("%(name)-10s %(levelname)-9s %(message)s") 27.81 + hand = logging.StreamHandler() 27.82 + hand.setFormatter(fmt) 27.83 + root = logging.getLogger("") 27.84 + root.setLevel(logging.DEBUG) 27.85 + root.addHandler(hand) 27.86 + loggers = ['A', 27.87 + 'A.B', 27.88 + 'A.BB', 27.89 + 'A.C', 27.90 + 'AA.B', 27.91 + 'A.B.C', 27.92 + 'A.B.C.D', 27.93 + 'A.B.C.D.E', 27.94 + 'Z.A.B', 27.95 + ] 27.96 + filt = MatchFilter(name = ['A.C', 'A.B.C'], #reject these loggers and their children 27.97 + levelno = [logging.WARNING, logging.CRITICAL], #reject these levels, 27.98 + msg = 'bug 2' #reject if this in message 27.99 + ) 27.100 + hand.addFilter(filt) 27.101 + for log in loggers: 27.102 + doLog(logging.getLogger(log), loggers.index(log)) 27.103 + 27.104 +if __name__ == "__main__": 27.105 + test()
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 28.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test19.py Thu Jul 22 13:42:39 2004 +0000 28.3 @@ -0,0 +1,57 @@ 28.4 +#!/usr/bin/env python 28.5 +# 28.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 28.7 +# 28.8 +# Permission to use, copy, modify, and distribute this software and its 28.9 +# documentation for any purpose and without fee is hereby granted, 28.10 +# provided that the above copyright notice appear in all copies and that 28.11 +# both that copyright notice and this permission notice appear in 28.12 +# supporting documentation, and that the name of Vinay Sajip 28.13 +# not be used in advertising or publicity pertaining to distribution 28.14 +# of the software without specific, written prior permission. 28.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 28.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 28.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 28.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 28.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 28.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 28.21 +# 28.22 +# This file is part of the Python logging distribution. See 28.23 +# http://www.red-dove.com/python_logging.html 28.24 +# 28.25 +"""Test harness for the logging module. A basic test of parents. 28.26 + 28.27 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 28.28 +""" 28.29 + 28.30 +import logging 28.31 + 28.32 +def main(): 28.33 + logging.basicConfig() 28.34 + root = logging.getLogger("") 28.35 + ab = logging.getLogger("a.b") 28.36 + abc = logging.getLogger("a.b.c") 28.37 + root.setLevel(logging.ERROR) 28.38 + ab.setLevel(logging.INFO) 28.39 + abc.info("Info") 28.40 + abc.warning("Warning") 28.41 + abc.error("Error") 28.42 + print "abc = %s" % abc 28.43 + print "abc.parent = %s" % abc.parent 28.44 + print "ab = %s" % ab 28.45 + print "ab.parent = %s" % ab.parent 28.46 + print "root = %s" % root 28.47 + 28.48 +if __name__ == "__main__": 28.49 + import sys 28.50 + print sys.argv[0] 28.51 + args = sys.argv[1:] 28.52 + if "-profile" in args: 28.53 + import profile, pstats 28.54 + args.remove("-profile") 28.55 + statf = "log_test19.pro" 28.56 + profile.run("main()", statf) 28.57 + stats = pstats.Stats(statf) 28.58 + stats.strip_dirs().sort_stats('time').print_stats() 28.59 + else: 28.60 + main()
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 29.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test2.py Thu Jul 22 13:42:39 2004 +0000 29.3 @@ -0,0 +1,119 @@ 29.4 +#!/usr/bin/env python 29.5 +# 29.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 29.7 +# 29.8 +# Permission to use, copy, modify, and distribute this software and its 29.9 +# documentation for any purpose and without fee is hereby granted, 29.10 +# provided that the above copyright notice appear in all copies and that 29.11 +# both that copyright notice and this permission notice appear in 29.12 +# supporting documentation, and that the name of Vinay Sajip 29.13 +# not be used in advertising or publicity pertaining to distribution 29.14 +# of the software without specific, written prior permission. 29.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 29.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 29.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 29.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 29.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 29.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 29.21 +# 29.22 +# This file is part of the Python logging distribution. See 29.23 +# http://www.red-dove.com/python_logging.html 29.24 +# 29.25 +""" 29.26 +A test harness for the logging module. Tests logger levels and basic Formatter, and logging to 29.27 +sockets. 29.28 + 29.29 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 29.30 +""" 29.31 + 29.32 +import logging, logging.handlers, socket 29.33 + 29.34 +msgcount = 0 29.35 + 29.36 +def nextmessage(): 29.37 + global msgcount 29.38 + rv = "Message %d" % msgcount 29.39 + msgcount = msgcount + 1 29.40 + return rv 29.41 + 29.42 +def main(): 29.43 + logging.basicConfig() 29.44 + logging.getLogger("").setLevel(logging.DEBUG) 29.45 + hdlr = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT) 29.46 + if __name__ == "__main__": 29.47 + hdlr.setFormatter(logging.Formatter("%(asctime)s %(name)-19s %(levelname)-5s - %(message)s")) 29.48 + logging.getLogger("").addHandler(hdlr) 29.49 + ERR = logging.getLogger("ERR") 29.50 + ERR.setLevel(logging.ERROR) 29.51 + INF = logging.getLogger("INF") 29.52 + INF.setLevel(logging.INFO) 29.53 + INF_ERR = logging.getLogger("INF.ERR") 29.54 + INF_ERR.setLevel(logging.ERROR) 29.55 + DEB = logging.getLogger("DEB") 29.56 + DEB.setLevel(logging.DEBUG) 29.57 + 29.58 + INF_UNDEF = logging.getLogger("INF.UNDEF") 29.59 + INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF") 29.60 + UNDEF = logging.getLogger("UNDEF") 29.61 + 29.62 + GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF") 29.63 + CHILD = logging.getLogger("INF.BADPARENT") 29.64 + 29.65 + #These should log 29.66 + ERR.log(logging.CRITICAL, nextmessage()) 29.67 + ERR.error(nextmessage()) 29.68 + 29.69 + INF.log(logging.CRITICAL, nextmessage()) 29.70 + INF.error(nextmessage()) 29.71 + INF.warning(nextmessage()) 29.72 + INF.info(nextmessage()) 29.73 + 29.74 + INF_UNDEF.log(logging.CRITICAL, nextmessage()) 29.75 + INF_UNDEF.error(nextmessage()) 29.76 + INF_UNDEF.warning(nextmessage()) 29.77 + INF_UNDEF.info(nextmessage()) 29.78 + 29.79 + INF_ERR.log(logging.CRITICAL, nextmessage()) 29.80 + INF_ERR.error(nextmessage()) 29.81 + 29.82 + INF_ERR_UNDEF.log(logging.CRITICAL, nextmessage()) 29.83 + INF_ERR_UNDEF.error(nextmessage()) 29.84 + 29.85 + DEB.log(logging.CRITICAL, nextmessage()) 29.86 + DEB.error(nextmessage()) 29.87 + DEB.warning(nextmessage()) 29.88 + DEB.info(nextmessage()) 29.89 + DEB.debug(nextmessage()) 29.90 + 29.91 + UNDEF.log(logging.CRITICAL, nextmessage()) 29.92 + UNDEF.error(nextmessage()) 29.93 + UNDEF.warning(nextmessage()) 29.94 + UNDEF.info(nextmessage()) 29.95 + 29.96 + GRANDCHILD.log(logging.CRITICAL, nextmessage()) 29.97 + CHILD.log(logging.CRITICAL, nextmessage()) 29.98 + 29.99 + #These should not log 29.100 + ERR.warning(nextmessage()) 29.101 + ERR.info(nextmessage()) 29.102 + ERR.debug(nextmessage()) 29.103 + 29.104 + INF.debug(nextmessage()) 29.105 + INF_UNDEF.debug(nextmessage()) 29.106 + 29.107 + INF_ERR.warning(nextmessage()) 29.108 + INF_ERR.info(nextmessage()) 29.109 + INF_ERR.debug(nextmessage()) 29.110 + INF_ERR_UNDEF.warning(nextmessage()) 29.111 + INF_ERR_UNDEF.info(nextmessage()) 29.112 + INF_ERR_UNDEF.debug(nextmessage()) 29.113 + 29.114 + INF.info("Messages should bear numbers 0 through 24.") 29.115 + hdlr.close() 29.116 + logging.getLogger("").removeHandler(hdlr) 29.117 + 29.118 +if __name__ == "__main__": 29.119 + try: 29.120 + main() 29.121 + except socket.error: 29.122 + print "\nA socket error occurred. Ensure that logrecv.py is running to receive logging requests from this script."
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 30.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test20.py Thu Jul 22 13:42:39 2004 +0000 30.3 @@ -0,0 +1,84 @@ 30.4 +#!/usr/bin/env python 30.5 +# 30.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 30.7 +# 30.8 +# Permission to use, copy, modify, and distribute this software and its 30.9 +# documentation for any purpose and without fee is hereby granted, 30.10 +# provided that the above copyright notice appear in all copies and that 30.11 +# both that copyright notice and this permission notice appear in 30.12 +# supporting documentation, and that the name of Vinay Sajip 30.13 +# not be used in advertising or publicity pertaining to distribution 30.14 +# of the software without specific, written prior permission. 30.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 30.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 30.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 30.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 30.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 30.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 30.21 +# 30.22 +# This file is part of the Python logging distribution. See 30.23 +# http://www.red-dove.com/python_logging.html 30.24 +# 30.25 +"""Test harness for the logging module. Demonstrates the use of custom class 30.26 +instances for messages and filtering based on classes. 30.27 + 30.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 30.29 +""" 30.30 + 30.31 +import logging 30.32 + 30.33 +class MyClass: 30.34 + def __init__(self, arg1, arg2): 30.35 + self.arg1 = arg1 30.36 + self.arg2 = arg2 30.37 + 30.38 + def __str__(self): 30.39 + return "%s, %s" % (self.arg1, self.arg2) 30.40 + 30.41 +class MyChildClass(MyClass): 30.42 + pass 30.43 + 30.44 +class ClassFilter(logging.Filter): 30.45 + def __init__(self, klass): 30.46 + self.klass = klass 30.47 + 30.48 + def filter(self, record): 30.49 + return isinstance(record.msg, self.klass) 30.50 + 30.51 +class MyClassFilter(ClassFilter): 30.52 + def __init__(self, arg): 30.53 + ClassFilter.__init__(self, MyClass) 30.54 + self.arg = arg 30.55 + 30.56 + def filter(self, record): 30.57 + return ClassFilter.filter(self, record) and (record.msg.arg2 == self.arg) 30.58 + 30.59 +def main(): 30.60 + handler = logging.StreamHandler() 30.61 + root = logging.getLogger("") 30.62 + root.setLevel(logging.DEBUG) 30.63 + root.addHandler(handler) 30.64 + root.addFilter(MyClassFilter("world")) 30.65 + #Not logged, as it's not a MyClass instance 30.66 + root.info("%s, %s", "Hello", "world") 30.67 + #Logged, as it's an appropriate instance which matches the filter criteria 30.68 + root.info(MyClass("Hello", "world")) 30.69 + #Not logged, as it's an appropriate class but doesn't match the filter criteria 30.70 + root.info(MyClass("Hello", "world!")) 30.71 + #Logged, as it's an appropriate instance which matches the filter criteria 30.72 + root.info(MyClass("Goodbye", "world")) 30.73 + #Logged, as it's an appropriate class which matches the filter criteria 30.74 + root.info(MyChildClass("Hello again", "world")) 30.75 + 30.76 +if __name__ == "__main__": 30.77 + import sys 30.78 + args = sys.argv[1:] 30.79 + if "-profile" in args: 30.80 + import profile, pstats 30.81 + args.remove("-profile") 30.82 + statf = "log_test20.pro" 30.83 + profile.run("main()", statf) 30.84 + stats = pstats.Stats(statf) 30.85 + stats.strip_dirs().sort_stats('time').print_stats() 30.86 + else: 30.87 + main()
31.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 31.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test21.py Thu Jul 22 13:42:39 2004 +0000 31.3 @@ -0,0 +1,141 @@ 31.4 +#!/usr/bin/env python 31.5 +# 31.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 31.7 +# 31.8 +# Permission to use, copy, modify, and distribute this software and its 31.9 +# documentation for any purpose and without fee is hereby granted, 31.10 +# provided that the above copyright notice appear in all copies and that 31.11 +# both that copyright notice and this permission notice appear in 31.12 +# supporting documentation, and that the name of Vinay Sajip 31.13 +# not be used in advertising or publicity pertaining to distribution 31.14 +# of the software without specific, written prior permission. 31.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 31.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 31.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 31.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 31.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 31.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 31.21 +# 31.22 +# This file is part of the Python logging distribution. See 31.23 +# http://www.red-dove.com/python_logging.html 31.24 +# 31.25 +"""Test harness for the logging module. Demonstrates the use of a wildcard 31.26 +name-space filter with and without custom message classes. 31.27 + 31.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 31.29 +""" 31.30 + 31.31 +import logging, re, string, types 31.32 + 31.33 +class TaggedEvent: 31.34 + def __init__(self, tag, msg): 31.35 + self.tag = tag 31.36 + self.msg = msg 31.37 + 31.38 + def __str__(self): 31.39 + return "%s: %s" % (self.tag, self.msg) 31.40 + 31.41 +class WildcardFilter(logging.Filter): 31.42 + def __init__(self, wildcards): 31.43 + self.setWildcards(wildcards) 31.44 + 31.45 + def setWildcard(self, wildcard): 31.46 + arr = string.split(wildcard, ".") 31.47 + for i in xrange(len(arr)): 31.48 + s = arr[i] 31.49 + if s == "*": 31.50 + arr[i] = r'[\w.]*' 31.51 + elif string.find(s, "*") > 0: 31.52 + arr[i] = string.replace(s, "*", r'[\w]*') 31.53 + s = "^%s$" % string.join(arr, r'\.') 31.54 + self.patterns.append(re.compile(s)) 31.55 + 31.56 + def setWildcards(self, wildcards): 31.57 + if type(wildcards) != types.ListType: 31.58 + wildcards = [wildcards] 31.59 + self.patterns = [] 31.60 + for wildcard in wildcards: 31.61 + self.setWildcard(wildcard) 31.62 + 31.63 + def filter(self, record): 31.64 + rv = 0 31.65 + for pat in self.patterns: 31.66 + m = pat.match(record.name) 31.67 + if m is not None: 31.68 + rv = 1 31.69 + break 31.70 + return rv 31.71 + 31.72 +class TagFilter(WildcardFilter): 31.73 + def filter(self, record): 31.74 + rv = 0 31.75 + if isinstance(record.msg, TaggedEvent): 31.76 + tag = record.msg.tag 31.77 + else: 31.78 + tag = record.name 31.79 + for pat in self.patterns: 31.80 + m = pat.match(tag) 31.81 + if m is not None: 31.82 + rv = 1 31.83 + break 31.84 + return rv 31.85 + 31.86 +def main(): 31.87 + handler = logging.StreamHandler() 31.88 + root = logging.getLogger("") 31.89 + root.setLevel(logging.DEBUG) 31.90 + ab = logging.getLogger("a.b") 31.91 + abc = logging.getLogger("a.b.c") 31.92 + 31.93 + root.addHandler(handler) 31.94 + filter = WildcardFilter("*.b") 31.95 + handler.addFilter(filter) 31.96 + 31.97 + ab.info("#1 from a.b") #logged 31.98 + abc.info("#1 from a.b.c") #not logged 31.99 + filter.setWildcards("*.b.c") 31.100 + ab.info("#2 from a.b") #not logged 31.101 + abc.info("#2 from a.b.c") #logged 31.102 + filter.setWildcards("*.b*") 31.103 + ab.info("#3 from a.b") #logged 31.104 + abc.info("#3 from a.b.c") #not logged 31.105 + filter.setWildcards("*") 31.106 + ab.info("#4 from a.b") #logged 31.107 + abc.info("#4 from a.b.c") #logged 31.108 + filter.setWildcards("a*") 31.109 + ab.info("#5 from a.b") #not logged 31.110 + abc.info("#5 from a.b.c") #not logged 31.111 + filter.setWildcards("a.*") 31.112 + ab.info("#6 from a.b") #logged 31.113 + abc.info("#6 from a.b.c") #logged 31.114 + filter.setWildcards("*.b.*") 31.115 + ab.info("#7 from a.b") #not logged 31.116 + abc.info("#7 from a.b.c") #logged 31.117 + filter.setWildcards(["*.b", "*.b.*"]) 31.118 + ab.info("#8 from a.b") #logged 31.119 + abc.info("#8 from a.b.c") #logged 31.120 + filter.setWildcards(["a.*.c"]) 31.121 + ab.info("#9 from a.b") #not logged 31.122 + abc.info("#9 from a.b.c") #logged 31.123 + 31.124 + #Now test filtering with a tagged class 31.125 + handler.removeFilter(filter) 31.126 + tagfilter = TagFilter(["*.b", "*.b.*"]) 31.127 + root.addFilter(tagfilter) 31.128 + root.info(TaggedEvent("a.b", "#10")) #logged 31.129 + root.info(TaggedEvent("a.c", "#10")) #not logged 31.130 + root.info(TaggedEvent("a.b.c", "#10")) #logged 31.131 + root.info(TaggedEvent("a.b.d", "#10")) #logged 31.132 + 31.133 +if __name__ == "__main__": 31.134 + import sys 31.135 + args = sys.argv[1:] 31.136 + if "-profile" in args: 31.137 + import profile, pstats 31.138 + args.remove("-profile") 31.139 + statf = "log_test21.pro" 31.140 + profile.run("main()", statf) 31.141 + stats = pstats.Stats(statf) 31.142 + stats.strip_dirs().sort_stats('time').print_stats() 31.143 + else: 31.144 + main()
32.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 32.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test22.py Thu Jul 22 13:42:39 2004 +0000 32.3 @@ -0,0 +1,50 @@ 32.4 +#!/usr/bin/env python 32.5 +# 32.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 32.7 +# 32.8 +# Permission to use, copy, modify, and distribute this software and its 32.9 +# documentation for any purpose and without fee is hereby granted, 32.10 +# provided that the above copyright notice appear in all copies and that 32.11 +# both that copyright notice and this permission notice appear in 32.12 +# supporting documentation, and that the name of Vinay Sajip 32.13 +# not be used in advertising or publicity pertaining to distribution 32.14 +# of the software without specific, written prior permission. 32.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 32.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 32.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 32.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 32.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 32.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 32.21 +# 32.22 +# This file is part of the Python logging distribution. See 32.23 +# http://www.red-dove.com/python_logging.html 32.24 +# 32.25 +"""Test harness for the logging module. Demonstrates the use of different 32.26 +converters for time(secs) -> time(tuple). 32.27 + 32.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 32.29 +""" 32.30 + 32.31 +import logging, time 32.32 + 32.33 +def main(): 32.34 + handler = logging.StreamHandler() 32.35 + f1 = logging.Formatter("%(asctime)s %(message)s", "%m/%d %H:%M:%S") 32.36 + f2 = logging.Formatter("%(asctime)s %(message)s", "%m/%d %H:%M:%S") 32.37 + f2.converter = time.gmtime 32.38 + handler.setFormatter(f1) 32.39 + root = logging.getLogger("") 32.40 + root.setLevel(logging.DEBUG) 32.41 + root.addHandler(handler) 32.42 + root.info("Something happened! [should be in local time]") 32.43 + handler.setFormatter(f2) 32.44 + root.info("Something else happened! [should be in GMT]") 32.45 + handler.setFormatter(f1) 32.46 + root.info("Something happened again! [should be in local time]") 32.47 + logging.Formatter.converter = time.gmtime 32.48 + root.info("Something else happened again! [should be in GMT]") 32.49 + logging.Formatter.converter = time.localtime 32.50 + root.info("Something else happened yet again! [should be in local time]") 32.51 + 32.52 +if __name__ == "__main__": 32.53 + main()
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 33.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test3.ini Thu Jul 22 13:42:39 2004 +0000 33.3 @@ -0,0 +1,95 @@ 33.4 +[loggers] 33.5 +keys=root,area1,area11,area111,area1111,area11111,area111111 33.6 + 33.7 +[handlers] 33.8 +keys=console,file,socket,datagram,syslog,NT,SMTP 33.9 + 33.10 +[formatters] 33.11 +keys=short,medium,long 33.12 + 33.13 +[logger_root] 33.14 +handlers=console 33.15 +level=DEBUG 33.16 + 33.17 +[logger_area1] 33.18 +level=DEBUG 33.19 +qualname=log02 33.20 +handlers=file 33.21 + 33.22 +[logger_area11] 33.23 +level=INFO 33.24 +qualname=log02.log03 33.25 +handlers=socket 33.26 + 33.27 +[logger_area111] 33.28 +level=WARN 33.29 +propagate=0 33.30 +qualname=log02.log03.log04 33.31 +handlers=datagram 33.32 + 33.33 +[logger_area1111] 33.34 +level=ERROR 33.35 +qualname=log02.log03.log04.log05 33.36 +handlers=syslog 33.37 + 33.38 +[logger_area11111] 33.39 +level=CRITICAL 33.40 +qualname=log02.log03.log04.log05.log06 33.41 +handlers=NT 33.42 + 33.43 +[logger_area111111] 33.44 +level=WARN 33.45 +qualname=log02.log03.log04.log05.log06.log07 33.46 +handlers=SMTP 33.47 + 33.48 +[handler_console] 33.49 +class=StreamHandler 33.50 +formatter=medium 33.51 +stream=sys.stdout 33.52 +args=(sys.stdout,) 33.53 + 33.54 +[handler_file] 33.55 +class=FileHandler 33.56 +level=DEBUG 33.57 +formatter=long 33.58 +args=('python.log', 'w') 33.59 + 33.60 +[handler_socket] 33.61 +class=handlers.SocketHandler 33.62 +level=INFO 33.63 +formatter=medium 33.64 +args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT) 33.65 + 33.66 +[handler_datagram] 33.67 +class=handlers.DatagramHandler 33.68 +level=WARN 33.69 +formatter=medium 33.70 +args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT) 33.71 + 33.72 +[handler_syslog] 33.73 +class=handlers.SysLogHandler 33.74 +level=ERROR 33.75 +formatter=short 33.76 +args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) 33.77 + 33.78 +[handler_NT] 33.79 +class=handlers.NTEventLogHandler 33.80 +level=CRITICAL 33.81 +formatter=medium 33.82 +args=('Python Application', '', 'Application') 33.83 + 33.84 +[handler_SMTP] 33.85 +class=handlers.SMTPHandler 33.86 +level=WARN 33.87 +formatter=long 33.88 +args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject') 33.89 + 33.90 +[formatter_short] 33.91 +format=%(message)s 33.92 + 33.93 +[formatter_long] 33.94 +format=%(asctime)s %(pathname)s(%(lineno)d): %(levelname)s %(message)s 33.95 + 33.96 +[formatter_medium] 33.97 +format=%(asctime)s %(levelname)s %(message)s 33.98 +datefmt=%m-%d-%Y
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 34.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test3.py Thu Jul 22 13:42:39 2004 +0000 34.3 @@ -0,0 +1,70 @@ 34.4 +#!/usr/bin/env python 34.5 +# 34.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 34.7 +# 34.8 +# Permission to use, copy, modify, and distribute this software and its 34.9 +# documentation for any purpose and without fee is hereby granted, 34.10 +# provided that the above copyright notice appear in all copies and that 34.11 +# both that copyright notice and this permission notice appear in 34.12 +# supporting documentation, and that the name of Vinay Sajip 34.13 +# not be used in advertising or publicity pertaining to distribution 34.14 +# of the software without specific, written prior permission. 34.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 34.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 34.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 34.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 34.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 34.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 34.21 +# 34.22 +# This file is part of the Python logging distribution. See 34.23 +# http://www.red-dove.com/python_logging.html 34.24 +# 34.25 +""" 34.26 +A test harness for the logging module. Tests new fileConfig (not yet a complete test). 34.27 + 34.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 34.29 +""" 34.30 +import logging, logging.config 34.31 + 34.32 +def doLog(logger): 34.33 + logger.debug("Debug") 34.34 + logger.info("Info") 34.35 + logger.warning("Warning") 34.36 + logger.error("Error") 34.37 + logger.critical("Critical") 34.38 + 34.39 +def main(): 34.40 + logging.config.fileConfig("log_test3.ini") 34.41 + logger = logging.getLogger(None) 34.42 + print "---------------------------------------------------" 34.43 + print "-- Logging to root; messages appear on console only" 34.44 + print "---------------------------------------------------" 34.45 + doLog(logger) 34.46 + print "----------------------------------------------------------------------" 34.47 + print "-- Logging to log02; messages appear on console and in file python.log" 34.48 + print "----------------------------------------------------------------------" 34.49 + logger = logging.getLogger("log02") 34.50 + doLog(logger) 34.51 + print "--------------------------------------------------------------------------" 34.52 + print "-- Logging to log02.log03; messages appear on console, in file python.log," 34.53 + print "-- and at logrecv.py tcp (if running. <= DEBUG messages will not appear)." 34.54 + print "--------------------------------------------------------------------------" 34.55 + logger = logging.getLogger("log02.log03") 34.56 + doLog(logger) 34.57 + print "-----------------------------------------------------------------------" 34.58 + print "-- Logging to log02.log03.log04; messages appear only at logrecv.py udp" 34.59 + print "-- (if running. <= INFO messages will not appear)." 34.60 + print "-----------------------------------------------------------------------" 34.61 + logger = logging.getLogger("log02.log03.log04") 34.62 + doLog(logger) 34.63 + print "--------------------------------------------------------------------" 34.64 + print "-- Logging to log02.log03.log04.log05.log06; messages appear at" 34.65 + print "-- logrecv.py udp (if running. < CRITICAL messages will not appear)." 34.66 + print "--------------------------------------------------------------------" 34.67 + logger = logging.getLogger("log02.log03.log04.log05.log06") 34.68 + doLog(logger) 34.69 + print "-- All done." 34.70 + logging.shutdown() 34.71 + 34.72 +if __name__ == "__main__": 34.73 + main() 34.74 \ No newline at end of file
35.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 35.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test4.py Thu Jul 22 13:42:39 2004 +0000 35.3 @@ -0,0 +1,168 @@ 35.4 +#!/usr/bin/env python 35.5 +# 35.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 35.7 +# 35.8 +# Permission to use, copy, modify, and distribute this software and its 35.9 +# documentation for any purpose and without fee is hereby granted, 35.10 +# provided that the above copyright notice appear in all copies and that 35.11 +# both that copyright notice and this permission notice appear in 35.12 +# supporting documentation, and that the name of Vinay Sajip 35.13 +# not be used in advertising or publicity pertaining to distribution 35.14 +# of the software without specific, written prior permission. 35.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 35.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 35.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 35.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 35.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 35.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 35.21 +# 35.22 +# This file is part of the Python logging distribution. See 35.23 +# http://www.red-dove.com/python_logging.html 35.24 +# 35.25 +""" 35.26 +A test harness for the logging module. Tests arbitrary logging levels, filtering, and 35.27 +use of strftime formatting. 35.28 + 35.29 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 35.30 +""" 35.31 + 35.32 +import logging 35.33 +import locale, sys 35.34 + 35.35 +locale.setlocale(locale.LC_ALL, '') 35.36 + 35.37 +def message(s): 35.38 + sys.stderr.write("%s\n" % s) 35.39 + 35.40 +# 35.41 +# First, we define our levels. There can be as many as you want - the only limitations are that 35.42 +# they should be integers, the lowest should be > 0 and larger values mean less information being 35.43 +# logged. If you need specific level values which do not fit into these limitations, you can use 35.44 +# a mapping dictionary to convert between your application levels and the logging system. 35.45 +# 35.46 +SILENT = 10 35.47 +TACITURN = 9 35.48 +TERSE = 8 35.49 +EFFUSIVE = 7 35.50 +SOCIABLE = 6 35.51 +VERBOSE = 5 35.52 +TALKATIVE = 4 35.53 +GARRULOUS = 3 35.54 +CHATTERBOX = 2 35.55 +BORING = 1 35.56 + 35.57 +LEVEL_RANGE = range(BORING, SILENT + 1) 35.58 + 35.59 +# 35.60 +# Next, we define names for our levels. You don't need to do this - in which case the system will 35.61 +# use "Level n" to denote the text for the level. 35.62 +# 35.63 +my_logging_levels = { 35.64 + SILENT : 'Silent', 35.65 + TACITURN : 'Taciturn', 35.66 + TERSE : 'Terse', 35.67 + EFFUSIVE : 'Effusive', 35.68 + SOCIABLE : 'Sociable', 35.69 + VERBOSE : 'Verbose', 35.70 + TALKATIVE : 'Talkative', 35.71 + GARRULOUS : 'Garrulous', 35.72 + CHATTERBOX: 'Chatterbox', 35.73 + BORING : 'Boring', 35.74 +} 35.75 + 35.76 +# 35.77 +# Now, tell the logging system to associate names with our levels. 35.78 +# 35.79 +for lvl in my_logging_levels.keys(): 35.80 + logging.addLevelName(lvl, my_logging_levels[lvl]) 35.81 + 35.82 +# 35.83 +# Now, define a test function which logs an event at each of our levels. 35.84 +# 35.85 +def doLog(log): 35.86 + for lvl in LEVEL_RANGE: 35.87 + log.log(lvl, "This should only be seen at the '%s' logging level (or lower)", logging.getLevelName(lvl)) 35.88 + 35.89 +# 35.90 +# Get the root logger and add a console hander to it, when run as a script. 35.91 +# 35.92 +log = logging.getLogger("") 35.93 + 35.94 +if __name__ == "__main__": 35.95 + hdlr = logging.StreamHandler() 35.96 + hdlr.setFormatter(logging.Formatter("%(asctime)s %(message)s", "%X")) #date format is as per the locale 35.97 + log.addHandler(hdlr) 35.98 +else: 35.99 + hdlr = log.handlers[0] 35.100 +# 35.101 +# Set the logging level to each different value and call the utility function to log events. 35.102 +# In the output, you should see that each time round the loop, the logging events actually output 35.103 +# decreases. 35.104 +# 35.105 +for lvl in LEVEL_RANGE: 35.106 + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) 35.107 + log.setLevel(lvl) 35.108 + doLog(log) 35.109 +# 35.110 +# Now, we demonstrate level filtering at the handler level. Tell the handler defined above 35.111 +# to filter at level 'SOCIABLE', and repeat the above loop. Compare the output from the two runs. 35.112 +# 35.113 +hdlr.setLevel(SOCIABLE) 35.114 +message("-- Filtering at handler level to SOCIABLE --") 35.115 +for lvl in LEVEL_RANGE: 35.116 + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) 35.117 + log.setLevel(lvl) 35.118 + doLog(log) 35.119 + 35.120 +hdlr.setLevel(0) #turn off level filtering at the handler 35.121 + 35.122 +# 35.123 +# Now, let's demonstrate filtering. Suppose for some perverse reason we only want to print out 35.124 +# all except GARRULOUS messages. Let's create a filter for this purpose... 35.125 +# 35.126 +class SpecificLevelFilter(logging.Filter): 35.127 + def __init__(self, lvl): 35.128 + self.level = lvl 35.129 + 35.130 + def filter(self, record): 35.131 + return self.level != record.levelno 35.132 + 35.133 +class GarrulousFilter(SpecificLevelFilter): 35.134 + def __init__(self): 35.135 + SpecificLevelFilter.__init__(self, GARRULOUS) 35.136 + 35.137 +garr = GarrulousFilter() 35.138 +hdlr.addFilter(garr) 35.139 +message("-- Filtering using GARRULOUS filter --") 35.140 +for lvl in LEVEL_RANGE: 35.141 + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) 35.142 + log.setLevel(lvl) 35.143 + doLog(log) 35.144 +# 35.145 +# Now, let's demonstrate filtering at the logger. This time, use a filter which excludes SOCIABLE 35.146 +# and TACITURN messages. Note that GARRULOUS events are still excluded. 35.147 +# 35.148 +class VerySpecificFilter(logging.Filter): 35.149 + def filter(self, record): 35.150 + return record.levelno not in [SOCIABLE, TACITURN] 35.151 + 35.152 +spec = VerySpecificFilter() 35.153 +log.addFilter(spec) 35.154 +message("-- Filtering using specific filter for SOCIABLE, TACITURN --") 35.155 +for lvl in LEVEL_RANGE: 35.156 + message("-- setting logging level to '%s' -----" % logging.getLevelName(lvl)) 35.157 + log.setLevel(lvl) 35.158 + doLog(log) 35.159 + 35.160 +log.removeFilter(spec) 35.161 +hdlr.removeFilter(garr) 35.162 +#Undo the one level which clashes...for regression tests 35.163 +logging.addLevelName(logging.DEBUG, "DEBUG") 35.164 + 35.165 +# 35.166 +# Er...that's it for now 35.167 +# 35.168 +if __name__ == "__main__": 35.169 + print "All done." 35.170 + logging.shutdown() 35.171 +
36.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 36.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test5.py Thu Jul 22 13:42:39 2004 +0000 36.3 @@ -0,0 +1,44 @@ 36.4 +#!/usr/bin/env python 36.5 +# 36.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 36.7 +# 36.8 +# Permission to use, copy, modify, and distribute this software and its 36.9 +# documentation for any purpose and without fee is hereby granted, 36.10 +# provided that the above copyright notice appear in all copies and that 36.11 +# both that copyright notice and this permission notice appear in 36.12 +# supporting documentation, and that the name of Vinay Sajip 36.13 +# not be used in advertising or publicity pertaining to distribution 36.14 +# of the software without specific, written prior permission. 36.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 36.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 36.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 36.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 36.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 36.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 36.21 +# 36.22 +# This file is part of the Python logging distribution. See 36.23 +# http://www.red-dove.com/python_logging.html 36.24 +# 36.25 +""" 36.26 +A test harness for the logging module. Tests SMTPHandler. 36.27 + 36.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 36.29 +""" 36.30 +import logging, logging.handlers 36.31 + 36.32 +MAILHOST = 'beta' 36.33 +FROM = 'log_test5@yourdomain.com' 36.34 +TO = ['arkadi_renko'] 36.35 +SUBJECT = 'Test Logging email from Python logging module (non-buffering)' 36.36 + 36.37 +def main(): 36.38 + log = logging.getLogger("") 36.39 + log.setLevel(logging.DEBUG) 36.40 + hdlr = logging.handlers.SMTPHandler(MAILHOST, FROM, TO, SUBJECT) 36.41 + hdlr.setFormatter(logging.Formatter("%(asctime)s %(levelname)-5s %(message)s")) 36.42 + log.addHandler(hdlr) 36.43 + log.info("Test email contents") 36.44 + log.removeHandler(hdlr) 36.45 + 36.46 +if __name__ == "__main__": 36.47 + main() 36.48 \ No newline at end of file
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 37.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test6.py Thu Jul 22 13:42:39 2004 +0000 37.3 @@ -0,0 +1,47 @@ 37.4 +#!/usr/bin/env python 37.5 +# 37.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 37.7 +# 37.8 +# Permission to use, copy, modify, and distribute this software and its 37.9 +# documentation for any purpose and without fee is hereby granted, 37.10 +# provided that the above copyright notice appear in all copies and that 37.11 +# both that copyright notice and this permission notice appear in 37.12 +# supporting documentation, and that the name of Vinay Sajip 37.13 +# not be used in advertising or publicity pertaining to distribution 37.14 +# of the software without specific, written prior permission. 37.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 37.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 37.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 37.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 37.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 37.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 37.21 +# 37.22 +# This file is part of the Python logging distribution. See 37.23 +# http://www.red-dove.com/python_logging.html 37.24 +# 37.25 +""" 37.26 +A test harness for the logging module. Tests NTEventLogHandler. 37.27 + 37.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 37.29 +""" 37.30 +import logging, logging.handlers 37.31 + 37.32 +def main(): 37.33 + ntl = logging.handlers.NTEventLogHandler("Python Logging Test") 37.34 + logger = logging.getLogger("") 37.35 + logger.setLevel(logging.DEBUG) 37.36 + logger.addHandler(ntl) 37.37 + logger.debug("This is a '%s' message", "Debug") 37.38 + logger.info("This is a '%s' message", "Info") 37.39 + logger.warning("This is a '%s' message", "Warning") 37.40 + logger.error("This is a '%s' message", "Error") 37.41 + logger.critical("This is a '%s' message", "Critical") 37.42 + try: 37.43 + x = 4 / 0 37.44 + except: 37.45 + logger.info("This is an %s (or should that be %s?)", "informational exception", "exceptional information", exc_info=1) 37.46 + logger.exception("This is the same stuff, via a %s", "exception() call") 37.47 + logger.removeHandler(ntl) 37.48 + 37.49 +if __name__ == "__main__": 37.50 + main() 37.51 \ No newline at end of file
38.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 38.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test7.py Thu Jul 22 13:42:39 2004 +0000 38.3 @@ -0,0 +1,48 @@ 38.4 +#!/usr/bin/env python 38.5 +# 38.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 38.7 +# 38.8 +# Permission to use, copy, modify, and distribute this software and its 38.9 +# documentation for any purpose and without fee is hereby granted, 38.10 +# provided that the above copyright notice appear in all copies and that 38.11 +# both that copyright notice and this permission notice appear in 38.12 +# supporting documentation, and that the name of Vinay Sajip 38.13 +# not be used in advertising or publicity pertaining to distribution 38.14 +# of the software without specific, written prior permission. 38.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 38.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 38.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 38.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 38.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 38.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 38.21 +# 38.22 +# This file is part of the Python logging distribution. See 38.23 +# http://www.red-dove.com/python_logging.html 38.24 +# 38.25 +""" 38.26 +A test harness for the logging module. Tests MemoryHandler. 38.27 + 38.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 38.29 +""" 38.30 +import sys, logging, logging.handlers 38.31 + 38.32 +def message(s): 38.33 + sys.stderr.write("%s\n" % s) 38.34 + 38.35 +sh = logging.StreamHandler() 38.36 +mh = logging.handlers.MemoryHandler(10,logging.WARNING, sh) 38.37 +logger = logging.getLogger("") 38.38 +logger.setLevel(logging.DEBUG) 38.39 +logger.addHandler(mh) 38.40 +message("-- logging at DEBUG, nothing should be seen yet --") 38.41 +logger.debug("Debug message") 38.42 +message("-- logging at INFO, nothing should be seen yet --") 38.43 +logger.info("Info message") 38.44 +message("-- logging at WARNING, 3 messages should be seen --") 38.45 +logger.warning("Warning message") 38.46 +for i in xrange(102): 38.47 + message("-- logging %d at level INFO, messages should be seen every 10 events --" % i) 38.48 + logger.info("Info index = %d", i) 38.49 +sh.close() 38.50 +mh.close() 38.51 +logger.removeHandler(mh) 38.52 \ No newline at end of file
39.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 39.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test8.py Thu Jul 22 13:42:39 2004 +0000 39.3 @@ -0,0 +1,69 @@ 39.4 +#!/usr/bin/env python 39.5 +# 39.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 39.7 +# 39.8 +# Permission to use, copy, modify, and distribute this software and its 39.9 +# documentation for any purpose and without fee is hereby granted, 39.10 +# provided that the above copyright notice appear in all copies and that 39.11 +# both that copyright notice and this permission notice appear in 39.12 +# supporting documentation, and that the name of Vinay Sajip 39.13 +# not be used in advertising or publicity pertaining to distribution 39.14 +# of the software without specific, written prior permission. 39.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 39.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 39.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 39.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 39.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 39.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 39.21 +# 39.22 +# This file is part of the Python logging distribution. See 39.23 +# http://www.red-dove.com/python_logging.html 39.24 +# 39.25 +""" 39.26 +A test harness for the logging module. Tests FileHandler rollover. 39.27 + 39.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 39.29 +""" 39.30 +import logging, logging.handlers 39.31 +import locale 39.32 + 39.33 +locale.setlocale(locale.LC_ALL, '') 39.34 + 39.35 +sequence = 0 39.36 + 39.37 +def doLog(logger): 39.38 + global sequence 39.39 + sequence = sequence + 1 39.40 + logger.debug("%6d This message should be at level %d - %s", sequence,\ 39.41 + logging.DEBUG, logging.getLevelName(logging.DEBUG)) 39.42 + sequence = sequence + 1 39.43 + logger.info("%6d This message should be at level %d - %s", sequence, 39.44 + logging.INFO, logging.getLevelName(logging.INFO)) 39.45 + sequence = sequence + 1 39.46 + logger.warning("%6d This message should be at level %d - %s", sequence,\ 39.47 + logging.WARNING, logging.getLevelName(logging.WARNING)) 39.48 + sequence = sequence + 1 39.49 + logger.error("%6d This message should be at level %d - %s", sequence,\ 39.50 + logging.ERROR, logging.getLevelName(logging.ERROR)) 39.51 + sequence = sequence + 1 39.52 + logger.critical("%6d This message should be at level %d - %s", sequence,\ 39.53 + logging.CRITICAL, logging.getLevelName(logging.CRITICAL)) 39.54 + 39.55 +def main(): 39.56 + logger = logging.getLogger("") #root logger 39.57 + logger.setLevel(logging.DEBUG) 39.58 + if __name__ == "__main__": 39.59 + logname = "rollover.log" 39.60 + else: 39.61 + logname = "log_test_rollover.log" 39.62 + hdlr = logging.handlers.RotatingFileHandler(logname, "a", 5000, 3) 39.63 + if __name__ == "__main__": 39.64 + fmt = logging.Formatter("%(asctime)s %(levelname)-5s %(message)s", "%x %X") 39.65 + hdlr.setFormatter(fmt) 39.66 + logger.addHandler(hdlr) 39.67 + for i in xrange(100): 39.68 + doLog(logger) 39.69 + logger.removeHandler(hdlr) 39.70 + 39.71 +if __name__ == "__main__": 39.72 + main() 39.73 \ No newline at end of file
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 40.2 +++ b/tools/python/logging/logging-0.4.9.2/test/log_test9.py Thu Jul 22 13:42:39 2004 +0000 40.3 @@ -0,0 +1,71 @@ 40.4 +#!/usr/bin/env python 40.5 +# 40.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 40.7 +# 40.8 +# Permission to use, copy, modify, and distribute this software and its 40.9 +# documentation for any purpose and without fee is hereby granted, 40.10 +# provided that the above copyright notice appear in all copies and that 40.11 +# both that copyright notice and this permission notice appear in 40.12 +# supporting documentation, and that the name of Vinay Sajip 40.13 +# not be used in advertising or publicity pertaining to distribution 40.14 +# of the software without specific, written prior permission. 40.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 40.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 40.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 40.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 40.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 40.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 40.21 +# 40.22 +# This file is part of the Python logging distribution. See 40.23 +# http://www.red-dove.com/python_logging.html 40.24 +# 40.25 +""" 40.26 +A test harness for the logging module. Tests BufferingHandler, BufferingFormatter. 40.27 + 40.28 +Copyright (C) 2001-2002 Vinay Sajip. All Rights Reserved. 40.29 +""" 40.30 +import logging, logging.handlers 40.31 + 40.32 +class XMLFormatter(logging.BufferingFormatter): 40.33 + """ 40.34 + A formatter which formats a set of records using XML, using an example DTD called "logging.dtd". 40.35 + """ 40.36 + def __init__(self): 40.37 + fmtstr = """ 40.38 + <event name="%(name)s" level="%(levelno)d" filename="%(filename)s" lineno="%(lineno)d"> 40.39 + <message>%(message)s</message> 40.40 + </event>""" 40.41 + logging.BufferingFormatter.__init__(self, logging.Formatter(fmtstr)) 40.42 + 40.43 +# def formatHeader(self, records): 40.44 +# return """ 40.45 +#<?xml version="1.0" ?><!DOCTYPE eventSet SYSTEM "logging.dtd"> 40.46 +#<eventSet xmlns="http://www.red-dove.com/logging">""" 40.47 +# 40.48 +# def formatFooter(self, records): 40.49 +# return "</eventSet>" 40.50 + 40.51 +class XMLHandler(logging.handlers.BufferingHandler): 40.52 + def __init__(self, capacity): 40.53 + logging.handlers.BufferingHandler.__init__(self, capacity) 40.54 + self.setFormatter(XMLFormatter()) 40.55 + 40.56 + def flush(self): 40.57 + if len(self.buffer) > 0: 40.58 + file = open("events.xml","w") 40.59 + file.write(self.formatter.format(self.buffer)) 40.60 + file.close() 40.61 + self.buffer = [] 40.62 + 40.63 +def main(): 40.64 + logger = logging.getLogger("") 40.65 + logger.setLevel(logging.DEBUG) 40.66 + xh = XMLHandler(10) 40.67 + logger.addHandler(xh) 40.68 + for i in xrange(100): 40.69 + logger.info("Info index = %d", i) 40.70 + xh.close() 40.71 + logger.removeHandler(xh) 40.72 + 40.73 +if __name__ == "__main__": 40.74 + main()
41.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 41.2 +++ b/tools/python/logging/logging-0.4.9.2/test/logconf.ini Thu Jul 22 13:42:39 2004 +0000 41.3 @@ -0,0 +1,180 @@ 41.4 +[loggers] 41.5 +keys=root,log02,log03,log04,log05,log06,log07 41.6 + 41.7 +[handlers] 41.8 +keys=hand01,hand02,hand03,hand04,hand05,hand06,hand07,hand08,hand09 41.9 + 41.10 +[formatters] 41.11 +keys=form01,form02,form03,form04,form05,form06,form07,form08,form09 41.12 + 41.13 +[logger_root] 41.14 +level=NOTSET 41.15 +propagate=1 41.16 +channel= 41.17 +parent= 41.18 +qualname=(root) 41.19 +handlers=hand01 41.20 + 41.21 +[logger_log02] 41.22 +level=DEBUG 41.23 +propagate=1 41.24 +channel=log02 41.25 +parent=(root) 41.26 +qualname=log02 41.27 +handlers=hand02 41.28 + 41.29 +[logger_log03] 41.30 +level=INFO 41.31 +propagate=1 41.32 +channel=log03 41.33 +parent=log02 41.34 +qualname=log02.log03 41.35 +handlers=hand03 41.36 + 41.37 +[logger_log04] 41.38 +level=WARN 41.39 +propagate=0 41.40 +channel=log04 41.41 +parent=log03 41.42 +qualname=log02.log03.log04 41.43 +handlers=hand04 41.44 + 41.45 +[logger_log05] 41.46 +level=ERROR 41.47 +propagate=1 41.48 +channel=log05 41.49 +parent=log04 41.50 +qualname=log02.log03.log04.log05 41.51 +handlers=hand05 41.52 + 41.53 +[logger_log06] 41.54 +level=CRITICAL 41.55 +propagate=1 41.56 +channel=log06 41.57 +parent=log05 41.58 +qualname=log02.log03.log04.log05.log06 41.59 +handlers=hand06 41.60 + 41.61 +[logger_log07] 41.62 +level=WARN 41.63 +propagate=1 41.64 +channel=log07 41.65 +parent=log06 41.66 +qualname=log02.log03.log04.log05.log06.log07 41.67 +handlers=hand07 41.68 + 41.69 +[handler_hand01] 41.70 +class=StreamHandler 41.71 +level=NOTSET 41.72 +formatter=form01 41.73 +stream=sys.stdout 41.74 +args=(sys.stdout,) 41.75 + 41.76 +[handler_hand02] 41.77 +class=FileHandler 41.78 +level=DEBUG 41.79 +formatter=form02 41.80 +filename=python.log 41.81 +mode=w 41.82 +args=('python.log', 'w') 41.83 + 41.84 +[handler_hand03] 41.85 +class=handlers.SocketHandler 41.86 +level=INFO 41.87 +formatter=form03 41.88 +host=localhost 41.89 +port=handlers.DEFAULT_TCP_LOGGING_PORT 41.90 +args=('localhost', handlers.DEFAULT_TCP_LOGGING_PORT) 41.91 + 41.92 +[handler_hand04] 41.93 +class=handlers.DatagramHandler 41.94 +level=WARN 41.95 +formatter=form04 41.96 +host=localhost 41.97 +port=handlers.DEFAULT_UDP_LOGGING_PORT 41.98 +args=('localhost', handlers.DEFAULT_UDP_LOGGING_PORT) 41.99 + 41.100 +[handler_hand05] 41.101 +class=handlers.SysLogHandler 41.102 +level=ERROR 41.103 +formatter=form05 41.104 +host=localhost 41.105 +port=handlers.SYSLOG_UDP_PORT 41.106 +facility=LOG_USER 41.107 +args=(('localhost', handlers.SYSLOG_UDP_PORT), handlers.SysLogHandler.LOG_USER) 41.108 + 41.109 +[handler_hand06] 41.110 +class=handlers.NTEventLogHandler 41.111 +level=CRITICAL 41.112 +formatter=form06 41.113 +appname=Python Application 41.114 +dllname= 41.115 +logtype=Application 41.116 +args=('Python Application', '', 'Application') 41.117 + 41.118 +[handler_hand07] 41.119 +class=handlers.SMTPHandler 41.120 +level=WARN 41.121 +formatter=form07 41.122 +host=localhost 41.123 +port=25 41.124 +from=from@abc 41.125 +to=user1@abc,user2@xyz 41.126 +subject=Logger Subject 41.127 +args=('localhost', 'from@abc', ['user1@abc', 'user2@xyz'], 'Logger Subject') 41.128 + 41.129 +[handler_hand08] 41.130 +class=handlers.MemoryHandler 41.131 +level=NOTSET 41.132 +formatter=form08 41.133 +capacity=10 41.134 +flushlevel=ERROR 41.135 +target= 41.136 +args=(10, ERROR) 41.137 + 41.138 +[handler_hand09] 41.139 +class=handlers.HTTPHandler 41.140 +level=NOTSET 41.141 +formatter=form09 41.142 +host=localhost 41.143 +port=9022 41.144 +url=/log 41.145 +method=GET 41.146 +args=('localhost:9022', '/log', 'GET') 41.147 + 41.148 +[formatter_form01] 41.149 +format=F1 %(asctime)s %(levelname)s %(message)s 41.150 +datefmt= 41.151 + 41.152 +[formatter_form02] 41.153 +format=F2 %(asctime)s %(pathname)s(%(lineno)d): %(levelname)s %(message)s 41.154 +datefmt= 41.155 + 41.156 +[formatter_form03] 41.157 +format=F3 %(asctime)s %(levelname)s %(message)s 41.158 +datefmt= 41.159 + 41.160 +[formatter_form04] 41.161 +format=%(asctime)s %(levelname)s %(message)s 41.162 +datefmt= 41.163 + 41.164 +[formatter_form05] 41.165 +format=F5 %(asctime)s %(levelname)s %(message)s 41.166 +datefmt= 41.167 + 41.168 +[formatter_form06] 41.169 +format=F6 %(asctime)s %(levelname)s %(message)s 41.170 +datefmt= 41.171 + 41.172 +[formatter_form07] 41.173 +format=F7 %(asctime)s %(levelname)s %(message)s 41.174 +datefmt= 41.175 + 41.176 +[formatter_form08] 41.177 +format=F8 %(asctime)s %(levelname)s %(message)s 41.178 +datefmt= 41.179 + 41.180 +[formatter_form09] 41.181 +format=F9 %(asctime)s %(levelname)s %(message)s 41.182 +datefmt= 41.183 +
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 42.2 +++ b/tools/python/logging/logging-0.4.9.2/test/logconf.py Thu Jul 22 13:42:39 2004 +0000 42.3 @@ -0,0 +1,1738 @@ 42.4 +#!/usr/bin/env python 42.5 +# 42.6 +# Copyright 2001-2002 by Vinay Sajip. All Rights Reserved. 42.7 +# 42.8 +# Permission to use, copy, modify, and distribute this software and its 42.9 +# documentation for any purpose and without fee is hereby granted, 42.10 +# provided that the above copyright notice appear in all copies and that 42.11 +# both that copyright notice and this permission notice appear in 42.12 +# supporting documentation, and that the name of Vinay Sajip 42.13 +# not be used in advertising or publicity pertaining to distribution 42.14 +# of the software without specific, written prior permission. 42.15 +# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 42.16 +# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL 42.17 +# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 42.18 +# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 42.19 +# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 42.20 +# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 42.21 +# 42.22 +# This file is part of the Python logging distribution. See 42.23 +# http://www.red-dove.com/python_logging.html 42.24 +# 42.25 + 42.26 +""" 42.27 +A simple-minded GUI configurator for the logging module, using Tkinter. 42.28 + 42.29 +Should work under Python versions >= 1.5.2. 42.30 + 42.31 +Copyright (C) 2002 Vinay Sajip. All Rights Reserved. 42.32 + 42.33 +Configuration files are read/written using ConfigParser. 42.34 +""" 42.35 +""" 42.36 + 42.37 +(C) 2002 Vinay Sajip. All rights reserved. 42.38 +""" 42.39 +from Tkinter import * 42.40 +from tkFileDialog import * 42.41 +from tkMessageBox import * 42.42 + 42.43 +import os, sys, string, types 42.44 +import ConfigParser 42.45 + 42.46 +active = None 42.47 + 42.48 +__version__ = "0.4.1" 42.49 + 42.50 +DEFAULT_FILENAME = "logconf.ini" 42.51 + 42.52 +LOGGING_LEVELS = ( 42.53 + ("NOTSET", "NOTSET"), 42.54 + ("DEBUG", "DEBUG"), 42.55 + ("INFO", "INFO"), 42.56 + ("WARNING", "WARNING"), 42.57 + ("ERROR", "ERROR"), 42.58 + ("CRITICAL", "CRITICAL") 42.59 +) 42.60 + 42.61 +HANDLER_TYPES = ( 42.62 + ("StreamHandlerProxy", "StreamHandler"), 42.63 + ("FileHandlerProxy", "FileHandler"), 42.64 + ("RotatingFileHandlerProxy", "RotatingFileHandler"), 42.65 + ("SocketHandlerProxy", "SocketHandler"), 42.66 + ("DatagramHandlerProxy", "DatagramHandler"), 42.67 + ("SysLogHandlerProxy", "SysLogHandler"), 42.68 + ("NTEventLogHandlerProxy", "NTEventLogHandler"), 42.69 + ("SMTPHandlerProxy", "SMTPHandler"), 42.70 + ("MemoryHandlerProxy", "MemoryHandler"), 42.71 + ("HTTPHandlerProxy", "HTTPHandler"), 42.72 +# ("SOAPHandlerProxy", "SOAPHandler"), 42.73 +) 42.74 + 42.75 +OUTPUT_STREAMS = ( 42.76 + ("sys.stdout", "sys.stdout"), 42.77 + ("sys.stderr", "sys.stderr") 42.78 +) 42.79 + 42.80 +FILE_MODES = ( 42.81 + ("a", "a"), 42.82 + ("w", "w") 42.83 + ) 42.84 + 42.85 +HTTP_METHODS = ( 42.86 + ("GET", "GET"), 42.87 + ("POST", "POST") 42.88 +) 42.89 + 42.90 +SYSLOG_FACILITIES = ( 42.91 + ("LOG_AUTH", "auth"), 42.92 + ("LOG_AUTHPRIV", "authpriv"), 42.93 + ("LOG_CRON", "cron"), 42.94 + ("LOG_DAEMON", "daemon"), 42.95 + ("LOG_KERN", "kern"), 42.96 + ("LOG_LPR", "lpr"), 42.97 + ("LOG_MAIL", "mail"), 42.98 + ("LOG_NEWS", "news"), 42.99 + ("LOG_AUTH", "security"), 42.100 + ("LOG_SYSLOG", "syslog"), 42.101 + ("LOG_USER", "user"), 42.102 + ("LOG_UUCP", "uucp"), 42.103 + ("LOG_LOCAL0", "local0"), 42.104 + ("LOG_LOCAL1", "local1"), 42.105 + ("LOG_LOCAL2", "local2"), 42.106 + ("LOG_LOCAL3", "local3"), 42.107 + ("LOG_LOCAL4", "local4"), 42.108 + ("LOG_LOCAL5", "local5"), 42.109 + ("LOG_LOCAL6", "local6"), 42.110 + ("LOG_LOCAL7", "local7"), 42.111 +) 42.112 + 42.113 +LOG_TYPES = ( 42.114 + ("Application", "Application"), 42.115 + ("System", "System"), 42.116 + ("Security", "Security") 42.117 +) 42.118 + 42.119 +BOOLEAN_VALUES = ( 42.120 + ("0", "False"), 42.121 + ("1", "True") 42.122 +) 42.123 + 42.124 +class Property: 42.125 + def __init__(self, name, caption, value=None, choices=None): 42.126 + self.name = name 42.127 + self.caption = caption 42.128 + self.value = value 42.129 + self.choices = choices 42.130 + 42.131 + def getChoices(self): 42.132 + return self.choices 42.133 + 42.134 + def isvalid(self, s): 42.135 + return 0 42.136 + 42.137 + def getCaption(self): 42.138 + return self.caption 42.139 + 42.140 + def getValue(self): 42.141 + return self.value 42.142 + 42.143 + def getChoiceText(self, val): 42.144 + rv = "" 42.145 + choices = self.getChoices() 42.146 + if choices: 42.147 + for choice in choices: 42.148 + if choice[0] == val: 42.149 + rv = choice[1] 42.150 + break 42.151 + return rv 42.152 + 42.153 + def setValue(self, val): 42.154 + self.value = val 42.155 + 42.156 + def getValueText(self): 42.157 + if type(self.value) in [types.ListType, types.TupleType]: 42.158 + v = list(self.value) 42.159 + else: 42.160 + v = [self.value] 42.161 + choices = self.getChoices() 42.162 + if choices: 42.163 + v = map(self.getChoiceText, v) 42.164 + return string.join(v, ',') 42.165 + 42.166 +class PropertyHolder: 42.167 + def __init__(self, dict): 42.168 + self.dict = dict 42.169 + self.propnames = [] 42.170 + self.onPropListChanged = None 42.171 + 42.172 + def getPropNames(self): 42.173 + """ 42.174 + Return the property names in the order in which they are to 42.175 + be listed. 42.176 + """ 42.177 + return self.propnames 42.178 + 42.179 + def getProp(self, name): 42.180 + return self.dict[name] 42.181 + 42.182 + def isReadonly(self, name): 42.183 + return 0 42.184 + 42.185 + #convenience methods 42.186 + def getPropValue(self, name): 42.187 + return self.dict[name].value 42.188 + 42.189 + def setPropValue(self, name, value): 42.190 + self.dict[name].setValue(value) 42.191 + 42.192 +LINE_COLOUR = '#999999' 42.193 + 42.194 +class ScrollingList(Frame): 42.195 + def __init__(self, parent, *args, **kwargs): 42.196 + Frame.__init__(self, parent) 42.197 + self.parent = parent 42.198 + self.listener = self.parent 42.199 + self.sb = Scrollbar(self, orient=VERTICAL) 42.200 + kwargs["yscrollcommand"] = self.sb.set 42.201 + self.list = apply(Listbox, (self,) + args, kwargs) 42.202 + self.sb.config(command=self.list.yview) 42.203 + self.sb.pack(side=RIGHT, fill=Y) 42.204 + self.list.pack(side=LEFT, fill=BOTH,expand=1) 42.205 + self.list.bind('<ButtonRelease-1>', self.onListChange) 42.206 + self.choices = None 42.207 + 42.208 + def setContents(self, choices, value): 42.209 + self.choices = choices 42.210 + self.value = value 42.211 + self.list.delete(0, END) 42.212 + if type(value) == types.ListType: 42.213 + sm = EXTENDED 42.214 + else: 42.215 + sm = BROWSE 42.216 + self.list.configure(selectmode=sm) 42.217 + i = 0 42.218 + for choice in choices: 42.219 + self.list.insert(END, choice[1]) 42.220 + if sm == EXTENDED: 42.221 + if choice[0] in value: 42.222 + self.list.select_set(i) 42.223 + else: 42.224 + if choice[0] == value: 42.225 + self.list.select_set(i) 42.226 + i = i + 1 42.227 + 42.228 + def getValue(self): 42.229 + if type(self.value) == types.ListType: 42.230 + multi = 1 42.231 + rv = [] 42.232 + else: 42.233 + multi = 0 42.234 + for i in xrange(len(self.choices)): 42.235 + if self.list.select_includes(i): 42.236 + if not multi: 42.237 + rv = self.choices[i][0] 42.238 + break 42.239 + else: 42.240 + rv.append(self.choices[i][0]) 42.241 + return rv 42.242 + 42.243 + def onListChange(self, event): 42.244 + self.value = self.getValue() 42.245 + self.listener.onListChange(self.value) 42.246 + 42.247 +class PropertyHeader(Canvas): 42.248 + def __init__(self, parent, *args, **kwargs): 42.249 + self.namewidth = 120 42.250 + if kwargs.has_key("namewidth"): 42.251 + self.namewidth = kwargs["namewidth"] 42.252 + del kwargs["namewidth"] 42.253 + self.rowheight = 16 42.254 + if kwargs.has_key("rowheight"): 42.255 + self.rowheight = kwargs["rowheight"] 42.256 + del kwargs["rowheight"] 42.257 + apply(Canvas.__init__, (self, parent)+args, kwargs) 42.258 + self.bind('<Configure>', self.onConfigure) 42.259 + x = 5 42.260 + y = 0 42.261 + wid = int(self.cget('width')) 42.262 + self.create_text(x, y, text='Property', anchor='nw') 42.263 + self.create_text(x + self.namewidth, y, text='Value', anchor='nw') 42.264 + self.create_line(self.namewidth, 0, self.namewidth, self.rowheight, fill=LINE_COLOUR) 42.265 + self.tline = self.create_line(0, 0, wid, 0, fill=LINE_COLOUR) 42.266 + #self.create_line(0, 0, 0, self.rowheight, fill=LINE_COLOUR) 42.267 + #self.create_line(wid - 1, 0, wid - 1, self.rowheight, fill=LINE_COLOUR) 42.268 + 42.269 + def onConfigure(self, event): 42.270 + self.delete(self.tline) 42.271 + self.tline = self.create_line(0, 0, event.width, 0, fill=LINE_COLOUR) 42.272 + 42.273 +_popup = None 42.274 + 42.275 +class PropertyCanvas(Canvas): 42.276 + def __init__(self, parent, *args, **kwargs): 42.277 + self.namewidth = 120 42.278 + if kwargs.has_key("namewidth"): 42.279 + self.namewidth = kwargs["namewidth"] 42.280 + del kwargs["namewidth"] 42.281 + self.rowheight = 16 42.282 + if kwargs.has_key("rowheight"): 42.283 + self.rowheight = kwargs["rowheight"] 42.284 + del kwargs["rowheight"] 42.285 + apply(Canvas.__init__, (self, parent)+args, kwargs) 42.286 + self.namitems = [] 42.287 + self.valitems = [] 42.288 + self.lines = [] 42.289 + self.pnames = [] 42.290 + #Event bindings... 42.291 + self.bind('<Enter>', self.onEnter) 42.292 + self.bind('<Button-1>', self.onClick) 42.293 + self.bind('<Configure>', self.onConfigure) 42.294 + self.button = Button(height=self.rowheight, width=self.rowheight, text='...', command=self.onEdit) 42.295 + self.btnitem = None 42.296 + self.editor = Entry() 42.297 + self.edititem = None 42.298 + self.popup = Toplevel() 42.299 + self.popup.withdraw() 42.300 + self.popup.overrideredirect(1) 42.301 + self.list = ScrollingList(self.popup, background='white', relief=FLAT, borderwidth=0) 42.302 + self.list.pack(fill=BOTH, expand=1) 42.303 + self.list.listener = self 42.304 + self.listvisible = 0 42.305 + 42.306 + def clear(self): 42.307 + for itm in self.namitems: 42.308 + self.delete(itm) 42.309 + self.namitems = [] 42.310 + for itm in self.valitems: 42.311 + self.delete(itm) 42.312 + self.valitems = [] 42.313 + for lin in self.lines: 42.314 + self.delete(lin) 42.315 + self.lines = [] 42.316 + 42.317 + def setPropertyHolder(self, ph): 42.318 + self.ph = ph 42.319 + self.pnames = ph.getPropNames() 42.320 + wid = int(self.cget('width')) 42.321 + hei = int(self.cget('height')) 42.322 + self.clear() 42.323 + x = 5 42.324 + y = 0 42.325 + i = 0 42.326 + self.props = [] 42.327 + for n in self.pnames: 42.328 + prop = self.ph.getProp(n) 42.329 + self.props.append(prop) 42.330 + tn = "n%d" % i 42.331 + tv = "v%d" % i 42.332 + self.namitems.append(self.create_text(x, y + 2, text=prop.getCaption(), anchor='nw', tags=tn)) 42.333 + self.valitems.append(self.create_text(x + self.namewidth, y + 2, text=prop.getValueText(), anchor='nw', tags=tv)) 42.334 + y = y + self.rowheight 42.335 + i = i + 1 42.336 + self.drawLines(wid, hei) 42.337 + #self.config(height=y) 42.338 + 42.339 + def drawLines(self, wid, hei): 42.340 + for lin in self.lines: 42.341 + self.delete(lin) 42.342 + self.lines = [] 42.343 + y = 0 42.344 + for i in xrange(len(self.pnames)): 42.345 + self.lines.append(self.create_line(0, y, wid, y, fill=LINE_COLOUR)) 42.346 + y = y + self.rowheight 42.347 + self.lines.append(self.create_line(0, y, wid, y, fill=LINE_COLOUR)) 42.348 + self.create_line(self.namewidth, 0, self.namewidth, hei, fill=LINE_COLOUR) 42.349 + 42.350 + def onEnter(self, event): 42.351 + if not self.edititem and not self.listvisible: 42.352 + self.focus_set() 42.353 + 42.354 + def hideControls(self): 42.355 + if self.listvisible: 42.356 + self.popup.withdraw() 42.357 + global _popup 42.358 + _popup = None 42.359 + self.listvisible = 0 42.360 + if self.edititem: 42.361 + self.ph.setPropValue(self.editprop.name, self.editor.get()) 42.362 + self.itemconfig(self.valitems[self.editrow], text=self.editprop.getValueText()) 42.363 + self.delete(self.edititem) 42.364 + self.edititem = None 42.365 + if self.btnitem: 42.366 + self.delete(self.btnitem) 42.367 + self.btnitem = None 42.368 + 42.369 + def onClick(self, event): 42.370 + row = event.y / self.rowheight 42.371 + self.hideControls() 42.372 + if row < len(self.pnames): 42.373 + wid = int(self.cget('width')) 42.374 + hei = self.rowheight 42.375 + prop = self.props[row] 42.376 + if not self.ph.isReadonly(self.pnames[row]): 42.377 + self.editrow = row 42.378 + self.editprop = prop 42.379 + choices = prop.getChoices() 42.380 + if choices != None: 42.381 + val = prop.getValue() 42.382 + self.list.setContents(choices, val) 42.383 + self.listy = row * hei + self.rowheight 42.384 + self.btnitem = self.create_window(wid - hei, row * hei, width=hei, height=hei, window=self.button, anchor='nw', tags='button') 42.385 + else: 42.386 + self.editor.delete(0, END) 42.387 + self.editor.insert(0, prop.getValueText()) 42.388 + self.editor.select_range(0, END) 42.389 + self.edititem = self.create_window(self.namewidth + 1, row * hei, width=wid - self.namewidth, height = hei + 1, window=self.editor, anchor='nw', tags='editor') 42.390 + self.editor.focus_set() 42.391 + 42.392 + def onConfigure(self, event): 42.393 + self.hideControls() 42.394 + self.drawLines(event.width, event.height) 42.395 + self.configure(width=event.width, height=event.height) 42.396 + 42.397 + def onEdit(self): 42.398 + wid = int(self.cget('width')) 42.399 + #self.listitem = self.create_window(self.namewidth + 1, self.listy, width=wid - self.namewidth - 1, height = self.rowheight * 3, window=self.list, anchor='nw', tags='list') 42.400 + w = wid - self.namewidth - 1 42.401 + h = self.rowheight * 5 42.402 + x = self.winfo_rootx() + self.namewidth + 1 42.403 + y = self.winfo_rooty() + self.listy 42.404 + s = "%dx%d+%d+%d" % (w, h, x, y) 42.405 + self.popup.deiconify() 42.406 + self.popup.lift() 42.407 + self.popup.focus_set() 42.408 + self.listvisible = 1 42.409 + self.list.focus_set() 42.410 + #For some reason with 1.5.2 (Windows), making the geometry call 42.411 + #immediately following the assignment to s doesn't work. So we 42.412 + #do it here 42.413 + self.popup.geometry(s) 42.414 + global _popup 42.415 + _popup = self.popup 42.416 + 42.417 + def onListChange(self, val): 42.418 + self.ph.setPropValue(self.editprop.name, val) 42.419 + self.itemconfig(self.valitems[self.editrow], text=self.editprop.getValueText()) 42.420 + if type(val) != types.ListType: 42.421 + self.hideControls() 42.422 + 42.423 +class PropertyEditor(Frame): 42.424 + def __init__(self, parent, *args, **kwargs): 42.425 + Frame.__init__(self, parent) 42.426 + self.parent = parent 42.427 + nw = kwargs.get("namewidth", 120) 42.428 + rh = kwargs.get("rowheight", 16) 42.429 + wid = kwargs.get("width", 300) 42.430 + hei = kwargs.get("height", 60) 42.431 + self.header = PropertyHeader(self, namewidth=nw, rowheight=rh, height=14, highlightthickness=0) 42.432 + self.body = PropertyCanvas(self, namewidth=nw, rowheight=rh, width=wid, height=hei, background='white', highlightthickness=0) 42.433 + self.header.pack(side=TOP, fill=X) 42.434 + self.body.pack(side=BOTTOM, fill=BOTH, expand=1) 42.435 + 42.436 + def setPropertyHolder(self, ph): 42.437 + self.body.setPropertyHolder(ph) 42.438 + 42.439 +class ADUPanel(Frame): 42.440 + def __init__(self, parent): 42.441 + Frame.__init__(self, parent) 42.442 + self.parent = parent 42.443 + self.add = Button(self, text="New", command=parent.onAdd) 42.444 + self.add.pack(side=LEFT) #, fill=X, expand=1) 42.445 + self.rmv = Button(self, text="Delete", command=parent.onDelete) 42.446 + self.rmv.pack(side=LEFT) #, fill=X, expand=1) 42.447 + #self.upd = Button(self, text="Update", command=parent.onUpdate) 42.448 + #self.upd.pack(side=RIGHT, fill=X, expand=1) 42.449 + 42.450 +class ScrollList(Frame): 42.451 + def __init__(self, parent, *args, **kwargs): 42.452 + Frame.__init__(self, parent) 42.453 + self.parent = parent 42.454 + self.sb = Scrollbar(self, orient=VERTICAL) 42.455 + kwargs["yscrollcommand"] = self.sb.set 42.456 + self.list = apply(Listbox, (self,) + args, kwargs) 42.457 + self.sb.config(command=self.list.yview) 42.458 + self.sb.pack(side=RIGHT, fill=Y) 42.459 + self.list.pack(side=LEFT, fill=BOTH,expand=1) 42.460 + 42.461 +def sortqn(log1, log2): 42.462 + qn1 = log1.getQualifiedName() 42.463 + qn2 = log2.getQualifiedName() 42.464 + if qn1 == "(root)": 42.465 + rv = -1 42.466 + elif qn2 == "(root)": 42.467 + rv = 1 42.468 + else: 42.469 + rv = cmp(qn1, qn2) 42.470 + return rv 42.471 + 42.472 +def sortn(obj1, obj2): 42.473 + return cmp(obj1.getPropValue("name"), obj2.getPropValue("name")) 42.474 + 42.475 +class LoggerPanel(Frame): 42.476 + def __init__(self, parent): 42.477 + Frame.__init__(self, parent) 42.478 + self.parent = parent 42.479 + label = Label(self, text="Loggers:") 42.480 + label.grid(row=0, column=0, sticky='w') 42.481 + self.slist = ScrollList(self, height=15, background='white') 42.482 + self.slist.list.bind('<ButtonRelease-1>', self.onListChange) 42.483 + self.slist.grid(row=1, column=0, sticky="nsew") 42.484 + self.adu = ADUPanel(self) 42.485 + self.adu.grid(row=2, column=0, sticky="we") 42.486 + label = Label(self, text="Properties of selected logger:") 42.487 + label.grid(row=3, column=0, sticky='w') 42.488 + self.pe = PropertyEditor(self, height=120, borderwidth=1) 42.489 + self.pe.grid(row=4, column=0, sticky='nsew') 42.490 + self.columnconfigure(0, weight=1) 42.491 + self.rowconfigure(1, weight=3) 42.492 + self.rowconfigure(4, weight=1) 42.493 + 42.494 + def setConfig(self, config): 42.495 + self.config = config 42.496 + #populate list of loggers 42.497 + llist = config.getLoggers() 42.498 + llist.sort(sortqn) 42.499 + self.slist.list.delete(0, END) 42.500 + self.pe.body.clear() 42.501 + self.names = [] 42.502 + for logger in llist: 42.503 + self.names.append(logger.getPropValue("name")) 42.504 + self.slist.list.insert(END, logger.getQualifiedName()) 42.505 + 42.506 + def onAdd(self): 42.507 + items = self.slist.list.curselection() 42.508 + if not len(items): 42.509 + showerror("No Parent Selected", "You haven't selected a parent logger.") 42.510 + else: 42.511 + idx = int(items[0]) 42.512 + parent = self.config.getLogger(self.names[idx]) 42.513 + log = self.config.getLogger(None) 42.514 + log.onChannelChanged = self.onChannelChanged 42.515 + log.setPropValue("parent", parent.getPropValue("name")) 42.516 + self.names.insert(1 + idx, log.getPropValue("name")) 42.517 + self.slist.list.insert(1 + idx, log.getQualifiedName()) 42.518 + self.slist.list.select_clear(0, END) 42.519 + self.slist.list.select_set(1 + idx) 42.520 + self.pe.setPropertyHolder(log) 42.521 + 42.522 + def onDelete(self): 42.523 + items = self.slist.list.curselection() 42.524 + if not len(items): 42.525 + showerror("No Item Selected", "You haven't selected anything to delete.") 42.526 + else: 42.527 + idx = int(items[0]) 42.528 + name = self.slist.list.get(idx) 42.529 + if name == "(root)": 42.530 + showerror("Root Item Selected", "You cannot delete the root logger.") 42.531 + else: 42.532 + resp = askyesno("Logger Deletion", "Are you sure you want to delete logger '%s'?" % name) 42.533 + if resp: 42.534 + #self.config.removeLogger(self.names[idx]) 42.535 + log = self.config.getLogger(self.names[idx]) 42.536 + log.deleted = 1 42.537 + self.slist.list.delete(idx) 42.538 + del self.names[idx] 42.539 + self.pe.body.clear() 42.540 + 42.541 + def onChannelChanged(self, nm, chname): 42.542 + i = self.names.index(nm) 42.543 + sel = i 42.544 + while i < len(self.names): 42.545 + log = self.config.getLogger(self.names[i]) 42.546 + self.slist.list.delete(i) 42.547 + self.slist.list.insert(i, log.getQualifiedName()) 42.548 + i = i + 1 42.549 + self.slist.list.select_clear(0, END) 42.550 + self.slist.list.select_set(sel) 42.551 + 42.552 + def onListChange(self, event): 42.553 + self.pe.body.hideControls() 42.554 + items = self.slist.list.curselection() 42.555 + idx = int(items[0]) 42.556 + name = self.names[idx] 42.557 + log = self.config.getLogger(name) 42.558 + self.pe.setPropertyHolder(log) 42.559 + 42.560 +class HandlerPanel(Frame): 42.561 + def __init__(self, parent): 42.562 + Frame.__init__(self, parent) 42.563 + self.parent = parent 42.564 + label = Label(self, text="Handlers:") 42.565 + label.grid(row=0, column=0, sticky='w') 42.566 + self.slist = ScrollList(self, height=6, background='white') 42.567 + self.slist.list.bind('<ButtonRelease-1>', self.onListChange) 42.568 + self.slist.grid(row=1, column=0, sticky="nsew") 42.569 + self.adu = ADUPanel(self) 42.570 + self.adu.grid(row=2, column=0, sticky="we") 42.571 + label = Label(self, text="Properties of selected handler:") 42.572 + label.grid(row=3, column=0, sticky='w') 42.573 + self.pe = PropertyEditor(self, height=90, borderwidth=1) 42.574 + self.pe.grid(row=4, column=0, sticky='nsew') 42.575 + self.columnconfigure(0, weight=1) 42.576 + self.rowconfigure(1, weight=1) 42.577 + self.rowconfigure(4, weight=1) 42.578 + 42.579 + def setConfig(self, config): 42.580 + self.config = config 42.581 + #populate list of handlers 42.582 + hlist = config.getHandlers() 42.583 + hlist.sort(sortn) 42.584 + self.slist.list.delete(0, END) 42.585 + self.pe.body.clear() 42.586 + for hand in hlist: 42.587 + hand.onPropListChanged = self.onPropListChanged 42.588 + self.slist.list.insert(END, hand.getPropValue("name")) 42.589 + 42.590 + def onAdd(self): 42.591 + self.pe.body.hideControls() 42.592 + hand = self.config.getHandler(None) 42.593 + self.slist.list.insert(END, hand.getProp("name").getValueText()) 42.594 + self.slist.list.select_clear(0, END) 42.595 + self.slist.list.select_set(END) 42.596 + hand.onPropListChanged = self.onPropListChanged 42.597 + self.pe.setPropertyHolder(hand) 42.598 + 42.599 + def onDelete(self): 42.600 + items = self.slist.list.curselection() 42.601 + if not len(items): 42.602 + showerror("No Item Selected", "You haven't selected anything to delete") 42.603 + else: 42.604 + name = self.slist.list.get(int(items[0])) 42.605 + log = self.config.handlerIsUsed(name) 42.606 + if log: 42.607 + showerror("Handler in use", 42.608 + "The handler '%s' is being used by logger '%s'"\ 42.609 + ", so it cannot be deleted." % ( 42.610 + name, log)) 42.611 + else: 42.612 + self.config.removeHandler(name) 42.613 + self.slist.list.delete(items) 42.614 + self.pe.body.clear() 42.615 + 42.616 + def onUpdate(self): 42.617 + print "handler update" 42.618 + 42.619 + def onListChange(self, event): 42.620 + self.pe.body.hideControls() 42.621 + items = self.slist.list.curselection() 42.622 + name = self.slist.list.get(int(items[0])) 42.623 + hand = self.config.getHandler(name) 42.624 + self.pe.setPropertyHolder(hand) 42.625 + 42.626 + def onPropListChanged(self, newhand): 42.627 + newhand.onPropListChanged = self.onPropListChanged 42.628 + self.pe.setPropertyHolder(newhand) 42.629 + 42.630 +class FormatterPanel(Frame): 42.631 + def __init__(self, parent): 42.632 + Frame.__init__(self, parent) 42.633 + self.parent = parent 42.634 + label = Label(self, text="Formatters:") 42.635 + label.grid(row=0, column=0, sticky='w') 42.636 + self.slist = ScrollList(self, height=4, background='white') 42.637 + self.slist.list.bind('<ButtonRelease-1>', self.onListChange) 42.638 + self.slist.grid(row=1, column=0, sticky="nsew") 42.639 + self.adu = ADUPanel(self) 42.640 + self.adu.grid(row=2, column=0, sticky="ew") 42.641 + label = Label(self, text="Properties of selected formatter:") 42.642 + label.grid(row=3, column=0, sticky='w') 42.643 + self.pe = PropertyEditor(self, height=60, borderwidth=1) 42.644 + self.pe.grid(row=4, column=0, sticky='nsew') 42.645 + self.columnconfigure(0, weight=1) 42.646 + self.rowconfigure(1, weight=1) 42.647 + self.rowconfigure(4, weight=1) 42.648 + 42.649 + def setConfig(self, config): 42.650 + self.config = config 42.651 + #populate list of formatters 42.652 + flist = config.getFormatters() 42.653 + flist.sort(sortn) 42.654 + self.slist.list.delete(0, END) 42.655 + self.pe.body.clear() 42.656 + for form in flist: 42.657 + self.slist.list.insert(END, form.getPropValue("name")) 42.658 + 42.659 + def onAdd(self): 42.660 + self.pe.body.hideControls() 42.661 + fmt = self.config.getFormatter(None) 42.662 + self.slist.list.insert(END, fmt.getProp("name").getValueText()) 42.663 + self.slist.list.select_clear(0, END) 42.664 + i = self.slist.list.size() 42.665 + self.slist.list.select_set(i - 1) 42.666 + self.pe.setPropertyHolder(fmt) 42.667 + 42.668 + def onDelete(self): 42.669 + self.pe.body.hideControls() 42.670 + items = self.slist.list.curselection() 42.671 + if not len(items): 42.672 + showerror("No Item Selected", "You haven't selected anything to delete") 42.673 + else: 42.674 + name = self.slist.list.get(int(items[0])) 42.675 + h = self.config.formatterIsUsed(name) 42.676 + if h: 42.677 + showerror("Formatter in use", 42.678 + "The formatter '%s' is being used by handler '%s'"\ 42.679 + ", so it cannot be deleted." % ( 42.680 + name, h)) 42.681 + else: 42.682 + self.config.removeFormatter(name) 42.683 + self.slist.list.delete(items) 42.684 + self.pe.body.clear() 42.685 + 42.686 + def onUpdate(self): 42.687 + self.pe.body.hideControls() 42.688 + 42.689 + def onListChange(self, event): 42.690 + self.pe.body.hideControls() 42.691 + items = self.slist.list.curselection() 42.692 + name = self.slist.list.get(int(items[0])) 42.693 + fmt = self.config.getFormatter(name) 42.694 + self.pe.setPropertyHolder(fmt) 42.695 + 42.696 +class FilterPanel(Frame): 42.697 + def __init__(self, parent): 42.698 + Frame.__init__(self, parent) 42.699 + self.parent = parent 42.700 + label = Label(self, text="Filters:") 42.701 + label.grid(row=0, column=0, sticky='w') 42.702 + self.slist = ScrollList(self, height=4, background='white') 42.703 + self.slist.list.bind('<ButtonRelease-1>', self.onListChange) 42.704 + self.slist.grid(row=1, column=0, sticky="nsew") 42.705 + self.adu = ADUPanel(self) 42.706 + self.adu.grid(row=2, column=0, sticky="ew") 42.707 + label = Label(self, text="Properties of selected filter:") 42.708 + label.grid(row=3, column=0, sticky='w') 42.709 + self.pe = PropertyEditor(self, height=60, borderwidth=1) 42.710 + self.pe.grid(row=4, column=0, sticky='nsew') 42.711 + self.columnconfigure(0, weight=1) 42.712 + self.rowconfigure(1, weight=1) 42.713 + self.rowconfigure(4, weight=1) 42.714 + 42.715 + def setConfig(self, config): 42.716 + self.config = config 42.717 + #populate list of filters 42.718 + flist = config.getFilters() 42.719 + flist.sort(sortn) 42.720 + self.slist.list.delete(0, END) 42.721 + self.pe.body.clear() 42.722 + for filt in flist: 42.723 + self.slist.list.insert(END, filt.getPropValue("name")) 42.724 + 42.725 + def onAdd(self): 42.726 + self.pe.body.hideControls() 42.727 + filt = self.config.getFilter(None) 42.728 + self.slist.list.insert(END, filt.getProp("name").getValueText()) 42.729 + self.slist.list.select_clear(0, END) 42.730 + i = self.slist.list.size() 42.731 + self.slist.list.select_set(i - 1) 42.732 + self.pe.setPropertyHolder(filt) 42.733 + 42.734 + def onDelete(self): 42.735 + self.pe.body.hideControls() 42.736 + items = self.slist.list.curselection() 42.737 + if not len(items): 42.738 + showerror("No Item Selected", "You haven't selected anything to delete") 42.739 + else: 42.740 + name = self.slist.list.get(int(items[0])) 42.741 + h = self.config.filterIsUsed(name) 42.742 + if h: 42.743 + showerror("Filter in use", 42.744 + "The filter '%s' is being used by '%s'"\ 42.745 + ", so it cannot be deleted." % ( 42.746 + name, h)) 42.747 + else: 42.748 + self.config.removeFilter(name) 42.749 + self.slist.list.delete(items) 42.750 + self.pe.body.clear() 42.751 + 42.752 + def onUpdate(self): 42.753 + self.pe.body.hideControls() 42.754 + 42.755 + def onListChange(self, event): 42.756 + self.pe.body.hideControls() 42.757 + items = self.slist.list.curselection() 42.758 + name = self.slist.list.get(int(items[0])) 42.759 + filt = self.config.getFilter(name) 42.760 + self.pe.setPropertyHolder(filt) 42.761 + 42.762 +class ConfigPanel(Frame): 42.763 + def __init__(self, parent): 42.764 + Frame.__init__(self, parent) 42.765 + self.parent = parent 42.766 + self.load = Button(self, text="Load...", command=parent.onLoad) 42.767 + self.load.pack(side=LEFT) 42.768 + self.save = Button(self, text="Save", command=parent.onSave) 42.769 + self.save.pack(side=LEFT) 42.770 + self.save = Button(self, text="Save as...", command=parent.onSaveAs) 42.771 + self.save.pack(side=LEFT) 42.772 + self.reset = Button(self, text="Reset", command=parent.onReset) 42.773 + self.reset.pack(side=RIGHT) 42.774 + 42.775 +class Configurator(Frame): 42.776 + def __init__(self, parent): 42.777 + Frame.__init__(self, parent) 42.778 + self.parent = parent 42.779 + self.llist = LoggerPanel(self) 42.780 + self.llist.grid(row=0, column=0, rowspan=2, sticky='nsew') 42.781 + spacer = Canvas(self, width=2, highlightthickness=0) 42.782 + spacer.grid(row=0, column=1, rowspan=2, sticky='ns') 42.783 + self.hlist = HandlerPanel(self) 42.784 + self.hlist.grid(row=0, column=2, sticky='nsew') 42.785 + self.flist = FormatterPanel(self) 42.786 + self.flist.grid(row=1, column=2, sticky='nsew') 42.787 + self.cfg = ConfigPanel(self) 42.788 + self.cfg.grid(row=2, column=0, columnspan=2, sticky='w') 42.789 + self.filename = None 42.790 + 42.791 + self.rowconfigure(0, weight=1) 42.792 + self.columnconfigure(0, weight=1) 42.793 + self.columnconfigure(2, weight=1) 42.794 + 42.795 + label = Label(self, text="Copyright (C) 2002 Vinay Sajip. All rights reserved.", foreground='brown') 42.796 + label.grid(row=3, column=0, columnspan=2, sticky='w') 42.797 + 42.798 + if len(sys.argv) > 1: 42.799 + fn = sys.argv[1] 42.800 + try: 42.801 + self.loadFile(fn) 42.802 + except Exception, e: 42.803 + print e 42.804 + raise 42.805 + else: 42.806 + self.onReset(0) 42.807 + self.setTitle() 42.808 + self.focus_set() 42.809 + 42.810 + def setTitle(self): 42.811 + if self.filename: 42.812 + s = os.path.split(self.filename)[1] 42.813 + else: 42.814 + s = "untitled" 42.815 + self.winfo_toplevel().title("%s - Python Logging Configurator V%s" % (s, __version__)) 42.816 + 42.817 + def loadFile(self, fn): 42.818 + self.config = LoggingConfig() 42.819 + self.config.read(fn) 42.820 + self.filename = fn 42.821 + self.llist.setConfig(self.config) 42.822 + self.hlist.setConfig(self.config) 42.823 + self.flist.setConfig(self.config) 42.824 + self.setTitle() 42.825 + 42.826 + def onLoad(self): 42.827 + fn = askopenfilename(title="Choose configuration file", filetypes=[("Logging configurations", "*.ini"), ("All files", "*.*")]) 42.828 + if fn: 42.829 + self.loadFile(fn) 42.830 + 42.831 + def onSaveAs(self): 42.832 + if self.filename: 42.833 + fn = os.path.split(self.filename)[1] 42.834 + else: 42.835 + fn = DEFAULT_FILENAME 42.836 + fn = asksaveasfilename(title="Save configuration as", initialfile=fn, filetypes=[("Logging configurations", "*.ini"), ("All files", "*.*")]) 42.837 + if fn: 42.838 + self.config.save(fn) 42.839 + self.filename = fn 42.840 + self.setTitle() 42.841 + 42.842 + def onSave(self): 42.843 + if not self.filename: 42.844 + self.onSaveAs() 42.845 + else: 42.846 + self.config.save(self.filename) 42.847 + 42.848 + def onReset(self, confirm=1): 42.849 + if not confirm: 42.850 + doit = 1 42.851 + else: 42.852 + doit = askyesno("Reset", "Are you sure you want to reset?") 42.853 + if doit: 42.854 + self.config = LoggingConfig() 42.855 + self.llist.setConfig(self.config) 42.856 + self.hlist.setConfig(self.config) 42.857 + self.flist.setConfig(self.config) 42.858 + self.setTitle() 42.859 + 42.860 +# -- general properties 42.861 + 42.862 +class NameProperty(Property): 42.863 + def __init__(self, value=None): 42.864 + Property.__init__(self, "name", "Name", value) 42.865 + 42.866 +class LevelProperty(Property): 42.867 + def __init__(self, value=None): 42.868 + Property.__init__(self, "level", "Level", value) 42.869 + 42.870 + def getChoices(self): 42.871 + return LOGGING_LEVELS 42.872 + 42.873 +# -- formatter properties 42.874 + 42.875 +class FormatProperty(Property): 42.876 + def __init__(self, value=None): 42.877 + Property.__init__(self, "format", "Format", value) 42.878 + 42.879 +class DateFormatProperty(Property): 42.880 + def __init__(self, value=None): 42.881 + Property.__init__(self, "datefmt", "Date Format", value) 42.882 + 42.883 +class FormatterProxy(PropertyHolder): 42.884 + def __init__(self, config, dict): 42.885 + self.config = config 42.886 + PropertyHolder.__init__(self, dict) 42.887 + prop = NameProperty(dict.get("name", "")) 42.888 + self.dict["name"] = prop 42.889 + prop = FormatProperty(dict.get("format", "%(asctime)s %(levelname)s %(message)s")) 42.890 + self.dict["format"] = prop 42.891 + prop = DateFormatProperty(dict.get("datefmt", "")) 42.892 + self.dict["datefmt"] = prop 42.893 + self.propnames = ["name", "format", "datefmt"] 42.894 + 42.895 + def isReadonly(self, name): 42.896 + return name == "name" 42.897 + 42.898<