How to use Python's logging module for doing all kinds of crazy ninja logging magic (or just simple things too). Presented to ClePy on September 11, 2006.
a DIY solution... • ...but it probably already inspired at least two or three others in your organization! • Almost as embarassing as writing your own web framework
– ERROR 40 – WARNING 30 – INFO 20 – DEBUG 10 – NOTSET 0 • Convenience methods correspond to logging at these levels • Plus you can define your own levels
Logs at the CRITICAL level on the root logger – fmt is a format string – Any remaining args apply to format specifiers in the format string – If kwarg exc_info is True, or an exception tuple (sys.exc_info), exception info is also logged
[, exc_info]]) • exception(f m t [, *args]) – Includes exception info – Can only be used inside an exception handler • warning(f m t [, *ar gs [, exc_info]]) • info(f m t [, *ar gs [, exc_info]]) • debug(f m t [, *ar gs [, exc_info]]) • log(l evel , f m t [, *ar gs [, exc_info]])
logging.debug(“Debugging info”) logging.info(“Something wonderful is about to” \ “happen...”) logging.critical(“I have a bad feeling about this”) DEBUG 20060806 23:09:43,570 Debugging info INFO 20060806 23:09:43,574 Something wonderful is about to happen... CRITICAL 20060806 23:09:44,348 I have a bad feeling about this
series of names ('foo' or 'foo.bar.spam') – If logname is '', you get the root logger – If no Logger named logname exists, creates and returns a new logger – If a Logger named logname already exists, returns the existing instance
the name 'foo.bar.spam', and this is True, messages to L will also be sent to the logger with name 'foo.bar' • L.setLevel(level) • L.isEnabledFor(level) • L.getEffectiveLevel() – Level set by setLevel() – Or parent logger's getEffectiveLevel() – Or root logger's effective level
using its addHandler() • Attach any number of different handlers • All are Handler subclasses • Some default handlers are in logging; others in logging.handlers
UDP server as pickled LogRecords – Delivery is not guaranteed • FileHandler(filename [, mode]) – Write log messages to a file • handlers.HTTPHandler(host, url [, method]) – Upload log messages to an HTTP server using GET or POST
messages in memory and flush them to another handler (target) when we hit capacity (bytes) or see a message of level flushLevel • handlers.NTEventLogHandler( appname [, dllname [, logtype]]) – Only available if Win32 extensions for Python have been installed
• handlers.SysLogHandler( [address [, facility]]) – address is a tuple (host, port); defaults to ('localhost', 514) – facility is an integer facility code (see SysLogHandler's code for a full list)
– Applies filters, deals with locking, and emits the message • h.handleError(record) – Used when an error occurs during normal handling; does nothing by default
messages below level • addLevelName(level, levelName) – Create a new logging level • getLevelName(level) – Returns the name of the level associated with the numeric level • shutdown() – Flush and shut down all logging objects
have many components? Might want to divide logging into multiple loggers... netlog = logging.getLogger('mondo.net') netlog.info(“Networking on port %d”, port) • Logging messages issued on 'mondo.net' will propagate up to loggers defined for 'mondo'... So the mondo.log will have: CRITICAL mondo 20060807 00:50:17,900 MONDO OVERLOAD! INFO mondo.net 20060807 00:50:17,905 networking on port 31337
handlers for 'mondo.net'; eg, if we wanted to log network messages to a file: nethand = logging.FileHandler('mondo.net.log') nethand.setLevel(logging.DEBUG) nethand.setFormatter(f) netlog.addHandler(nethand) • Now messages sent to netlog will be written to 'mondo.net.log' and to 'mondo.log'; critical messages will go to both places and be displayed on sys.stderr
– check out the online docs • Use getLogger() to avoid having to pass log objects around • In earlier versions of Python 2.3, findCaller() is lightly broken – Only unwinds the stack by 1 level instead of as many as needed – Chance to practice your monkeypatching skills