diff options
Diffstat (limited to 'Lib/logging/handlers.py')
| -rw-r--r-- | Lib/logging/handlers.py | 136 |
1 files changed, 70 insertions, 66 deletions
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py index 756baf0..f8c7164 100644 --- a/Lib/logging/handlers.py +++ b/Lib/logging/handlers.py @@ -1,4 +1,4 @@ -# Copyright 2001-2007 by Vinay Sajip. All Rights Reserved. +# Copyright 2001-2010 by Vinay Sajip. All Rights Reserved. # # Permission to use, copy, modify, and distribute this software and its # documentation for any purpose and without fee is hereby granted, @@ -19,13 +19,13 @@ Additional handlers 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. -Copyright (C) 2001-2009 Vinay Sajip. All Rights Reserved. +Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved. To use, simply 'import logging.handlers' and log away! """ -import logging, socket, types, os, string, cPickle, struct, time, re -from stat import ST_DEV, ST_INO +import logging, socket, os, cPickle, struct, time, re +from stat import ST_DEV, ST_INO, ST_MTIME try: import codecs @@ -46,6 +46,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 @@ -107,8 +108,13 @@ class RotatingFileHandler(BaseRotatingHandler): If maxBytes is zero, rollover never occurs. """ + # If rotation/rollover is wanted, it doesn't make sense to use another + # mode. If for example 'w' were specified, then if there were multiple + # runs of the calling application, the logs from previous runs would be + # lost if the 'w' is respected, because the log file would be truncated + # on each run. if maxBytes > 0: - mode = 'a' # doesn't make sense otherwise! + mode = 'a' BaseRotatingHandler.__init__(self, filename, mode, encoding, delay) self.maxBytes = maxBytes self.backupCount = backupCount @@ -117,8 +123,9 @@ class RotatingFileHandler(BaseRotatingHandler): """ Do a rollover, as described in __init__(). """ - - self.stream.close() + if self.stream: + self.stream.close() + self.stream = None if self.backupCount > 0: for i in range(self.backupCount - 1, 0, -1): sfn = "%s.%d" % (self.baseFilename, i) @@ -160,9 +167,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 @@ -177,7 +184,6 @@ class TimedRotatingFileHandler(BaseRotatingHandler): # # Case of the 'when' specifier is not important; lower or upper case # will work. - currentTime = int(time.time()) if self.when == 'S': self.interval = 1 # one second self.suffix = "%Y-%m-%d_%H-%M-%S" @@ -208,9 +214,11 @@ class TimedRotatingFileHandler(BaseRotatingHandler): self.extMatch = re.compile(self.extMatch) 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) + if os.path.exists(filename): + t = os.stat(filename)[ST_MTIME] + else: + t = int(time.time()) + self.rolloverAt = self.computeRollover(t) def computeRollover(self, currentTime): """ @@ -317,6 +325,7 @@ class TimedRotatingFileHandler(BaseRotatingHandler): """ if self.stream: self.stream.close() + self.stream = None # get the time that this sequence started at and make it a TimeTuple t = self.rolloverAt - self.interval if self.utc: @@ -636,7 +645,8 @@ class SysLogHandler(logging.Handler): LOG_NEWS = 7 # network news subsystem LOG_UUCP = 8 # UUCP subsystem LOG_CRON = 9 # clock daemon - LOG_AUTHPRIV = 10 # security/authorization messages (private) + LOG_AUTHPRIV = 10 # security/authorization messages (private) + LOG_FTP = 11 # FTP daemon # other codes through 15 reserved for system use LOG_LOCAL0 = 16 # reserved for local use @@ -668,6 +678,7 @@ class SysLogHandler(logging.Handler): "authpriv": LOG_AUTHPRIV, "cron": LOG_CRON, "daemon": LOG_DAEMON, + "ftp": LOG_FTP, "kern": LOG_KERN, "lpr": LOG_LPR, "mail": LOG_MAIL, @@ -698,7 +709,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. @@ -710,13 +722,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): @@ -742,9 +757,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 @@ -773,20 +788,19 @@ class SysLogHandler(logging.Handler): The record is formatted, and then sent to the syslog server. If exception information is present, it is NOT sent to the server. """ - msg = self.format(record) + msg = self.format(record) + '\000' """ We need to convert record level to lowercase, maybe this will change in the future. """ - msg = self.log_format_string % ( - self.encodePriority(self.facility, - self.mapPriority(record.levelname)), - msg) - # Treat unicode messages as required by RFC 5424 - if _unicode and type(msg) is unicode: + prio = '<%d>' % self.encodePriority(self.facility, + self.mapPriority(record.levelname)) + # Message is a string. Convert to bytes as required by RFC 5424 + if type(msg) is unicode: msg = msg.encode('utf-8') if codecs: msg = codecs.BOM_UTF8 + msg + msg = prio + msg try: if self.unixsocket: try: @@ -794,8 +808,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,7 +821,8 @@ class SMTPHandler(logging.Handler): """ A handler class which sends an SMTP email for each logging event. """ - def __init__(self, mailhost, fromaddr, toaddrs, subject, credentials=None): + def __init__(self, mailhost, fromaddr, toaddrs, subject, + credentials=None, secure=None): """ Initialize the handler. @@ -813,22 +830,28 @@ class SMTPHandler(logging.Handler): line of the email. To specify a non-standard SMTP port, use the (host, port) tuple format for the mailhost argument. To specify authentication credentials, supply a (username, password) tuple - for the credentials argument. + for the credentials argument. To specify the use of a secure + protocol (TLS), pass in a tuple for the secure argument. This will + only be used when authentication credentials are supplied. The tuple + will be either an empty tuple, or a single-value tuple with the name + of a keyfile, or a 2-value tuple with the names of the keyfile and + certificate file. (This tuple is passed to the `starttls` method). """ 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 + self.secure = secure def getSubject(self, record): """ @@ -839,24 +862,6 @@ class SMTPHandler(logging.Handler): """ return self.subject - weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - - monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - - def date_time(self): - """ - Return the current date and time formatted for a MIME header. - Needed for Python 1.5.2 (no email package available) - """ - year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time()) - s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( - self.weekdayname[wd], - day, self.monthname[month], year, - hh, mm, ss) - return s - def emit(self, record): """ Emit a record. @@ -865,10 +870,7 @@ class SMTPHandler(logging.Handler): """ try: import smtplib - try: - from email.utils import formatdate - except ImportError: - formatdate = self.date_time + from email.utils import formatdate port = self.mailport if not port: port = smtplib.SMTP_PORT @@ -876,10 +878,14 @@ 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: + if self.secure is not None: + smtp.ehlo() + smtp.starttls(*self.secure) + smtp.ehlo() smtp.login(self.username, self.password) smtp.sendmail(self.fromaddr, self.toaddrs, msg) smtp.quit() @@ -920,8 +926,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): @@ -999,9 +1005,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 @@ -1018,7 +1024,7 @@ class HTTPHandler(logging.Handler): """ Emit a record. - Send the record to the Web server as an URL-encoded dictionary + Send the record to the Web server as a percent-encoded dictionary """ try: import httplib, urllib @@ -1027,7 +1033,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 = '?' @@ -1035,7 +1041,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) @@ -1043,9 +1049,7 @@ class HTTPHandler(logging.Handler): h.putheader("Content-type", "application/x-www-form-urlencoded") h.putheader("Content-length", str(len(data))) - h.endheaders() - if self.method == "POST": - h.send(data) + h.endheaders(data if self.method == "POST" else None) h.getreply() #can't do anything with the result except (KeyboardInterrupt, SystemExit): raise |
