Ignore:
Timestamp:
Mar 19, 2014, 11:31:01 PM (11 years ago)
Author:
dmik
Message:

python: Merge vendor 2.7.6 to trunk.

Location:
python/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • python/trunk

  • python/trunk/Lib/logging/handlers.py

    r2 r391  
    1 # Copyright 2001-2007 by Vinay Sajip. All Rights Reserved.
     1# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
    22#
    33# Permission to use, copy, modify, and distribute this software and its
     
    1717"""
    1818Additional handlers for the logging package for Python. The core package is
    19 based on PEP 282 and comments thereto in comp.lang.python, and influenced by
    20 Apache's log4j system.
    21 
    22 Copyright (C) 2001-2009 Vinay Sajip. All Rights Reserved.
     19based on PEP 282 and comments thereto in comp.lang.python.
     20
     21Copyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
    2322
    2423To use, simply 'import logging.handlers' and log away!
    2524"""
    2625
    27 import logging, socket, types, os, string, cPickle, struct, time, re
    28 from stat import ST_DEV, ST_INO
     26import errno, logging, socket, os, cPickle, struct, time, re
     27from stat import ST_DEV, ST_INO, ST_MTIME
    2928
    3029try:
     
    3231except ImportError:
    3332    codecs = None
     33try:
     34    unicode
     35    _unicode = True
     36except NameError:
     37    _unicode = False
    3438
    3539#
     
    4246DEFAULT_SOAP_LOGGING_PORT   = 9023
    4347SYSLOG_UDP_PORT             = 514
     48SYSLOG_TCP_PORT             = 514
    4449
    4550_MIDNIGHT = 24 * 60 * 60  # number of seconds in a day
     
    103108        If maxBytes is zero, rollover never occurs.
    104109        """
     110        # If rotation/rollover is wanted, it doesn't make sense to use another
     111        # mode. If for example 'w' were specified, then if there were multiple
     112        # runs of the calling application, the logs from previous runs would be
     113        # lost if the 'w' is respected, because the log file would be truncated
     114        # on each run.
    105115        if maxBytes > 0:
    106             mode = 'a' # doesn't make sense otherwise!
     116            mode = 'a'
    107117        BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
    108118        self.maxBytes = maxBytes
     
    113123        Do a rollover, as described in __init__().
    114124        """
    115 
    116         self.stream.close()
     125        if self.stream:
     126            self.stream.close()
     127            self.stream = None
    117128        if self.backupCount > 0:
    118129            for i in range(self.backupCount - 1, 0, -1):
     
    127138            if os.path.exists(dfn):
    128139                os.remove(dfn)
    129             os.rename(self.baseFilename, dfn)
    130             #print "%s -> %s" % (self.baseFilename, dfn)
    131         self.mode = 'w'
    132         self.stream = self._open()
     140            # Issue 18940: A file may not have been created if delay is True.
     141            if os.path.exists(self.baseFilename):
     142                os.rename(self.baseFilename, dfn)
     143        if not self.delay:
     144            self.stream = self._open()
    133145
    134146    def shouldRollover(self, record):
     
    156168    files are kept - the oldest ones are deleted.
    157169    """
    158     def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=0, utc=0):
     170    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
    159171        BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
    160         self.when = string.upper(when)
     172        self.when = when.upper()
    161173        self.backupCount = backupCount
    162174        self.utc = utc
     
    173185        # Case of the 'when' specifier is not important; lower or upper case
    174186        # will work.
    175         currentTime = int(time.time())
    176187        if self.when == 'S':
    177188            self.interval = 1 # one second
     
    204215        self.extMatch = re.compile(self.extMatch)
    205216        self.interval = self.interval * interval # multiply by units requested
    206         self.rolloverAt = self.computeRollover(int(time.time()))
    207 
    208         #print "Will rollover at %d, %d seconds from now" % (self.rolloverAt, self.rolloverAt - currentTime)
     217        if os.path.exists(filename):
     218            t = os.stat(filename)[ST_MTIME]
     219        else:
     220            t = int(time.time())
     221        self.rolloverAt = self.computeRollover(t)
    209222
    210223    def computeRollover(self, currentTime):
     
    261274                        if dstNow != dstAtRollover:
    262275                            if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
    263                                 newRolloverAt = newRolloverAt - 3600
     276                                addend = -3600
    264277                            else:           # DST bows out before next rollover, so we need to add an hour
    265                                 newRolloverAt = newRolloverAt + 3600
     278                                addend = 3600
     279                            newRolloverAt += addend
    266280                    result = newRolloverAt
    267281        return result
     
    313327        if self.stream:
    314328            self.stream.close()
     329            self.stream = None
    315330        # get the time that this sequence started at and make it a TimeTuple
     331        currentTime = int(time.time())
     332        dstNow = time.localtime(currentTime)[-1]
    316333        t = self.rolloverAt - self.interval
    317334        if self.utc:
     
    319336        else:
    320337            timeTuple = time.localtime(t)
     338            dstThen = timeTuple[-1]
     339            if dstNow != dstThen:
     340                if dstNow:
     341                    addend = 3600
     342                else:
     343                    addend = -3600
     344                timeTuple = time.localtime(t + addend)
    321345        dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
    322346        if os.path.exists(dfn):
    323347            os.remove(dfn)
    324         os.rename(self.baseFilename, dfn)
     348        # Issue 18940: A file may not have been created if delay is True.
     349        if os.path.exists(self.baseFilename):
     350            os.rename(self.baseFilename, dfn)
    325351        if self.backupCount > 0:
    326             # find the oldest log file and delete it
    327             #s = glob.glob(self.baseFilename + ".20*")
    328             #if len(s) > self.backupCount:
    329             #    s.sort()
    330             #    os.remove(s[0])
    331352            for s in self.getFilesToDelete():
    332353                os.remove(s)
    333         #print "%s -> %s" % (self.baseFilename, dfn)
    334         self.mode = 'w'
    335         self.stream = self._open()
    336         currentTime = int(time.time())
     354        if not self.delay:
     355            self.stream = self._open()
    337356        newRolloverAt = self.computeRollover(currentTime)
    338357        while newRolloverAt <= currentTime:
     
    340359        #If DST changes and midnight or weekly rollover, adjust for this.
    341360        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
    342             dstNow = time.localtime(currentTime)[-1]
    343361            dstAtRollover = time.localtime(newRolloverAt)[-1]
    344362            if dstNow != dstAtRollover:
    345363                if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
    346                     newRolloverAt = newRolloverAt - 3600
     364                    addend = -3600
    347365                else:           # DST bows out before next rollover, so we need to add an hour
    348                     newRolloverAt = newRolloverAt + 3600
     366                    addend = 3600
     367                newRolloverAt += addend
    349368        self.rolloverAt = newRolloverAt
    350369
     
    371390    def __init__(self, filename, mode='a', encoding=None, delay=0):
    372391        logging.FileHandler.__init__(self, filename, mode, encoding, delay)
    373         if not os.path.exists(self.baseFilename):
    374             self.dev, self.ino = -1, -1
    375         else:
    376             stat = os.stat(self.baseFilename)
    377             self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
     392        self.dev, self.ino = -1, -1
     393        self._statstream()
     394
     395    def _statstream(self):
     396        if self.stream:
     397            sres = os.fstat(self.stream.fileno())
     398            self.dev, self.ino = sres[ST_DEV], sres[ST_INO]
    378399
    379400    def emit(self, record):
     
    385406        current stream.
    386407        """
    387         if not os.path.exists(self.baseFilename):
    388             stat = None
    389             changed = 1
    390         else:
    391             stat = os.stat(self.baseFilename)
    392             changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino)
    393         if changed and self.stream is not None:
    394             self.stream.flush()
    395             self.stream.close()
    396             self.stream = self._open()
    397             if stat is None:
    398                 stat = os.stat(self.baseFilename)
    399             self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
     408        # Reduce the chance of race conditions by stat'ing by path only
     409        # once and then fstat'ing our new fd if we opened a new log stream.
     410        # See issue #14632: Thanks to John Mulligan for the problem report
     411        # and patch.
     412        try:
     413            # stat the file by path, checking for existence
     414            sres = os.stat(self.baseFilename)
     415        except OSError as err:
     416            if err.errno == errno.ENOENT:
     417                sres = None
     418            else:
     419                raise
     420        # compare file system stat with that of our stream file handle
     421        if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
     422            if self.stream is not None:
     423                # we have an open file handle, clean it up
     424                self.stream.flush()
     425                self.stream.close()
     426                # open a new file handle and get new stat info from that fd
     427                self.stream = self._open()
     428                self._statstream()
    400429        logging.FileHandler.emit(self, record)
    401430
     
    507536        ei = record.exc_info
    508537        if ei:
    509             dummy = self.format(record) # just to get traceback text into record.exc_text
     538            # just to get traceback text into record.exc_text ...
     539            dummy = self.format(record)
    510540            record.exc_info = None  # to avoid Unpickleable error
    511         s = cPickle.dumps(record.__dict__, 1)
     541        # See issue #14436: If msg or args are objects, they may not be
     542        # available on the receiving end. So we convert the msg % args
     543        # to a string, save it as msg and zap the args.
     544        d = dict(record.__dict__)
     545        d['msg'] = record.getMessage()
     546        d['args'] = None
     547        s = cPickle.dumps(d, 1)
    512548        if ei:
    513549            record.exc_info = ei  # for next handler
     
    550586        Closes the socket.
    551587        """
    552         if self.sock:
    553             self.sock.close()
    554             self.sock = None
     588        self.acquire()
     589        try:
     590            if self.sock:
     591                self.sock.close()
     592                self.sock = None
     593        finally:
     594            self.release()
    555595        logging.Handler.close(self)
    556596
     
    632672    LOG_UUCP      = 8       #  UUCP subsystem
    633673    LOG_CRON      = 9       #  clock daemon
    634     LOG_AUTHPRIV  = 10  #  security/authorization messages (private)
     674    LOG_AUTHPRIV  = 10      #  security/authorization messages (private)
     675    LOG_FTP       = 11      #  FTP daemon
    635676
    636677    #  other codes through 15 reserved for system use
     
    664705        "cron":     LOG_CRON,
    665706        "daemon":   LOG_DAEMON,
     707        "ftp":      LOG_FTP,
    666708        "kern":     LOG_KERN,
    667709        "lpr":      LOG_LPR,
     
    694736    }
    695737
    696     def __init__(self, address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER):
     738    def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
     739                 facility=LOG_USER, socktype=None):
    697740        """
    698741        Initialize a handler.
     
    700743        If address is specified as a string, a UNIX socket is used. To log to a
    701744        local syslogd, "SysLogHandler(address="/dev/log")" can be used.
    702         If facility is not specified, LOG_USER is used.
     745        If facility is not specified, LOG_USER is used. If socktype is
     746        specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
     747        socket type will be used. For Unix sockets, you can also specify a
     748        socktype of None, in which case socket.SOCK_DGRAM will be used, falling
     749        back to socket.SOCK_STREAM.
    703750        """
    704751        logging.Handler.__init__(self)
     
    706753        self.address = address
    707754        self.facility = facility
    708         if type(address) == types.StringType:
     755        self.socktype = socktype
     756
     757        if isinstance(address, basestring):
    709758            self.unixsocket = 1
    710759            self._connect_unixsocket(address)
    711760        else:
    712761            self.unixsocket = 0
    713             self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    714 
     762            if socktype is None:
     763                socktype = socket.SOCK_DGRAM
     764            self.socket = socket.socket(socket.AF_INET, socktype)
     765            if socktype == socket.SOCK_STREAM:
     766                self.socket.connect(address)
     767            self.socktype = socktype
    715768        self.formatter = None
    716769
    717770    def _connect_unixsocket(self, address):
    718         self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
    719         # syslog may require either DGRAM or STREAM sockets
     771        use_socktype = self.socktype
     772        if use_socktype is None:
     773            use_socktype = socket.SOCK_DGRAM
     774        self.socket = socket.socket(socket.AF_UNIX, use_socktype)
    720775        try:
    721776            self.socket.connect(address)
     777            # it worked, so set self.socktype to the used type
     778            self.socktype = use_socktype
    722779        except socket.error:
    723780            self.socket.close()
    724             self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    725             self.socket.connect(address)
     781            if self.socktype is not None:
     782                # user didn't specify falling back, so fail
     783                raise
     784            use_socktype = socket.SOCK_STREAM
     785            self.socket = socket.socket(socket.AF_UNIX, use_socktype)
     786            try:
     787                self.socket.connect(address)
     788                # it worked, so set self.socktype to the used type
     789                self.socktype = use_socktype
     790            except socket.error:
     791                self.socket.close()
     792                raise
    726793
    727794    # curious: when talking to the unix-domain '/dev/log' socket, a
     
    738805        integers.
    739806        """
    740         if type(facility) == types.StringType:
     807        if isinstance(facility, basestring):
    741808            facility = self.facility_names[facility]
    742         if type(priority) == types.StringType:
     809        if isinstance(priority, basestring):
    743810            priority = self.priority_names[priority]
    744811        return (facility << 3) | priority
     
    748815        Closes the socket.
    749816        """
    750         if self.unixsocket:
    751             self.socket.close()
     817        self.acquire()
     818        try:
     819            if self.unixsocket:
     820                self.socket.close()
     821        finally:
     822            self.release()
    752823        logging.Handler.close(self)
    753824
     
    769840        exception information is present, it is NOT sent to the server.
    770841        """
    771         msg = self.format(record)
     842        msg = self.format(record) + '\000'
    772843        """
    773844        We need to convert record level to lowercase, maybe this will
    774845        change in the future.
    775846        """
    776         msg = self.log_format_string % (
    777             self.encodePriority(self.facility,
    778                                 self.mapPriority(record.levelname)),
    779                                 msg)
     847        prio = '<%d>' % self.encodePriority(self.facility,
     848                                            self.mapPriority(record.levelname))
     849        # Message is a string. Convert to bytes as required by RFC 5424
     850        if type(msg) is unicode:
     851            msg = msg.encode('utf-8')
     852        msg = prio + msg
    780853        try:
    781854            if self.unixsocket:
     
    783856                    self.socket.send(msg)
    784857                except socket.error:
     858                    self.socket.close() # See issue 17981
    785859                    self._connect_unixsocket(self.address)
    786860                    self.socket.send(msg)
     861            elif self.socktype == socket.SOCK_DGRAM:
     862                self.socket.sendto(msg, self.address)
    787863            else:
    788                 self.socket.sendto(msg, self.address)
     864                self.socket.sendall(msg)
    789865        except (KeyboardInterrupt, SystemExit):
    790866            raise
     
    796872    A handler class which sends an SMTP email for each logging event.
    797873    """
    798     def __init__(self, mailhost, fromaddr, toaddrs, subject, credentials=None):
     874    def __init__(self, mailhost, fromaddr, toaddrs, subject,
     875                 credentials=None, secure=None):
    799876        """
    800877        Initialize the handler.
     
    804881        (host, port) tuple format for the mailhost argument. To specify
    805882        authentication credentials, supply a (username, password) tuple
    806         for the credentials argument.
     883        for the credentials argument. To specify the use of a secure
     884        protocol (TLS), pass in a tuple for the secure argument. This will
     885        only be used when authentication credentials are supplied. The tuple
     886        will be either an empty tuple, or a single-value tuple with the name
     887        of a keyfile, or a 2-value tuple with the names of the keyfile and
     888        certificate file. (This tuple is passed to the `starttls` method).
    807889        """
    808890        logging.Handler.__init__(self)
    809         if type(mailhost) == types.TupleType:
     891        if isinstance(mailhost, tuple):
    810892            self.mailhost, self.mailport = mailhost
    811893        else:
    812894            self.mailhost, self.mailport = mailhost, None
    813         if type(credentials) == types.TupleType:
     895        if isinstance(credentials, tuple):
    814896            self.username, self.password = credentials
    815897        else:
    816898            self.username = None
    817899        self.fromaddr = fromaddr
    818         if type(toaddrs) == types.StringType:
     900        if isinstance(toaddrs, basestring):
    819901            toaddrs = [toaddrs]
    820902        self.toaddrs = toaddrs
    821903        self.subject = subject
     904        self.secure = secure
     905        self._timeout = 5.0
    822906
    823907    def getSubject(self, record):
     
    830914        return self.subject
    831915
    832     weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    833 
    834     monthname = [None,
    835                  'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
    836                  'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    837 
    838     def date_time(self):
    839         """
    840         Return the current date and time formatted for a MIME header.
    841         Needed for Python 1.5.2 (no email package available)
    842         """
    843         year, month, day, hh, mm, ss, wd, y, z = time.gmtime(time.time())
    844         s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
    845                 self.weekdayname[wd],
    846                 day, self.monthname[month], year,
    847                 hh, mm, ss)
    848         return s
    849 
    850916    def emit(self, record):
    851917        """
     
    856922        try:
    857923            import smtplib
    858             try:
    859                 from email.utils import formatdate
    860             except ImportError:
    861                 formatdate = self.date_time
     924            from email.utils import formatdate
    862925            port = self.mailport
    863926            if not port:
    864927                port = smtplib.SMTP_PORT
    865             smtp = smtplib.SMTP(self.mailhost, port)
     928            smtp = smtplib.SMTP(self.mailhost, port, timeout=self._timeout)
    866929            msg = self.format(record)
    867930            msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
    868931                            self.fromaddr,
    869                             string.join(self.toaddrs, ","),
     932                            ",".join(self.toaddrs),
    870933                            self.getSubject(record),
    871934                            formatdate(), msg)
    872935            if self.username:
     936                if self.secure is not None:
     937                    smtp.ehlo()
     938                    smtp.starttls(*self.secure)
     939                    smtp.ehlo()
    873940                smtp.login(self.username, self.password)
    874941            smtp.sendmail(self.fromaddr, self.toaddrs, msg)
     
    911978         }
    912979        except ImportError:
    913             print "The Python Win32 extensions for NT (service, event "\
    914                         "logging) appear not to be available."
     980            print("The Python Win32 extensions for NT (service, event "\
     981                        "logging) appear not to be available.")
    915982            self._welu = None
    916983
     
    9901057        """
    9911058        logging.Handler.__init__(self)
    992         method = string.upper(method)
     1059        method = method.upper()
    9931060        if method not in ["GET", "POST"]:
    994             raise ValueError, "method must be GET or POST"
     1061            raise ValueError("method must be GET or POST")
    9951062        self.host = host
    9961063        self.url = url
     
    10091076        Emit a record.
    10101077
    1011         Send the record to the Web server as an URL-encoded dictionary
     1078        Send the record to the Web server as a percent-encoded dictionary
    10121079        """
    10131080        try:
     
    10181085            data = urllib.urlencode(self.mapLogRecord(record))
    10191086            if self.method == "GET":
    1020                 if (string.find(url, '?') >= 0):
     1087                if (url.find('?') >= 0):
    10211088                    sep = '&'
    10221089                else:
     
    10261093            # support multiple hosts on one IP address...
    10271094            # need to strip optional :port from host, if present
    1028             i = string.find(host, ":")
     1095            i = host.find(":")
    10291096            if i >= 0:
    10301097                host = host[:i]
     
    10341101                            "application/x-www-form-urlencoded")
    10351102                h.putheader("Content-length", str(len(data)))
    1036             h.endheaders()
    1037             if self.method == "POST":
    1038                 h.send(data)
     1103            h.endheaders(data if self.method == "POST" else None)
    10391104            h.getreply()    #can't do anything with the result
    10401105        except (KeyboardInterrupt, SystemExit):
     
    10831148        This version just zaps the buffer to empty.
    10841149        """
    1085         self.buffer = []
     1150        self.acquire()
     1151        try:
     1152            self.buffer = []
     1153        finally:
     1154            self.release()
    10861155
    10871156    def close(self):
     
    11311200        different behaviour.
    11321201        """
    1133         if self.target:
    1134             for record in self.buffer:
    1135                 self.target.handle(record)
    1136             self.buffer = []
     1202        self.acquire()
     1203        try:
     1204            if self.target:
     1205                for record in self.buffer:
     1206                    self.target.handle(record)
     1207                self.buffer = []
     1208        finally:
     1209            self.release()
    11371210
    11381211    def close(self):
     
    11411214        """
    11421215        self.flush()
    1143         self.target = None
    1144         BufferingHandler.close(self)
     1216        self.acquire()
     1217        try:
     1218            self.target = None
     1219            BufferingHandler.close(self)
     1220        finally:
     1221            self.release()
Note: See TracChangeset for help on using the changeset viewer.