python (3.12.0)

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