Home | History | Annotate | Download | only in logging
      1 # Copyright 2001-2016 by Vinay Sajip. All Rights Reserved.
      2 #
      3 # Permission to use, copy, modify, and distribute this software and its
      4 # documentation for any purpose and without fee is hereby granted,
      5 # provided that the above copyright notice appear in all copies and that
      6 # both that copyright notice and this permission notice appear in
      7 # supporting documentation, and that the name of Vinay Sajip
      8 # not be used in advertising or publicity pertaining to distribution
      9 # of the software without specific, written prior permission.
     10 # VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     11 # ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
     12 # VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     13 # ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     14 # IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     15 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16 
     17 """
     18 Additional handlers for the logging package for Python. The core package is
     19 based on PEP 282 and comments thereto in comp.lang.python.
     20 
     21 Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved.
     22 
     23 To use, simply 'import logging.handlers' and log away!
     24 """
     25 
     26 import logging, socket, os, pickle, struct, time, re
     27 from stat import ST_DEV, ST_INO, ST_MTIME
     28 import queue
     29 try:
     30     import threading
     31 except ImportError: #pragma: no cover
     32     threading = None
     33 
     34 #
     35 # Some constants...
     36 #
     37 
     38 DEFAULT_TCP_LOGGING_PORT    = 9020
     39 DEFAULT_UDP_LOGGING_PORT    = 9021
     40 DEFAULT_HTTP_LOGGING_PORT   = 9022
     41 DEFAULT_SOAP_LOGGING_PORT   = 9023
     42 SYSLOG_UDP_PORT             = 514
     43 SYSLOG_TCP_PORT             = 514
     44 
     45 _MIDNIGHT = 24 * 60 * 60  # number of seconds in a day
     46 
     47 class BaseRotatingHandler(logging.FileHandler):
     48     """
     49     Base class for handlers that rotate log files at a certain point.
     50     Not meant to be instantiated directly.  Instead, use RotatingFileHandler
     51     or TimedRotatingFileHandler.
     52     """
     53     def __init__(self, filename, mode, encoding=None, delay=False):
     54         """
     55         Use the specified filename for streamed logging
     56         """
     57         logging.FileHandler.__init__(self, filename, mode, encoding, delay)
     58         self.mode = mode
     59         self.encoding = encoding
     60         self.namer = None
     61         self.rotator = None
     62 
     63     def emit(self, record):
     64         """
     65         Emit a record.
     66 
     67         Output the record to the file, catering for rollover as described
     68         in doRollover().
     69         """
     70         try:
     71             if self.shouldRollover(record):
     72                 self.doRollover()
     73             logging.FileHandler.emit(self, record)
     74         except Exception:
     75             self.handleError(record)
     76 
     77     def rotation_filename(self, default_name):
     78         """
     79         Modify the filename of a log file when rotating.
     80 
     81         This is provided so that a custom filename can be provided.
     82 
     83         The default implementation calls the 'namer' attribute of the
     84         handler, if it's callable, passing the default name to
     85         it. If the attribute isn't callable (the default is None), the name
     86         is returned unchanged.
     87 
     88         :param default_name: The default name for the log file.
     89         """
     90         if not callable(self.namer):
     91             result = default_name
     92         else:
     93             result = self.namer(default_name)
     94         return result
     95 
     96     def rotate(self, source, dest):
     97         """
     98         When rotating, rotate the current log.
     99 
    100         The default implementation calls the 'rotator' attribute of the
    101         handler, if it's callable, passing the source and dest arguments to
    102         it. If the attribute isn't callable (the default is None), the source
    103         is simply renamed to the destination.
    104 
    105         :param source: The source filename. This is normally the base
    106                        filename, e.g. 'test.log'
    107         :param dest:   The destination filename. This is normally
    108                        what the source is rotated to, e.g. 'test.log.1'.
    109         """
    110         if not callable(self.rotator):
    111             # Issue 18940: A file may not have been created if delay is True.
    112             if os.path.exists(source):
    113                 os.rename(source, dest)
    114         else:
    115             self.rotator(source, dest)
    116 
    117 class RotatingFileHandler(BaseRotatingHandler):
    118     """
    119     Handler for logging to a set of files, which switches from one file
    120     to the next when the current file reaches a certain size.
    121     """
    122     def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False):
    123         """
    124         Open the specified file and use it as the stream for logging.
    125 
    126         By default, the file grows indefinitely. You can specify particular
    127         values of maxBytes and backupCount to allow the file to rollover at
    128         a predetermined size.
    129 
    130         Rollover occurs whenever the current log file is nearly maxBytes in
    131         length. If backupCount is >= 1, the system will successively create
    132         new files with the same pathname as the base file, but with extensions
    133         ".1", ".2" etc. appended to it. For example, with a backupCount of 5
    134         and a base file name of "app.log", you would get "app.log",
    135         "app.log.1", "app.log.2", ... through to "app.log.5". The file being
    136         written to is always "app.log" - when it gets filled up, it is closed
    137         and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
    138         exist, then they are renamed to "app.log.2", "app.log.3" etc.
    139         respectively.
    140 
    141         If maxBytes is zero, rollover never occurs.
    142         """
    143         # If rotation/rollover is wanted, it doesn't make sense to use another
    144         # mode. If for example 'w' were specified, then if there were multiple
    145         # runs of the calling application, the logs from previous runs would be
    146         # lost if the 'w' is respected, because the log file would be truncated
    147         # on each run.
    148         if maxBytes > 0:
    149             mode = 'a'
    150         BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
    151         self.maxBytes = maxBytes
    152         self.backupCount = backupCount
    153 
    154     def doRollover(self):
    155         """
    156         Do a rollover, as described in __init__().
    157         """
    158         if self.stream:
    159             self.stream.close()
    160             self.stream = None
    161         if self.backupCount > 0:
    162             for i in range(self.backupCount - 1, 0, -1):
    163                 sfn = self.rotation_filename("%s.%d" % (self.baseFilename, i))
    164                 dfn = self.rotation_filename("%s.%d" % (self.baseFilename,
    165                                                         i + 1))
    166                 if os.path.exists(sfn):
    167                     if os.path.exists(dfn):
    168                         os.remove(dfn)
    169                     os.rename(sfn, dfn)
    170             dfn = self.rotation_filename(self.baseFilename + ".1")
    171             if os.path.exists(dfn):
    172                 os.remove(dfn)
    173             self.rotate(self.baseFilename, dfn)
    174         if not self.delay:
    175             self.stream = self._open()
    176 
    177     def shouldRollover(self, record):
    178         """
    179         Determine if rollover should occur.
    180 
    181         Basically, see if the supplied record would cause the file to exceed
    182         the size limit we have.
    183         """
    184         if self.stream is None:                 # delay was set...
    185             self.stream = self._open()
    186         if self.maxBytes > 0:                   # are we rolling over?
    187             msg = "%s\n" % self.format(record)
    188             self.stream.seek(0, 2)  #due to non-posix-compliant Windows feature
    189             if self.stream.tell() + len(msg) >= self.maxBytes:
    190                 return 1
    191         return 0
    192 
    193 class TimedRotatingFileHandler(BaseRotatingHandler):
    194     """
    195     Handler for logging to a file, rotating the log file at certain timed
    196     intervals.
    197 
    198     If backupCount is > 0, when rollover is done, no more than backupCount
    199     files are kept - the oldest ones are deleted.
    200     """
    201     def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None):
    202         BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
    203         self.when = when.upper()
    204         self.backupCount = backupCount
    205         self.utc = utc
    206         self.atTime = atTime
    207         # Calculate the real rollover interval, which is just the number of
    208         # seconds between rollovers.  Also set the filename suffix used when
    209         # a rollover occurs.  Current 'when' events supported:
    210         # S - Seconds
    211         # M - Minutes
    212         # H - Hours
    213         # D - Days
    214         # midnight - roll over at midnight
    215         # W{0-6} - roll over on a certain day; 0 - Monday
    216         #
    217         # Case of the 'when' specifier is not important; lower or upper case
    218         # will work.
    219         if self.when == 'S':
    220             self.interval = 1 # one second
    221             self.suffix = "%Y-%m-%d_%H-%M-%S"
    222             self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
    223         elif self.when == 'M':
    224             self.interval = 60 # one minute
    225             self.suffix = "%Y-%m-%d_%H-%M"
    226             self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
    227         elif self.when == 'H':
    228             self.interval = 60 * 60 # one hour
    229             self.suffix = "%Y-%m-%d_%H"
    230             self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
    231         elif self.when == 'D' or self.when == 'MIDNIGHT':
    232             self.interval = 60 * 60 * 24 # one day
    233             self.suffix = "%Y-%m-%d"
    234             self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
    235         elif self.when.startswith('W'):
    236             self.interval = 60 * 60 * 24 * 7 # one week
    237             if len(self.when) != 2:
    238                 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
    239             if self.when[1] < '0' or self.when[1] > '6':
    240                 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
    241             self.dayOfWeek = int(self.when[1])
    242             self.suffix = "%Y-%m-%d"
    243             self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
    244         else:
    245             raise ValueError("Invalid rollover interval specified: %s" % self.when)
    246 
    247         self.extMatch = re.compile(self.extMatch, re.ASCII)
    248         self.interval = self.interval * interval # multiply by units requested
    249         # The following line added because the filename passed in could be a
    250         # path object (see Issue #27493), but self.baseFilename will be a string
    251         filename = self.baseFilename
    252         if os.path.exists(filename):
    253             t = os.stat(filename)[ST_MTIME]
    254         else:
    255             t = int(time.time())
    256         self.rolloverAt = self.computeRollover(t)
    257 
    258     def computeRollover(self, currentTime):
    259         """
    260         Work out the rollover time based on the specified time.
    261         """
    262         result = currentTime + self.interval
    263         # If we are rolling over at midnight or weekly, then the interval is already known.
    264         # What we need to figure out is WHEN the next interval is.  In other words,
    265         # if you are rolling over at midnight, then your base interval is 1 day,
    266         # but you want to start that one day clock at midnight, not now.  So, we
    267         # have to fudge the rolloverAt value in order to trigger the first rollover
    268         # at the right time.  After that, the regular interval will take care of
    269         # the rest.  Note that this code doesn't care about leap seconds. :)
    270         if self.when == 'MIDNIGHT' or self.when.startswith('W'):
    271             # This could be done with less code, but I wanted it to be clear
    272             if self.utc:
    273                 t = time.gmtime(currentTime)
    274             else:
    275                 t = time.localtime(currentTime)
    276             currentHour = t[3]
    277             currentMinute = t[4]
    278             currentSecond = t[5]
    279             currentDay = t[6]
    280             # r is the number of seconds left between now and the next rotation
    281             if self.atTime is None:
    282                 rotate_ts = _MIDNIGHT
    283             else:
    284                 rotate_ts = ((self.atTime.hour * 60 + self.atTime.minute)*60 +
    285                     self.atTime.second)
    286 
    287             r = rotate_ts - ((currentHour * 60 + currentMinute) * 60 +
    288                 currentSecond)
    289             if r < 0:
    290                 # Rotate time is before the current time (for example when
    291                 # self.rotateAt is 13:45 and it now 14:15), rotation is
    292                 # tomorrow.
    293                 r += _MIDNIGHT
    294                 currentDay = (currentDay + 1) % 7
    295             result = currentTime + r
    296             # If we are rolling over on a certain day, add in the number of days until
    297             # the next rollover, but offset by 1 since we just calculated the time
    298             # until the next day starts.  There are three cases:
    299             # Case 1) The day to rollover is today; in this case, do nothing
    300             # Case 2) The day to rollover is further in the interval (i.e., today is
    301             #         day 2 (Wednesday) and rollover is on day 6 (Sunday).  Days to
    302             #         next rollover is simply 6 - 2 - 1, or 3.
    303             # Case 3) The day to rollover is behind us in the interval (i.e., today
    304             #         is day 5 (Saturday) and rollover is on day 3 (Thursday).
    305             #         Days to rollover is 6 - 5 + 3, or 4.  In this case, it's the
    306             #         number of days left in the current week (1) plus the number
    307             #         of days in the next week until the rollover day (3).
    308             # The calculations described in 2) and 3) above need to have a day added.
    309             # This is because the above time calculation takes us to midnight on this
    310             # day, i.e. the start of the next day.
    311             if self.when.startswith('W'):
    312                 day = currentDay # 0 is Monday
    313                 if day != self.dayOfWeek:
    314                     if day < self.dayOfWeek:
    315                         daysToWait = self.dayOfWeek - day
    316                     else:
    317                         daysToWait = 6 - day + self.dayOfWeek + 1
    318                     newRolloverAt = result + (daysToWait * (60 * 60 * 24))
    319                     if not self.utc:
    320                         dstNow = t[-1]
    321                         dstAtRollover = time.localtime(newRolloverAt)[-1]
    322                         if dstNow != dstAtRollover:
    323                             if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
    324                                 addend = -3600
    325                             else:           # DST bows out before next rollover, so we need to add an hour
    326                                 addend = 3600
    327                             newRolloverAt += addend
    328                     result = newRolloverAt
    329         return result
    330 
    331     def shouldRollover(self, record):
    332         """
    333         Determine if rollover should occur.
    334 
    335         record is not used, as we are just comparing times, but it is needed so
    336         the method signatures are the same
    337         """
    338         t = int(time.time())
    339         if t >= self.rolloverAt:
    340             return 1
    341         return 0
    342 
    343     def getFilesToDelete(self):
    344         """
    345         Determine the files to delete when rolling over.
    346 
    347         More specific than the earlier method, which just used glob.glob().
    348         """
    349         dirName, baseName = os.path.split(self.baseFilename)
    350         fileNames = os.listdir(dirName)
    351         result = []
    352         prefix = baseName + "."
    353         plen = len(prefix)
    354         for fileName in fileNames:
    355             if fileName[:plen] == prefix:
    356                 suffix = fileName[plen:]
    357                 if self.extMatch.match(suffix):
    358                     result.append(os.path.join(dirName, fileName))
    359         result.sort()
    360         if len(result) < self.backupCount:
    361             result = []
    362         else:
    363             result = result[:len(result) - self.backupCount]
    364         return result
    365 
    366     def doRollover(self):
    367         """
    368         do a rollover; in this case, a date/time stamp is appended to the filename
    369         when the rollover happens.  However, you want the file to be named for the
    370         start of the interval, not the current time.  If there is a backup count,
    371         then we have to get a list of matching filenames, sort them and remove
    372         the one with the oldest suffix.
    373         """
    374         if self.stream:
    375             self.stream.close()
    376             self.stream = None
    377         # get the time that this sequence started at and make it a TimeTuple
    378         currentTime = int(time.time())
    379         dstNow = time.localtime(currentTime)[-1]
    380         t = self.rolloverAt - self.interval
    381         if self.utc:
    382             timeTuple = time.gmtime(t)
    383         else:
    384             timeTuple = time.localtime(t)
    385             dstThen = timeTuple[-1]
    386             if dstNow != dstThen:
    387                 if dstNow:
    388                     addend = 3600
    389                 else:
    390                     addend = -3600
    391                 timeTuple = time.localtime(t + addend)
    392         dfn = self.rotation_filename(self.baseFilename + "." +
    393                                      time.strftime(self.suffix, timeTuple))
    394         if os.path.exists(dfn):
    395             os.remove(dfn)
    396         self.rotate(self.baseFilename, dfn)
    397         if self.backupCount > 0:
    398             for s in self.getFilesToDelete():
    399                 os.remove(s)
    400         if not self.delay:
    401             self.stream = self._open()
    402         newRolloverAt = self.computeRollover(currentTime)
    403         while newRolloverAt <= currentTime:
    404             newRolloverAt = newRolloverAt + self.interval
    405         #If DST changes and midnight or weekly rollover, adjust for this.
    406         if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
    407             dstAtRollover = time.localtime(newRolloverAt)[-1]
    408             if dstNow != dstAtRollover:
    409                 if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
    410                     addend = -3600
    411                 else:           # DST bows out before next rollover, so we need to add an hour
    412                     addend = 3600
    413                 newRolloverAt += addend
    414         self.rolloverAt = newRolloverAt
    415 
    416 class WatchedFileHandler(logging.FileHandler):
    417     """
    418     A handler for logging to a file, which watches the file
    419     to see if it has changed while in use. This can happen because of
    420     usage of programs such as newsyslog and logrotate which perform
    421     log file rotation. This handler, intended for use under Unix,
    422     watches the file to see if it has changed since the last emit.
    423     (A file has changed if its device or inode have changed.)
    424     If it has changed, the old file stream is closed, and the file
    425     opened to get a new stream.
    426 
    427     This handler is not appropriate for use under Windows, because
    428     under Windows open files cannot be moved or renamed - logging
    429     opens the files with exclusive locks - and so there is no need
    430     for such a handler. Furthermore, ST_INO is not supported under
    431     Windows; stat always returns zero for this value.
    432 
    433     This handler is based on a suggestion and patch by Chad J.
    434     Schroeder.
    435     """
    436     def __init__(self, filename, mode='a', encoding=None, delay=False):
    437         logging.FileHandler.__init__(self, filename, mode, encoding, delay)
    438         self.dev, self.ino = -1, -1
    439         self._statstream()
    440 
    441     def _statstream(self):
    442         if self.stream:
    443             sres = os.fstat(self.stream.fileno())
    444             self.dev, self.ino = sres[ST_DEV], sres[ST_INO]
    445 
    446     def reopenIfNeeded(self):
    447         """
    448         Reopen log file if needed.
    449 
    450         Checks if the underlying file has changed, and if it
    451         has, close the old stream and reopen the file to get the
    452         current stream.
    453         """
    454         # Reduce the chance of race conditions by stat'ing by path only
    455         # once and then fstat'ing our new fd if we opened a new log stream.
    456         # See issue #14632: Thanks to John Mulligan for the problem report
    457         # and patch.
    458         try:
    459             # stat the file by path, checking for existence
    460             sres = os.stat(self.baseFilename)
    461         except FileNotFoundError:
    462             sres = None
    463         # compare file system stat with that of our stream file handle
    464         if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
    465             if self.stream is not None:
    466                 # we have an open file handle, clean it up
    467                 self.stream.flush()
    468                 self.stream.close()
    469                 self.stream = None  # See Issue #21742: _open () might fail.
    470                 # open a new file handle and get new stat info from that fd
    471                 self.stream = self._open()
    472                 self._statstream()
    473 
    474     def emit(self, record):
    475         """
    476         Emit a record.
    477 
    478         If underlying file has changed, reopen the file before emitting the
    479         record to it.
    480         """
    481         self.reopenIfNeeded()
    482         logging.FileHandler.emit(self, record)
    483 
    484 
    485 class SocketHandler(logging.Handler):
    486     """
    487     A handler class which writes logging records, in pickle format, to
    488     a streaming socket. The socket is kept open across logging calls.
    489     If the peer resets it, an attempt is made to reconnect on the next call.
    490     The pickle which is sent is that of the LogRecord's attribute dictionary
    491     (__dict__), so that the receiver does not need to have the logging module
    492     installed in order to process the logging event.
    493 
    494     To unpickle the record at the receiving end into a LogRecord, use the
    495     makeLogRecord function.
    496     """
    497 
    498     def __init__(self, host, port):
    499         """
    500         Initializes the handler with a specific host address and port.
    501 
    502         When the attribute *closeOnError* is set to True - if a socket error
    503         occurs, the socket is silently closed and then reopened on the next
    504         logging call.
    505         """
    506         logging.Handler.__init__(self)
    507         self.host = host
    508         self.port = port
    509         if port is None:
    510             self.address = host
    511         else:
    512             self.address = (host, port)
    513         self.sock = None
    514         self.closeOnError = False
    515         self.retryTime = None
    516         #
    517         # Exponential backoff parameters.
    518         #
    519         self.retryStart = 1.0
    520         self.retryMax = 30.0
    521         self.retryFactor = 2.0
    522 
    523     def makeSocket(self, timeout=1):
    524         """
    525         A factory method which allows subclasses to define the precise
    526         type of socket they want.
    527         """
    528         if self.port is not None:
    529             result = socket.create_connection(self.address, timeout=timeout)
    530         else:
    531             result = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    532             result.settimeout(timeout)
    533             try:
    534                 result.connect(self.address)
    535             except OSError:
    536                 result.close()  # Issue 19182
    537                 raise
    538         return result
    539 
    540     def createSocket(self):
    541         """
    542         Try to create a socket, using an exponential backoff with
    543         a max retry time. Thanks to Robert Olson for the original patch
    544         (SF #815911) which has been slightly refactored.
    545         """
    546         now = time.time()
    547         # Either retryTime is None, in which case this
    548         # is the first time back after a disconnect, or
    549         # we've waited long enough.
    550         if self.retryTime is None:
    551             attempt = True
    552         else:
    553             attempt = (now >= self.retryTime)
    554         if attempt:
    555             try:
    556                 self.sock = self.makeSocket()
    557                 self.retryTime = None # next time, no delay before trying
    558             except OSError:
    559                 #Creation failed, so set the retry time and return.
    560                 if self.retryTime is None:
    561                     self.retryPeriod = self.retryStart
    562                 else:
    563                     self.retryPeriod = self.retryPeriod * self.retryFactor
    564                     if self.retryPeriod > self.retryMax:
    565                         self.retryPeriod = self.retryMax
    566                 self.retryTime = now + self.retryPeriod
    567 
    568     def send(self, s):
    569         """
    570         Send a pickled string to the socket.
    571 
    572         This function allows for partial sends which can happen when the
    573         network is busy.
    574         """
    575         if self.sock is None:
    576             self.createSocket()
    577         #self.sock can be None either because we haven't reached the retry
    578         #time yet, or because we have reached the retry time and retried,
    579         #but are still unable to connect.
    580         if self.sock:
    581             try:
    582                 self.sock.sendall(s)
    583             except OSError: #pragma: no cover
    584                 self.sock.close()
    585                 self.sock = None  # so we can call createSocket next time
    586 
    587     def makePickle(self, record):
    588         """
    589         Pickles the record in binary format with a length prefix, and
    590         returns it ready for transmission across the socket.
    591         """
    592         ei = record.exc_info
    593         if ei:
    594             # just to get traceback text into record.exc_text ...
    595             dummy = self.format(record)
    596         # See issue #14436: If msg or args are objects, they may not be
    597         # available on the receiving end. So we convert the msg % args
    598         # to a string, save it as msg and zap the args.
    599         d = dict(record.__dict__)
    600         d['msg'] = record.getMessage()
    601         d['args'] = None
    602         d['exc_info'] = None
    603         # Issue #25685: delete 'message' if present: redundant with 'msg'
    604         d.pop('message', None)
    605         s = pickle.dumps(d, 1)
    606         slen = struct.pack(">L", len(s))
    607         return slen + s
    608 
    609     def handleError(self, record):
    610         """
    611         Handle an error during logging.
    612 
    613         An error has occurred during logging. Most likely cause -
    614         connection lost. Close the socket so that we can retry on the
    615         next event.
    616         """
    617         if self.closeOnError and self.sock:
    618             self.sock.close()
    619             self.sock = None        #try to reconnect next time
    620         else:
    621             logging.Handler.handleError(self, record)
    622 
    623     def emit(self, record):
    624         """
    625         Emit a record.
    626 
    627         Pickles the record and writes it to the socket in binary format.
    628         If there is an error with the socket, silently drop the packet.
    629         If there was a problem with the socket, re-establishes the
    630         socket.
    631         """
    632         try:
    633             s = self.makePickle(record)
    634             self.send(s)
    635         except Exception:
    636             self.handleError(record)
    637 
    638     def close(self):
    639         """
    640         Closes the socket.
    641         """
    642         self.acquire()
    643         try:
    644             sock = self.sock
    645             if sock:
    646                 self.sock = None
    647                 sock.close()
    648             logging.Handler.close(self)
    649         finally:
    650             self.release()
    651 
    652 class DatagramHandler(SocketHandler):
    653     """
    654     A handler class which writes logging records, in pickle format, to
    655     a datagram socket.  The pickle which is sent is that of the LogRecord's
    656     attribute dictionary (__dict__), so that the receiver does not need to
    657     have the logging module installed in order to process the logging event.
    658 
    659     To unpickle the record at the receiving end into a LogRecord, use the
    660     makeLogRecord function.
    661 
    662     """
    663     def __init__(self, host, port):
    664         """
    665         Initializes the handler with a specific host address and port.
    666         """
    667         SocketHandler.__init__(self, host, port)
    668         self.closeOnError = False
    669 
    670     def makeSocket(self):
    671         """
    672         The factory method of SocketHandler is here overridden to create
    673         a UDP socket (SOCK_DGRAM).
    674         """
    675         if self.port is None:
    676             family = socket.AF_UNIX
    677         else:
    678             family = socket.AF_INET
    679         s = socket.socket(family, socket.SOCK_DGRAM)
    680         return s
    681 
    682     def send(self, s):
    683         """
    684         Send a pickled string to a socket.
    685 
    686         This function no longer allows for partial sends which can happen
    687         when the network is busy - UDP does not guarantee delivery and
    688         can deliver packets out of sequence.
    689         """
    690         if self.sock is None:
    691             self.createSocket()
    692         self.sock.sendto(s, self.address)
    693 
    694 class SysLogHandler(logging.Handler):
    695     """
    696     A handler class which sends formatted logging records to a syslog
    697     server. Based on Sam Rushing's syslog module:
    698     http://www.nightmare.com/squirl/python-ext/misc/syslog.py
    699     Contributed by Nicolas Untz (after which minor refactoring changes
    700     have been made).
    701     """
    702 
    703     # from <linux/sys/syslog.h>:
    704     # ======================================================================
    705     # priorities/facilities are encoded into a single 32-bit quantity, where
    706     # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
    707     # facility (0-big number). Both the priorities and the facilities map
    708     # roughly one-to-one to strings in the syslogd(8) source code.  This
    709     # mapping is included in this file.
    710     #
    711     # priorities (these are ordered)
    712 
    713     LOG_EMERG     = 0       #  system is unusable
    714     LOG_ALERT     = 1       #  action must be taken immediately
    715     LOG_CRIT      = 2       #  critical conditions
    716     LOG_ERR       = 3       #  error conditions
    717     LOG_WARNING   = 4       #  warning conditions
    718     LOG_NOTICE    = 5       #  normal but significant condition
    719     LOG_INFO      = 6       #  informational
    720     LOG_DEBUG     = 7       #  debug-level messages
    721 
    722     #  facility codes
    723     LOG_KERN      = 0       #  kernel messages
    724     LOG_USER      = 1       #  random user-level messages
    725     LOG_MAIL      = 2       #  mail system
    726     LOG_DAEMON    = 3       #  system daemons
    727     LOG_AUTH      = 4       #  security/authorization messages
    728     LOG_SYSLOG    = 5       #  messages generated internally by syslogd
    729     LOG_LPR       = 6       #  line printer subsystem
    730     LOG_NEWS      = 7       #  network news subsystem
    731     LOG_UUCP      = 8       #  UUCP subsystem
    732     LOG_CRON      = 9       #  clock daemon
    733     LOG_AUTHPRIV  = 10      #  security/authorization messages (private)
    734     LOG_FTP       = 11      #  FTP daemon
    735 
    736     #  other codes through 15 reserved for system use
    737     LOG_LOCAL0    = 16      #  reserved for local use
    738     LOG_LOCAL1    = 17      #  reserved for local use
    739     LOG_LOCAL2    = 18      #  reserved for local use
    740     LOG_LOCAL3    = 19      #  reserved for local use
    741     LOG_LOCAL4    = 20      #  reserved for local use
    742     LOG_LOCAL5    = 21      #  reserved for local use
    743     LOG_LOCAL6    = 22      #  reserved for local use
    744     LOG_LOCAL7    = 23      #  reserved for local use
    745 
    746     priority_names = {
    747         "alert":    LOG_ALERT,
    748         "crit":     LOG_CRIT,
    749         "critical": LOG_CRIT,
    750         "debug":    LOG_DEBUG,
    751         "emerg":    LOG_EMERG,
    752         "err":      LOG_ERR,
    753         "error":    LOG_ERR,        #  DEPRECATED
    754         "info":     LOG_INFO,
    755         "notice":   LOG_NOTICE,
    756         "panic":    LOG_EMERG,      #  DEPRECATED
    757         "warn":     LOG_WARNING,    #  DEPRECATED
    758         "warning":  LOG_WARNING,
    759         }
    760 
    761     facility_names = {
    762         "auth":     LOG_AUTH,
    763         "authpriv": LOG_AUTHPRIV,
    764         "cron":     LOG_CRON,
    765         "daemon":   LOG_DAEMON,
    766         "ftp":      LOG_FTP,
    767         "kern":     LOG_KERN,
    768         "lpr":      LOG_LPR,
    769         "mail":     LOG_MAIL,
    770         "news":     LOG_NEWS,
    771         "security": LOG_AUTH,       #  DEPRECATED
    772         "syslog":   LOG_SYSLOG,
    773         "user":     LOG_USER,
    774         "uucp":     LOG_UUCP,
    775         "local0":   LOG_LOCAL0,
    776         "local1":   LOG_LOCAL1,
    777         "local2":   LOG_LOCAL2,
    778         "local3":   LOG_LOCAL3,
    779         "local4":   LOG_LOCAL4,
    780         "local5":   LOG_LOCAL5,
    781         "local6":   LOG_LOCAL6,
    782         "local7":   LOG_LOCAL7,
    783         }
    784 
    785     #The map below appears to be trivially lowercasing the key. However,
    786     #there's more to it than meets the eye - in some locales, lowercasing
    787     #gives unexpected results. See SF #1524081: in the Turkish locale,
    788     #"INFO".lower() != "info"
    789     priority_map = {
    790         "DEBUG" : "debug",
    791         "INFO" : "info",
    792         "WARNING" : "warning",
    793         "ERROR" : "error",
    794         "CRITICAL" : "critical"
    795     }
    796 
    797     def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
    798                  facility=LOG_USER, socktype=None):
    799         """
    800         Initialize a handler.
    801 
    802         If address is specified as a string, a UNIX socket is used. To log to a
    803         local syslogd, "SysLogHandler(address="/dev/log")" can be used.
    804         If facility is not specified, LOG_USER is used. If socktype is
    805         specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
    806         socket type will be used. For Unix sockets, you can also specify a
    807         socktype of None, in which case socket.SOCK_DGRAM will be used, falling
    808         back to socket.SOCK_STREAM.
    809         """
    810         logging.Handler.__init__(self)
    811 
    812         self.address = address
    813         self.facility = facility
    814         self.socktype = socktype
    815 
    816         if isinstance(address, str):
    817             self.unixsocket = True
    818             self._connect_unixsocket(address)
    819         else:
    820             self.unixsocket = False
    821             if socktype is None:
    822                 socktype = socket.SOCK_DGRAM
    823             self.socket = socket.socket(socket.AF_INET, socktype)
    824             if socktype == socket.SOCK_STREAM:
    825                 self.socket.connect(address)
    826             self.socktype = socktype
    827         self.formatter = None
    828 
    829     def _connect_unixsocket(self, address):
    830         use_socktype = self.socktype
    831         if use_socktype is None:
    832             use_socktype = socket.SOCK_DGRAM
    833         self.socket = socket.socket(socket.AF_UNIX, use_socktype)
    834         try:
    835             self.socket.connect(address)
    836             # it worked, so set self.socktype to the used type
    837             self.socktype = use_socktype
    838         except OSError:
    839             self.socket.close()
    840             if self.socktype is not None:
    841                 # user didn't specify falling back, so fail
    842                 raise
    843             use_socktype = socket.SOCK_STREAM
    844             self.socket = socket.socket(socket.AF_UNIX, use_socktype)
    845             try:
    846                 self.socket.connect(address)
    847                 # it worked, so set self.socktype to the used type
    848                 self.socktype = use_socktype
    849             except OSError:
    850                 self.socket.close()
    851                 raise
    852 
    853     def encodePriority(self, facility, priority):
    854         """
    855         Encode the facility and priority. You can pass in strings or
    856         integers - if strings are passed, the facility_names and
    857         priority_names mapping dictionaries are used to convert them to
    858         integers.
    859         """
    860         if isinstance(facility, str):
    861             facility = self.facility_names[facility]
    862         if isinstance(priority, str):
    863             priority = self.priority_names[priority]
    864         return (facility << 3) | priority
    865 
    866     def close (self):
    867         """
    868         Closes the socket.
    869         """
    870         self.acquire()
    871         try:
    872             self.socket.close()
    873             logging.Handler.close(self)
    874         finally:
    875             self.release()
    876 
    877     def mapPriority(self, levelName):
    878         """
    879         Map a logging level name to a key in the priority_names map.
    880         This is useful in two scenarios: when custom levels are being
    881         used, and in the case where you can't do a straightforward
    882         mapping by lowercasing the logging level name because of locale-
    883         specific issues (see SF #1524081).
    884         """
    885         return self.priority_map.get(levelName, "warning")
    886 
    887     ident = ''          # prepended to all messages
    888     append_nul = True   # some old syslog daemons expect a NUL terminator
    889 
    890     def emit(self, record):
    891         """
    892         Emit a record.
    893 
    894         The record is formatted, and then sent to the syslog server. If
    895         exception information is present, it is NOT sent to the server.
    896         """
    897         try:
    898             msg = self.format(record)
    899             if self.ident:
    900                 msg = self.ident + msg
    901             if self.append_nul:
    902                 msg += '\000'
    903 
    904             # We need to convert record level to lowercase, maybe this will
    905             # change in the future.
    906             prio = '<%d>' % self.encodePriority(self.facility,
    907                                                 self.mapPriority(record.levelname))
    908             prio = prio.encode('utf-8')
    909             # Message is a string. Convert to bytes as required by RFC 5424
    910             msg = msg.encode('utf-8')
    911             msg = prio + msg
    912             if self.unixsocket:
    913                 try:
    914                     self.socket.send(msg)
    915                 except OSError:
    916                     self.socket.close()
    917                     self._connect_unixsocket(self.address)
    918                     self.socket.send(msg)
    919             elif self.socktype == socket.SOCK_DGRAM:
    920                 self.socket.sendto(msg, self.address)
    921             else:
    922                 self.socket.sendall(msg)
    923         except Exception:
    924             self.handleError(record)
    925 
    926 class SMTPHandler(logging.Handler):
    927     """
    928     A handler class which sends an SMTP email for each logging event.
    929     """
    930     def __init__(self, mailhost, fromaddr, toaddrs, subject,
    931                  credentials=None, secure=None, timeout=5.0):
    932         """
    933         Initialize the handler.
    934 
    935         Initialize the instance with the from and to addresses and subject
    936         line of the email. To specify a non-standard SMTP port, use the
    937         (host, port) tuple format for the mailhost argument. To specify
    938         authentication credentials, supply a (username, password) tuple
    939         for the credentials argument. To specify the use of a secure
    940         protocol (TLS), pass in a tuple for the secure argument. This will
    941         only be used when authentication credentials are supplied. The tuple
    942         will be either an empty tuple, or a single-value tuple with the name
    943         of a keyfile, or a 2-value tuple with the names of the keyfile and
    944         certificate file. (This tuple is passed to the `starttls` method).
    945         A timeout in seconds can be specified for the SMTP connection (the
    946         default is one second).
    947         """
    948         logging.Handler.__init__(self)
    949         if isinstance(mailhost, (list, tuple)):
    950             self.mailhost, self.mailport = mailhost
    951         else:
    952             self.mailhost, self.mailport = mailhost, None
    953         if isinstance(credentials, (list, tuple)):
    954             self.username, self.password = credentials
    955         else:
    956             self.username = None
    957         self.fromaddr = fromaddr
    958         if isinstance(toaddrs, str):
    959             toaddrs = [toaddrs]
    960         self.toaddrs = toaddrs
    961         self.subject = subject
    962         self.secure = secure
    963         self.timeout = timeout
    964 
    965     def getSubject(self, record):
    966         """
    967         Determine the subject for the email.
    968 
    969         If you want to specify a subject line which is record-dependent,
    970         override this method.
    971         """
    972         return self.subject
    973 
    974     def emit(self, record):
    975         """
    976         Emit a record.
    977 
    978         Format the record and send it to the specified addressees.
    979         """
    980         try:
    981             import smtplib
    982             from email.message import EmailMessage
    983             import email.utils
    984 
    985             port = self.mailport
    986             if not port:
    987                 port = smtplib.SMTP_PORT
    988             smtp = smtplib.SMTP(self.mailhost, port, timeout=self.timeout)
    989             msg = EmailMessage()
    990             msg['From'] = self.fromaddr
    991             msg['To'] = ','.join(self.toaddrs)
    992             msg['Subject'] = self.getSubject(record)
    993             msg['Date'] = email.utils.localtime()
    994             msg.set_content(self.format(record))
    995             if self.username:
    996                 if self.secure is not None:
    997                     smtp.ehlo()
    998                     smtp.starttls(*self.secure)
    999                     smtp.ehlo()
   1000                 smtp.login(self.username, self.password)
   1001             smtp.send_message(msg)
   1002             smtp.quit()
   1003         except Exception:
   1004             self.handleError(record)
   1005 
   1006 class NTEventLogHandler(logging.Handler):
   1007     """
   1008     A handler class which sends events to the NT Event Log. Adds a
   1009     registry entry for the specified application name. If no dllname is
   1010     provided, win32service.pyd (which contains some basic message
   1011     placeholders) is used. Note that use of these placeholders will make
   1012     your event logs big, as the entire message source is held in the log.
   1013     If you want slimmer logs, you have to pass in the name of your own DLL
   1014     which contains the message definitions you want to use in the event log.
   1015     """
   1016     def __init__(self, appname, dllname=None, logtype="Application"):
   1017         logging.Handler.__init__(self)
   1018         try:
   1019             import win32evtlogutil, win32evtlog
   1020             self.appname = appname
   1021             self._welu = win32evtlogutil
   1022             if not dllname:
   1023                 dllname = os.path.split(self._welu.__file__)
   1024                 dllname = os.path.split(dllname[0])
   1025                 dllname = os.path.join(dllname[0], r'win32service.pyd')
   1026             self.dllname = dllname
   1027             self.logtype = logtype
   1028             self._welu.AddSourceToRegistry(appname, dllname, logtype)
   1029             self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
   1030             self.typemap = {
   1031                 logging.DEBUG   : win32evtlog.EVENTLOG_INFORMATION_TYPE,
   1032                 logging.INFO    : win32evtlog.EVENTLOG_INFORMATION_TYPE,
   1033                 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
   1034                 logging.ERROR   : win32evtlog.EVENTLOG_ERROR_TYPE,
   1035                 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
   1036          }
   1037         except ImportError:
   1038             print("The Python Win32 extensions for NT (service, event "\
   1039                         "logging) appear not to be available.")
   1040             self._welu = None
   1041 
   1042     def getMessageID(self, record):
   1043         """
   1044         Return the message ID for the event record. If you are using your
   1045         own messages, you could do this by having the msg passed to the
   1046         logger being an ID rather than a formatting string. Then, in here,
   1047         you could use a dictionary lookup to get the message ID. This
   1048         version returns 1, which is the base message ID in win32service.pyd.
   1049         """
   1050         return 1
   1051 
   1052     def getEventCategory(self, record):
   1053         """
   1054         Return the event category for the record.
   1055 
   1056         Override this if you want to specify your own categories. This version
   1057         returns 0.
   1058         """
   1059         return 0
   1060 
   1061     def getEventType(self, record):
   1062         """
   1063         Return the event type for the record.
   1064 
   1065         Override this if you want to specify your own types. This version does
   1066         a mapping using the handler's typemap attribute, which is set up in
   1067         __init__() to a dictionary which contains mappings for DEBUG, INFO,
   1068         WARNING, ERROR and CRITICAL. If you are using your own levels you will
   1069         either need to override this method or place a suitable dictionary in
   1070         the handler's typemap attribute.
   1071         """
   1072         return self.typemap.get(record.levelno, self.deftype)
   1073 
   1074     def emit(self, record):
   1075         """
   1076         Emit a record.
   1077 
   1078         Determine the message ID, event category and event type. Then
   1079         log the message in the NT event log.
   1080         """
   1081         if self._welu:
   1082             try:
   1083                 id = self.getMessageID(record)
   1084                 cat = self.getEventCategory(record)
   1085                 type = self.getEventType(record)
   1086                 msg = self.format(record)
   1087                 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
   1088             except Exception:
   1089                 self.handleError(record)
   1090 
   1091     def close(self):
   1092         """
   1093         Clean up this handler.
   1094 
   1095         You can remove the application name from the registry as a
   1096         source of event log entries. However, if you do this, you will
   1097         not be able to see the events as you intended in the Event Log
   1098         Viewer - it needs to be able to access the registry to get the
   1099         DLL name.
   1100         """
   1101         #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
   1102         logging.Handler.close(self)
   1103 
   1104 class HTTPHandler(logging.Handler):
   1105     """
   1106     A class which sends records to a Web server, using either GET or
   1107     POST semantics.
   1108     """
   1109     def __init__(self, host, url, method="GET", secure=False, credentials=None,
   1110                  context=None):
   1111         """
   1112         Initialize the instance with the host, the request URL, and the method
   1113         ("GET" or "POST")
   1114         """
   1115         logging.Handler.__init__(self)
   1116         method = method.upper()
   1117         if method not in ["GET", "POST"]:
   1118             raise ValueError("method must be GET or POST")
   1119         if not secure and context is not None:
   1120             raise ValueError("context parameter only makes sense "
   1121                              "with secure=True")
   1122         self.host = host
   1123         self.url = url
   1124         self.method = method
   1125         self.secure = secure
   1126         self.credentials = credentials
   1127         self.context = context
   1128 
   1129     def mapLogRecord(self, record):
   1130         """
   1131         Default implementation of mapping the log record into a dict
   1132         that is sent as the CGI data. Overwrite in your class.
   1133         Contributed by Franz Glasner.
   1134         """
   1135         return record.__dict__
   1136 
   1137     def emit(self, record):
   1138         """
   1139         Emit a record.
   1140 
   1141         Send the record to the Web server as a percent-encoded dictionary
   1142         """
   1143         try:
   1144             import http.client, urllib.parse
   1145             host = self.host
   1146             if self.secure:
   1147                 h = http.client.HTTPSConnection(host, context=self.context)
   1148             else:
   1149                 h = http.client.HTTPConnection(host)
   1150             url = self.url
   1151             data = urllib.parse.urlencode(self.mapLogRecord(record))
   1152             if self.method == "GET":
   1153                 if (url.find('?') >= 0):
   1154                     sep = '&'
   1155                 else:
   1156                     sep = '?'
   1157                 url = url + "%c%s" % (sep, data)
   1158             h.putrequest(self.method, url)
   1159             # support multiple hosts on one IP address...
   1160             # need to strip optional :port from host, if present
   1161             i = host.find(":")
   1162             if i >= 0:
   1163                 host = host[:i]
   1164             h.putheader("Host", host)
   1165             if self.method == "POST":
   1166                 h.putheader("Content-type",
   1167                             "application/x-www-form-urlencoded")
   1168                 h.putheader("Content-length", str(len(data)))
   1169             if self.credentials:
   1170                 import base64
   1171                 s = ('%s:%s' % self.credentials).encode('utf-8')
   1172                 s = 'Basic ' + base64.b64encode(s).strip().decode('ascii')
   1173                 h.putheader('Authorization', s)
   1174             h.endheaders()
   1175             if self.method == "POST":
   1176                 h.send(data.encode('utf-8'))
   1177             h.getresponse()    #can't do anything with the result
   1178         except Exception:
   1179             self.handleError(record)
   1180 
   1181 class BufferingHandler(logging.Handler):
   1182     """
   1183   A handler class which buffers logging records in memory. Whenever each
   1184   record is added to the buffer, a check is made to see if the buffer should
   1185   be flushed. If it should, then flush() is expected to do what's needed.
   1186     """
   1187     def __init__(self, capacity):
   1188         """
   1189         Initialize the handler with the buffer size.
   1190         """
   1191         logging.Handler.__init__(self)
   1192         self.capacity = capacity
   1193         self.buffer = []
   1194 
   1195     def shouldFlush(self, record):
   1196         """
   1197         Should the handler flush its buffer?
   1198 
   1199         Returns true if the buffer is up to capacity. This method can be
   1200         overridden to implement custom flushing strategies.
   1201         """
   1202         return (len(self.buffer) >= self.capacity)
   1203 
   1204     def emit(self, record):
   1205         """
   1206         Emit a record.
   1207 
   1208         Append the record. If shouldFlush() tells us to, call flush() to process
   1209         the buffer.
   1210         """
   1211         self.buffer.append(record)
   1212         if self.shouldFlush(record):
   1213             self.flush()
   1214 
   1215     def flush(self):
   1216         """
   1217         Override to implement custom flushing behaviour.
   1218 
   1219         This version just zaps the buffer to empty.
   1220         """
   1221         self.acquire()
   1222         try:
   1223             self.buffer = []
   1224         finally:
   1225             self.release()
   1226 
   1227     def close(self):
   1228         """
   1229         Close the handler.
   1230 
   1231         This version just flushes and chains to the parent class' close().
   1232         """
   1233         try:
   1234             self.flush()
   1235         finally:
   1236             logging.Handler.close(self)
   1237 
   1238 class MemoryHandler(BufferingHandler):
   1239     """
   1240     A handler class which buffers logging records in memory, periodically
   1241     flushing them to a target handler. Flushing occurs whenever the buffer
   1242     is full, or when an event of a certain severity or greater is seen.
   1243     """
   1244     def __init__(self, capacity, flushLevel=logging.ERROR, target=None,
   1245                  flushOnClose=True):
   1246         """
   1247         Initialize the handler with the buffer size, the level at which
   1248         flushing should occur and an optional target.
   1249 
   1250         Note that without a target being set either here or via setTarget(),
   1251         a MemoryHandler is no use to anyone!
   1252 
   1253         The ``flushOnClose`` argument is ``True`` for backward compatibility
   1254         reasons - the old behaviour is that when the handler is closed, the
   1255         buffer is flushed, even if the flush level hasn't been exceeded nor the
   1256         capacity exceeded. To prevent this, set ``flushOnClose`` to ``False``.
   1257         """
   1258         BufferingHandler.__init__(self, capacity)
   1259         self.flushLevel = flushLevel
   1260         self.target = target
   1261         # See Issue #26559 for why this has been added
   1262         self.flushOnClose = flushOnClose
   1263 
   1264     def shouldFlush(self, record):
   1265         """
   1266         Check for buffer full or a record at the flushLevel or higher.
   1267         """
   1268         return (len(self.buffer) >= self.capacity) or \
   1269                 (record.levelno >= self.flushLevel)
   1270 
   1271     def setTarget(self, target):
   1272         """
   1273         Set the target handler for this handler.
   1274         """
   1275         self.target = target
   1276 
   1277     def flush(self):
   1278         """
   1279         For a MemoryHandler, flushing means just sending the buffered
   1280         records to the target, if there is one. Override if you want
   1281         different behaviour.
   1282 
   1283         The record buffer is also cleared by this operation.
   1284         """
   1285         self.acquire()
   1286         try:
   1287             if self.target:
   1288                 for record in self.buffer:
   1289                     self.target.handle(record)
   1290                 self.buffer = []
   1291         finally:
   1292             self.release()
   1293 
   1294     def close(self):
   1295         """
   1296         Flush, if appropriately configured, set the target to None and lose the
   1297         buffer.
   1298         """
   1299         try:
   1300             if self.flushOnClose:
   1301                 self.flush()
   1302         finally:
   1303             self.acquire()
   1304             try:
   1305                 self.target = None
   1306                 BufferingHandler.close(self)
   1307             finally:
   1308                 self.release()
   1309 
   1310 
   1311 class QueueHandler(logging.Handler):
   1312     """
   1313     This handler sends events to a queue. Typically, it would be used together
   1314     with a multiprocessing Queue to centralise logging to file in one process
   1315     (in a multi-process application), so as to avoid file write contention
   1316     between processes.
   1317 
   1318     This code is new in Python 3.2, but this class can be copy pasted into
   1319     user code for use with earlier Python versions.
   1320     """
   1321 
   1322     def __init__(self, queue):
   1323         """
   1324         Initialise an instance, using the passed queue.
   1325         """
   1326         logging.Handler.__init__(self)
   1327         self.queue = queue
   1328 
   1329     def enqueue(self, record):
   1330         """
   1331         Enqueue a record.
   1332 
   1333         The base implementation uses put_nowait. You may want to override
   1334         this method if you want to use blocking, timeouts or custom queue
   1335         implementations.
   1336         """
   1337         self.queue.put_nowait(record)
   1338 
   1339     def prepare(self, record):
   1340         """
   1341         Prepares a record for queuing. The object returned by this method is
   1342         enqueued.
   1343 
   1344         The base implementation formats the record to merge the message
   1345         and arguments, and removes unpickleable items from the record
   1346         in-place.
   1347 
   1348         You might want to override this method if you want to convert
   1349         the record to a dict or JSON string, or send a modified copy
   1350         of the record while leaving the original intact.
   1351         """
   1352         # The format operation gets traceback text into record.exc_text
   1353         # (if there's exception data), and also puts the message into
   1354         # record.message. We can then use this to replace the original
   1355         # msg + args, as these might be unpickleable. We also zap the
   1356         # exc_info attribute, as it's no longer needed and, if not None,
   1357         # will typically not be pickleable.
   1358         self.format(record)
   1359         record.msg = record.message
   1360         record.args = None
   1361         record.exc_info = None
   1362         return record
   1363 
   1364     def emit(self, record):
   1365         """
   1366         Emit a record.
   1367 
   1368         Writes the LogRecord to the queue, preparing it for pickling first.
   1369         """
   1370         try:
   1371             self.enqueue(self.prepare(record))
   1372         except Exception:
   1373             self.handleError(record)
   1374 
   1375 if threading:
   1376     class QueueListener(object):
   1377         """
   1378         This class implements an internal threaded listener which watches for
   1379         LogRecords being added to a queue, removes them and passes them to a
   1380         list of handlers for processing.
   1381         """
   1382         _sentinel = None
   1383 
   1384         def __init__(self, queue, *handlers, respect_handler_level=False):
   1385             """
   1386             Initialise an instance with the specified queue and
   1387             handlers.
   1388             """
   1389             self.queue = queue
   1390             self.handlers = handlers
   1391             self._thread = None
   1392             self.respect_handler_level = respect_handler_level
   1393 
   1394         def dequeue(self, block):
   1395             """
   1396             Dequeue a record and return it, optionally blocking.
   1397 
   1398             The base implementation uses get. You may want to override this method
   1399             if you want to use timeouts or work with custom queue implementations.
   1400             """
   1401             return self.queue.get(block)
   1402 
   1403         def start(self):
   1404             """
   1405             Start the listener.
   1406 
   1407             This starts up a background thread to monitor the queue for
   1408             LogRecords to process.
   1409             """
   1410             self._thread = t = threading.Thread(target=self._monitor)
   1411             t.daemon = True
   1412             t.start()
   1413 
   1414         def prepare(self , record):
   1415             """
   1416             Prepare a record for handling.
   1417 
   1418             This method just returns the passed-in record. You may want to
   1419             override this method if you need to do any custom marshalling or
   1420             manipulation of the record before passing it to the handlers.
   1421             """
   1422             return record
   1423 
   1424         def handle(self, record):
   1425             """
   1426             Handle a record.
   1427 
   1428             This just loops through the handlers offering them the record
   1429             to handle.
   1430             """
   1431             record = self.prepare(record)
   1432             for handler in self.handlers:
   1433                 if not self.respect_handler_level:
   1434                     process = True
   1435                 else:
   1436                     process = record.levelno >= handler.level
   1437                 if process:
   1438                     handler.handle(record)
   1439 
   1440         def _monitor(self):
   1441             """
   1442             Monitor the queue for records, and ask the handler
   1443             to deal with them.
   1444 
   1445             This method runs on a separate, internal thread.
   1446             The thread will terminate if it sees a sentinel object in the queue.
   1447             """
   1448             q = self.queue
   1449             has_task_done = hasattr(q, 'task_done')
   1450             while True:
   1451                 try:
   1452                     record = self.dequeue(True)
   1453                     if record is self._sentinel:
   1454                         break
   1455                     self.handle(record)
   1456                     if has_task_done:
   1457                         q.task_done()
   1458                 except queue.Empty:
   1459                     break
   1460 
   1461         def enqueue_sentinel(self):
   1462             """
   1463             This is used to enqueue the sentinel record.
   1464 
   1465             The base implementation uses put_nowait. You may want to override this
   1466             method if you want to use timeouts or work with custom queue
   1467             implementations.
   1468             """
   1469             self.queue.put_nowait(self._sentinel)
   1470 
   1471         def stop(self):
   1472             """
   1473             Stop the listener.
   1474 
   1475             This asks the thread to terminate, and then waits for it to do so.
   1476             Note that if you don't call this before your application exits, there
   1477             may be some records still left on the queue, which won't be processed.
   1478             """
   1479             self.enqueue_sentinel()
   1480             self._thread.join()
   1481             self._thread = None
   1482