Issue #7086: Added TCP support to SysLogHandler and tidied up some anachronisms in the code.

This commit is contained in:
Vinay Sajip 2009-10-10 20:32:36 +00:00
parent 6e3dbbdf39
commit 1c77b7f84c
5 changed files with 90 additions and 77 deletions

View File

@ -764,12 +764,12 @@ functions.
Does basic configuration for the logging system by creating a
:class:`StreamHandler` with a default :class:`Formatter` and adding it to the
root logger. The function does nothing if any handlers have been defined for
the root logger. The functions :func:`debug`, :func:`info`, :func:`warning`,
root logger. The functions :func:`debug`, :func:`info`, :func:`warning`,
:func:`error` and :func:`critical` will call :func:`basicConfig` automatically
if no handlers are defined for the root logger.
This function does nothing if the root logger already has handlers configured.
This function does nothing if the root logger already has handlers
configured for it.
.. versionchanged:: 2.4
Formerly, :func:`basicConfig` did not take any keyword arguments.
@ -2008,16 +2008,22 @@ The :class:`SysLogHandler` class, located in the :mod:`logging.handlers` module,
supports sending logging messages to a remote or local Unix syslog.
.. class:: SysLogHandler([address[, facility]])
.. class:: SysLogHandler([address[, facility[, socktype]]])
Returns a new instance of the :class:`SysLogHandler` class intended to
communicate with a remote Unix machine whose address is given by *address* in
the form of a ``(host, port)`` tuple. If *address* is not specified,
``('localhost', 514)`` is used. The address is used to open a UDP socket. An
``('localhost', 514)`` is used. The address is used to open a socket. An
alternative to providing a ``(host, port)`` tuple is providing an address as a
string, for example "/dev/log". In this case, a Unix domain socket is used to
send the message to the syslog. If *facility* is not specified,
:const:`LOG_USER` is used.
:const:`LOG_USER` is used. The type of socket opened depends on the
*socktype* argument, which defaults to :const:`socket.SOCK_DGRAM` and thus
opens a UDP socket. To open a TCP socket (for use with the newer syslog
daemons such as rsyslog), specify a value of :const:`socket.SOCK_STREAM`.
.. versionchanged:: 2.7
*socktype* was added.
.. method:: close()

View File

@ -23,6 +23,8 @@ Copyright (C) 2001-2009 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
import sys, os, time, cStringIO, traceback, warnings
__all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
'FATAL', 'FileHandler', 'Filter', 'Formatter', 'Handler', 'INFO',
'LogRecord', 'Logger', 'LoggerAdapter', 'NOTSET', 'NullHandler',
@ -31,8 +33,6 @@ __all__ = ['BASIC_FORMAT', 'BufferingFormatter', 'CRITICAL', 'DEBUG', 'ERROR',
'exception', 'fatal', 'getLevelName', 'getLogger', 'getLoggerClass',
'info', 'log', 'makeLogRecord', 'setLoggerClass', 'warn', 'warning']
import sys, os, types, time, string, cStringIO, traceback, warnings
try:
import codecs
except ImportError:
@ -46,12 +46,17 @@ except ImportError:
__author__ = "Vinay Sajip <vinay_sajip@red-dove.com>"
__status__ = "production"
__version__ = "0.5.0.8"
__date__ = "27 April 2009"
__version__ = "0.5.0.9"
__date__ = "09 October 2009"
#---------------------------------------------------------------------------
# Miscellaneous module data
#---------------------------------------------------------------------------
try:
unicode
_unicode = True
except NameError:
_unicode = False
#
# _srcfile is used when walking the stack to check when we've got the first
@ -59,7 +64,7 @@ __date__ = "27 April 2009"
#
if hasattr(sys, 'frozen'): #support for py2exe
_srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
elif string.lower(__file__[-4:]) in ['.pyc', '.pyo']:
elif __file__[-4:].lower() in ['.pyc', '.pyo']:
_srcfile = __file__[:-4] + '.py'
else:
_srcfile = __file__
@ -71,7 +76,7 @@ def currentframe():
try:
raise Exception
except:
return sys.exc_traceback.tb_frame.f_back
return sys.exc_info()[2].tb_frame.f_back
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# done filching
@ -255,9 +260,7 @@ class LogRecord:
# 'Value is %d' instead of 'Value is 0'.
# For the use case of passing a dictionary, this should not be a
# problem.
if args and len(args) == 1 and (
type(args[0]) == types.DictType
) and args[0]:
if args and len(args) == 1 and isinstance(args[0], dict) and args[0]:
args = args[0]
self.args = args
self.levelname = getLevelName(level)
@ -306,11 +309,11 @@ class LogRecord:
Return the message for this LogRecord after merging any user-supplied
arguments with the message.
"""
if not hasattr(types, "UnicodeType"): #if no unicode support...
if not _unicode: #if no unicode support...
msg = str(self.msg)
else:
msg = self.msg
if type(msg) not in (types.UnicodeType, types.StringType):
if not isinstance(msg, basestring):
try:
msg = str(self.msg)
except UnicodeError:
@ -447,7 +450,7 @@ class Formatter:
formatException() and appended to the message.
"""
record.message = record.getMessage()
if string.find(self._fmt,"%(asctime)") >= 0:
if self._fmt.find("%(asctime)") >= 0:
record.asctime = self.formatTime(record, self.datefmt)
s = self._fmt % record.__dict__
if record.exc_info:
@ -541,7 +544,7 @@ class Filter:
return 1
elif self.name == record.name:
return 1
elif string.find(record.name, self.name, 0, self.nlen) != 0:
elif record.name.find(self.name, 0, self.nlen) != 0:
return 0
return (record.name[self.nlen] == ".")
@ -667,8 +670,8 @@ class Handler(Filterer):
This version is intended to be implemented by subclasses and so
raises a NotImplementedError.
"""
raise NotImplementedError, 'emit must be implemented '\
'by Handler subclasses'
raise NotImplementedError('emit must be implemented '
'by Handler subclasses')
def handle(self, record):
"""
@ -781,7 +784,7 @@ class StreamHandler(Handler):
msg = self.format(record)
stream = self.stream
fs = "%s\n"
if not hasattr(types, "UnicodeType"): #if no unicode support...
if not _unicode: #if no unicode support...
stream.write(fs % msg)
else:
try:
@ -903,8 +906,8 @@ def setLoggerClass(klass):
"""
if klass != Logger:
if not issubclass(klass, Logger):
raise TypeError, "logger not derived from logging.Logger: " + \
klass.__name__
raise TypeError("logger not derived from logging.Logger: "
+ klass.__name__)
global _loggerClass
_loggerClass = klass
@ -967,7 +970,7 @@ class Manager:
from the specified logger to the root of the logger hierarchy.
"""
name = alogger.name
i = string.rfind(name, ".")
i = name.rfind(".")
rv = None
while (i > 0) and not rv:
substr = name[:i]
@ -980,7 +983,7 @@ class Manager:
else:
assert isinstance(obj, PlaceHolder)
obj.append(alogger)
i = string.rfind(name, ".", 0, i - 1)
i = name.rfind(".", 0, i - 1)
if not rv:
rv = self.root
alogger.parent = rv
@ -994,7 +997,6 @@ class Manager:
namelen = len(name)
for c in ph.loggerMap.keys():
#The if means ... if not c.parent.name.startswith(nm)
#if string.find(c.parent.name, nm) <> 0:
if c.parent.name[:namelen] != name:
alogger.parent = c.parent
c.parent = alogger
@ -1090,7 +1092,7 @@ class Logger(Filterer):
"""
Convenience method for logging an ERROR with exception information.
"""
self.error(*((msg,) + args), **{'exc_info': 1})
self.error(msg, exc_info=1, *args)
def critical(self, msg, *args, **kwargs):
"""
@ -1115,9 +1117,9 @@ class Logger(Filterer):
logger.log(level, "We have a %s", "mysterious problem", exc_info=1)
"""
if type(level) != types.IntType:
if not isinstance(level, int):
if raiseExceptions:
raise TypeError, "level must be an integer"
raise TypeError("level must be an integer")
else:
return
if self.isEnabledFor(level):
@ -1173,7 +1175,7 @@ class Logger(Filterer):
else:
fn, lno, func = "(unknown file)", 0, "(unknown function)"
if exc_info:
if type(exc_info) != types.TupleType:
if not isinstance(exc_info, tuple):
exc_info = sys.exc_info()
record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra)
self.handle(record)
@ -1449,7 +1451,7 @@ def critical(msg, *args, **kwargs):
"""
if len(root.handlers) == 0:
basicConfig()
root.critical(*((msg,)+args), **kwargs)
root.critical(msg, *args, **kwargs)
fatal = critical
@ -1459,14 +1461,14 @@ def error(msg, *args, **kwargs):
"""
if len(root.handlers) == 0:
basicConfig()
root.error(*((msg,)+args), **kwargs)
root.error(msg, *args, **kwargs)
def exception(msg, *args):
"""
Log a message with severity 'ERROR' on the root logger,
with exception information.
"""
error(*((msg,)+args), **{'exc_info': 1})
error(msg, exc_info=1, *args)
def warning(msg, *args, **kwargs):
"""
@ -1474,7 +1476,7 @@ def warning(msg, *args, **kwargs):
"""
if len(root.handlers) == 0:
basicConfig()
root.warning(*((msg,)+args), **kwargs)
root.warning(msg, *args, **kwargs)
warn = warning
@ -1484,7 +1486,7 @@ def info(msg, *args, **kwargs):
"""
if len(root.handlers) == 0:
basicConfig()
root.info(*((msg,)+args), **kwargs)
root.info(msg, *args, **kwargs)
def debug(msg, *args, **kwargs):
"""
@ -1492,7 +1494,7 @@ def debug(msg, *args, **kwargs):
"""
if len(root.handlers) == 0:
basicConfig()
root.debug(*((msg,)+args), **kwargs)
root.debug(msg, *args, **kwargs)
def log(level, msg, *args, **kwargs):
"""
@ -1500,7 +1502,7 @@ def log(level, msg, *args, **kwargs):
"""
if len(root.handlers) == 0:
basicConfig()
root.log(*((level, msg)+args), **kwargs)
root.log(level, msg, *args, **kwargs)
def disable(level):
"""

View File

@ -19,15 +19,12 @@ Configuration functions for the logging package for Python. The core package
is based on PEP 282 and comments thereto in comp.lang.python, and influenced
by Apache's log4j system.
Should work under Python versions >= 1.5.2, except that source line
information is not available unless 'sys._getframe()' is.
Copyright (C) 2001-2008 Vinay Sajip. All Rights Reserved.
Copyright (C) 2001-2009 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging' and log away!
"""
import sys, logging, logging.handlers, string, socket, struct, os, traceback, types
import sys, logging, logging.handlers, socket, struct, os, traceback
try:
import thread
@ -52,7 +49,7 @@ else:
# _listener holds the server object doing the listening
_listener = None
def fileConfig(fname, defaults=None, disable_existing_loggers=1):
def fileConfig(fname, defaults=None, disable_existing_loggers=True):
"""
Read the logging configuration from a ConfigParser-format file.
@ -89,7 +86,7 @@ def fileConfig(fname, defaults=None, disable_existing_loggers=1):
def _resolve(name):
"""Resolve a dotted name to a global object."""
name = string.split(name, '.')
name = name.split('.')
used = name.pop(0)
found = __import__(used)
for n in name:
@ -102,14 +99,14 @@ def _resolve(name):
return found
def _strip_spaces(alist):
return map(lambda x: string.strip(x), alist)
return map(lambda x: x.strip(), alist)
def _create_formatters(cp):
"""Create and return formatters"""
flist = cp.get("formatters", "keys")
if not len(flist):
return {}
flist = string.split(flist, ",")
flist = flist.split(",")
flist = _strip_spaces(flist)
formatters = {}
for form in flist:
@ -138,7 +135,7 @@ def _install_handlers(cp, formatters):
hlist = cp.get("handlers", "keys")
if not len(hlist):
return {}
hlist = string.split(hlist, ",")
hlist = hlist.split(",")
hlist = _strip_spaces(hlist)
handlers = {}
fixups = [] #for inter-handler references
@ -181,8 +178,8 @@ def _install_loggers(cp, handlers, disable_existing_loggers):
# configure the root first
llist = cp.get("loggers", "keys")
llist = string.split(llist, ",")
llist = map(lambda x: string.strip(x), llist)
llist = llist.split(",")
llist = list(map(lambda x: x.strip(), llist))
llist.remove("root")
sectname = "logger_root"
root = logging.root
@ -195,7 +192,7 @@ def _install_loggers(cp, handlers, disable_existing_loggers):
root.removeHandler(h)
hlist = cp.get(sectname, "handlers")
if len(hlist):
hlist = string.split(hlist, ",")
hlist = hlist.split(",")
hlist = _strip_spaces(hlist)
for hand in hlist:
log.addHandler(handlers[hand])
@ -209,7 +206,7 @@ def _install_loggers(cp, handlers, disable_existing_loggers):
#what's left in existing is the set of loggers
#which were in the previous configuration but
#which are not in the new configuration.
existing = root.manager.loggerDict.keys()
existing = list(root.manager.loggerDict.keys())
#The list needs to be sorted so that we can
#avoid disabling child loggers of explicitly
#named loggers. With a sorted list it is easier
@ -247,7 +244,7 @@ def _install_loggers(cp, handlers, disable_existing_loggers):
logger.disabled = 0
hlist = cp.get(sectname, "handlers")
if len(hlist):
hlist = string.split(hlist, ",")
hlist = hlist.split(",")
hlist = _strip_spaces(hlist)
for hand in hlist:
logger.addHandler(handlers[hand])
@ -278,7 +275,7 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
stopListening().
"""
if not thread:
raise NotImplementedError, "listen() needs threading to work"
raise NotImplementedError("listen() needs threading to work")
class ConfigStreamHandler(StreamRequestHandler):
"""
@ -321,7 +318,7 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT):
traceback.print_exc()
os.remove(file)
except socket.error, e:
if type(e.args) != types.TupleType:
if not isinstance(e.args, tuple):
raise
else:
errcode = e.args[0]

View File

@ -24,7 +24,7 @@ Copyright (C) 2001-2009 Vinay Sajip. All Rights Reserved.
To use, simply 'import logging.handlers' and log away!
"""
import logging, socket, types, os, string, cPickle, struct, time, re
import logging, socket, os, cPickle, struct, time, re
from stat import ST_DEV, ST_INO
try:
@ -41,6 +41,7 @@ DEFAULT_UDP_LOGGING_PORT = 9021
DEFAULT_HTTP_LOGGING_PORT = 9022
DEFAULT_SOAP_LOGGING_PORT = 9023
SYSLOG_UDP_PORT = 514
SYSLOG_TCP_PORT = 514
_MIDNIGHT = 24 * 60 * 60 # number of seconds in a day
@ -155,9 +156,9 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
If backupCount is > 0, when rollover is done, no more than backupCount
files are kept - the oldest ones are deleted.
"""
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=0, utc=0):
def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
self.when = string.upper(when)
self.when = when.upper()
self.backupCount = backupCount
self.utc = utc
# Calculate the real rollover interval, which is just the number of
@ -204,8 +205,6 @@ class TimedRotatingFileHandler(BaseRotatingHandler):
self.interval = self.interval * interval # multiply by units requested
self.rolloverAt = self.computeRollover(int(time.time()))
#print "Will rollover at %d, %d seconds from now" % (self.rolloverAt, self.rolloverAt - currentTime)
def computeRollover(self, currentTime):
"""
Work out the rollover time based on the specified time.
@ -692,7 +691,8 @@ class SysLogHandler(logging.Handler):
"CRITICAL" : "critical"
}
def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
facility=LOG_USER, socktype=socket.SOCK_DGRAM):
"""
Initialize a handler.
@ -704,13 +704,16 @@ class SysLogHandler(logging.Handler):
self.address = address
self.facility = facility
if type(address) == types.StringType:
self.socktype = socktype
if isinstance(address, basestring):
self.unixsocket = 1
self._connect_unixsocket(address)
else:
self.unixsocket = 0
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket = socket.socket(socket.AF_INET, socktype)
if socktype == socket.SOCK_STREAM:
self.socket.connect(address)
self.formatter = None
def _connect_unixsocket(self, address):
@ -736,9 +739,9 @@ class SysLogHandler(logging.Handler):
priority_names mapping dictionaries are used to convert them to
integers.
"""
if type(facility) == types.StringType:
if isinstance(facility, basestring):
facility = self.facility_names[facility]
if type(priority) == types.StringType:
if isinstance(priority, basestring):
priority = self.priority_names[priority]
return (facility << 3) | priority
@ -783,8 +786,10 @@ class SysLogHandler(logging.Handler):
except socket.error:
self._connect_unixsocket(self.address)
self.socket.send(msg)
else:
elif self.socktype == socket.SOCK_DGRAM:
self.socket.sendto(msg, self.address)
else:
self.socket.sendall(msg)
except (KeyboardInterrupt, SystemExit):
raise
except:
@ -805,16 +810,16 @@ class SMTPHandler(logging.Handler):
for the credentials argument.
"""
logging.Handler.__init__(self)
if type(mailhost) == types.TupleType:
if isinstance(mailhost, tuple):
self.mailhost, self.mailport = mailhost
else:
self.mailhost, self.mailport = mailhost, None
if type(credentials) == types.TupleType:
if isinstance(credentials, tuple):
self.username, self.password = credentials
else:
self.username = None
self.fromaddr = fromaddr
if type(toaddrs) == types.StringType:
if isinstance(toaddrs, basestring):
toaddrs = [toaddrs]
self.toaddrs = toaddrs
self.subject = subject
@ -865,7 +870,7 @@ class SMTPHandler(logging.Handler):
msg = self.format(record)
msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
self.fromaddr,
string.join(self.toaddrs, ","),
",".join(self.toaddrs),
self.getSubject(record),
formatdate(), msg)
if self.username:
@ -909,8 +914,8 @@ class NTEventLogHandler(logging.Handler):
logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
}
except ImportError:
print "The Python Win32 extensions for NT (service, event "\
"logging) appear not to be available."
print("The Python Win32 extensions for NT (service, event "\
"logging) appear not to be available.")
self._welu = None
def getMessageID(self, record):
@ -988,9 +993,9 @@ class HTTPHandler(logging.Handler):
("GET" or "POST")
"""
logging.Handler.__init__(self)
method = string.upper(method)
method = method.upper()
if method not in ["GET", "POST"]:
raise ValueError, "method must be GET or POST"
raise ValueError("method must be GET or POST")
self.host = host
self.url = url
self.method = method
@ -1016,7 +1021,7 @@ class HTTPHandler(logging.Handler):
url = self.url
data = urllib.urlencode(self.mapLogRecord(record))
if self.method == "GET":
if (string.find(url, '?') >= 0):
if (url.find('?') >= 0):
sep = '&'
else:
sep = '?'
@ -1024,7 +1029,7 @@ class HTTPHandler(logging.Handler):
h.putrequest(self.method, url)
# support multiple hosts on one IP address...
# need to strip optional :port from host, if present
i = string.find(host, ":")
i = host.find(":")
if i >= 0:
host = host[:i]
h.putheader("Host", host)

View File

@ -398,6 +398,9 @@ Core and Builtins
Library
-------
- Issue #7086: Added TCP support to SysLogHandler, and tidied up some
anachronisms in the code which were a relic of 1.5.2 compatibility.
- Issue #7082: When falling back to the MIME 'name' parameter, the
correct place to look for it is the Content-Type header.