14dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip# Copyright 2001-2013 by Vinay Sajip. All Rights Reserved.
257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum#
357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# Permission to use, copy, modify, and distribute this software and its
457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# documentation for any purpose and without fee is hereby granted,
557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# provided that the above copyright notice appear in all copies and that
657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# both that copyright notice and this permission notice appear in
757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# supporting documentation, and that the name of Vinay Sajip
857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# not be used in advertising or publicity pertaining to distribution
957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# of the software without specific, written prior permission.
1057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
1257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
1457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
1757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum"""
183f74284e1baef338b34ed9a17a7b46e9607ccd5dVinay SajipAdditional handlers for the logging package for Python. The core package is
1916f6a29be8fe709c133f07150c1326451139faaeVinay Sajipbased on PEP 282 and comments thereto in comp.lang.python.
2057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
214dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay SajipCopyright (C) 2001-2013 Vinay Sajip. All Rights Reserved.
2257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
23aecf36a0b532260d38297e323c978bf6f0be2c83Vinay SajipTo use, simply 'import logging.handlers' and log away!
2457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum"""
2557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
26885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajipimport errno, logging, socket, os, cPickle, struct, time, re
279098ee43607340769d1dddf88522cc1bf9b1788fVinay Sajipfrom stat import ST_DEV, ST_INO, ST_MTIME
2857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
294600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajiptry:
304600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajip    import codecs
314600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajipexcept ImportError:
324600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajip    codecs = None
335ac6528b91382e218332d367c98f06a5ed8670ffVinay Sajiptry:
345ac6528b91382e218332d367c98f06a5ed8670ffVinay Sajip    unicode
355ac6528b91382e218332d367c98f06a5ed8670ffVinay Sajip    _unicode = True
365ac6528b91382e218332d367c98f06a5ed8670ffVinay Sajipexcept NameError:
375ac6528b91382e218332d367c98f06a5ed8670ffVinay Sajip    _unicode = False
384600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajip
3957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum#
4057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum# Some constants...
4157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum#
4257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
4357102f861d506b6c2d2215d100dac9143574fa77Guido van RossumDEFAULT_TCP_LOGGING_PORT    = 9020
4457102f861d506b6c2d2215d100dac9143574fa77Guido van RossumDEFAULT_UDP_LOGGING_PORT    = 9021
4557102f861d506b6c2d2215d100dac9143574fa77Guido van RossumDEFAULT_HTTP_LOGGING_PORT   = 9022
4657102f861d506b6c2d2215d100dac9143574fa77Guido van RossumDEFAULT_SOAP_LOGGING_PORT   = 9023
4757102f861d506b6c2d2215d100dac9143574fa77Guido van RossumSYSLOG_UDP_PORT             = 514
481c77b7f84c5fca050980854a677539ba377439ddVinay SajipSYSLOG_TCP_PORT             = 514
4957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
504b4a63e30a8f300e545f2c44b4c456c74718aa79Vinay Sajip_MIDNIGHT = 24 * 60 * 60  # number of seconds in a day
514b4a63e30a8f300e545f2c44b4c456c74718aa79Vinay Sajip
5217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajipclass BaseRotatingHandler(logging.FileHandler):
5317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    """
5417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    Base class for handlers that rotate log files at a certain point.
5517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    Not meant to be instantiated directly.  Instead, use RotatingFileHandler
5617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    or TimedRotatingFileHandler.
5717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    """
5892aa2f8d6bfe3088d3f1b322cdf546997eaaff71Vinay Sajip    def __init__(self, filename, mode, encoding=None, delay=0):
5917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
6017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        Use the specified filename for streamed logging
6117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
624600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajip        if codecs is None:
634600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajip            encoding = None
6492aa2f8d6bfe3088d3f1b322cdf546997eaaff71Vinay Sajip        logging.FileHandler.__init__(self, filename, mode, encoding, delay)
654600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajip        self.mode = mode
664600f11a0772d826c89ff67643ce04ca3c5b2c38Vinay Sajip        self.encoding = encoding
6757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
6817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    def emit(self, record):
6917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
7017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        Emit a record.
7117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
7217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        Output the record to the file, catering for rollover as described
7317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        in doRollover().
7417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
753970c11157e985966744cea2960964f5a3144f53Vinay Sajip        try:
763970c11157e985966744cea2960964f5a3144f53Vinay Sajip            if self.shouldRollover(record):
773970c11157e985966744cea2960964f5a3144f53Vinay Sajip                self.doRollover()
783970c11157e985966744cea2960964f5a3144f53Vinay Sajip            logging.FileHandler.emit(self, record)
7985c1909a78c97745cd41a56be7bce372b7d60d64Vinay Sajip        except (KeyboardInterrupt, SystemExit):
8085c1909a78c97745cd41a56be7bce372b7d60d64Vinay Sajip            raise
813970c11157e985966744cea2960964f5a3144f53Vinay Sajip        except:
823970c11157e985966744cea2960964f5a3144f53Vinay Sajip            self.handleError(record)
8317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
8417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajipclass RotatingFileHandler(BaseRotatingHandler):
8517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    """
8617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    Handler for logging to a set of files, which switches from one file
8717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    to the next when the current file reaches a certain size.
8817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    """
8992aa2f8d6bfe3088d3f1b322cdf546997eaaff71Vinay Sajip    def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0):
9057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
9157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Open the specified file and use it as the stream for logging.
9257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
9357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        By default, the file grows indefinitely. You can specify particular
9457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        values of maxBytes and backupCount to allow the file to rollover at
9557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        a predetermined size.
9657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
9757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Rollover occurs whenever the current log file is nearly maxBytes in
9857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        length. If backupCount is >= 1, the system will successively create
9957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        new files with the same pathname as the base file, but with extensions
10057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        ".1", ".2" etc. appended to it. For example, with a backupCount of 5
10157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        and a base file name of "app.log", you would get "app.log",
10257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "app.log.1", "app.log.2", ... through to "app.log.5". The file being
10357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        written to is always "app.log" - when it gets filled up, it is closed
10457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
10557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        exist, then they are renamed to "app.log.2", "app.log.3" etc.
10657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        respectively.
10757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
10857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        If maxBytes is zero, rollover never occurs.
10957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
110e8b1eafd8819bcc98d38a93a9ecfe76066c370a1Vinay Sajip        # If rotation/rollover is wanted, it doesn't make sense to use another
111e8b1eafd8819bcc98d38a93a9ecfe76066c370a1Vinay Sajip        # mode. If for example 'w' were specified, then if there were multiple
112e8b1eafd8819bcc98d38a93a9ecfe76066c370a1Vinay Sajip        # runs of the calling application, the logs from previous runs would be
113e8b1eafd8819bcc98d38a93a9ecfe76066c370a1Vinay Sajip        # lost if the 'w' is respected, because the log file would be truncated
114e8b1eafd8819bcc98d38a93a9ecfe76066c370a1Vinay Sajip        # on each run.
11517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        if maxBytes > 0:
116e8b1eafd8819bcc98d38a93a9ecfe76066c370a1Vinay Sajip            mode = 'a'
11792aa2f8d6bfe3088d3f1b322cdf546997eaaff71Vinay Sajip        BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
11857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.maxBytes = maxBytes
11957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.backupCount = backupCount
12057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
12157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def doRollover(self):
12257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
12357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Do a rollover, as described in __init__().
12457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
125aecf36a0b532260d38297e323c978bf6f0be2c83Vinay Sajip        if self.stream:
126aecf36a0b532260d38297e323c978bf6f0be2c83Vinay Sajip            self.stream.close()
127cdb8388cad74e0f2910d5b43531daf6271467292Vinay Sajip            self.stream = None
12857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        if self.backupCount > 0:
12957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            for i in range(self.backupCount - 1, 0, -1):
13057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                sfn = "%s.%d" % (self.baseFilename, i)
13157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                dfn = "%s.%d" % (self.baseFilename, i + 1)
13257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                if os.path.exists(sfn):
13357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                    #print "%s -> %s" % (sfn, dfn)
13457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                    if os.path.exists(dfn):
13557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                        os.remove(dfn)
13657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                    os.rename(sfn, dfn)
13757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            dfn = self.baseFilename + ".1"
13857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            if os.path.exists(dfn):
13957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                os.remove(dfn)
1406d7e29651cf6a2232d66e47974c522cc2927a6c2Vinay Sajip            # Issue 18940: A file may not have been created if delay is True.
1416d7e29651cf6a2232d66e47974c522cc2927a6c2Vinay Sajip            if os.path.exists(self.baseFilename):
1426d7e29651cf6a2232d66e47974c522cc2927a6c2Vinay Sajip                os.rename(self.baseFilename, dfn)
143fb03696fdabeb850cdc714cd1abb45f6d93fcdf6Vinay Sajip        if not self.delay:
144fb03696fdabeb850cdc714cd1abb45f6d93fcdf6Vinay Sajip            self.stream = self._open()
14557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
14617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    def shouldRollover(self, record):
14757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
14817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        Determine if rollover should occur.
14957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
15017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        Basically, see if the supplied record would cause the file to exceed
15117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        the size limit we have.
15257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
153aecf36a0b532260d38297e323c978bf6f0be2c83Vinay Sajip        if self.stream is None:                 # delay was set...
154aecf36a0b532260d38297e323c978bf6f0be2c83Vinay Sajip            self.stream = self._open()
15557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        if self.maxBytes > 0:                   # are we rolling over?
15657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            msg = "%s\n" % self.format(record)
1576fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz            self.stream.seek(0, 2)  #due to non-posix-compliant Windows feature
15857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            if self.stream.tell() + len(msg) >= self.maxBytes:
15917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip                return 1
16017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        return 0
16117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
16217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajipclass TimedRotatingFileHandler(BaseRotatingHandler):
16317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    """
16417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    Handler for logging to a file, rotating the log file at certain timed
16517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    intervals.
16617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
16717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    If backupCount is > 0, when rollover is done, no more than backupCount
16817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    files are kept - the oldest ones are deleted.
16917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    """
1701c77b7f84c5fca050980854a677539ba377439ddVinay Sajip    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
17192aa2f8d6bfe3088d3f1b322cdf546997eaaff71Vinay Sajip        BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
1721c77b7f84c5fca050980854a677539ba377439ddVinay Sajip        self.when = when.upper()
17317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        self.backupCount = backupCount
174a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip        self.utc = utc
17517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # Calculate the real rollover interval, which is just the number of
17617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # seconds between rollovers.  Also set the filename suffix used when
17717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # a rollover occurs.  Current 'when' events supported:
17817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # S - Seconds
17917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # M - Minutes
18017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # H - Hours
18117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # D - Days
18217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # midnight - roll over at midnight
18317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # W{0-6} - roll over on a certain day; 0 - Monday
18417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        #
18517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # Case of the 'when' specifier is not important; lower or upper case
18617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # will work.
18717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        if self.when == 'S':
18817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.interval = 1 # one second
18917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.suffix = "%Y-%m-%d_%H-%M-%S"
190e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$"
19117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        elif self.when == 'M':
19217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.interval = 60 # one minute
19317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.suffix = "%Y-%m-%d_%H-%M"
194e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$"
19517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        elif self.when == 'H':
19617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.interval = 60 * 60 # one hour
19717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.suffix = "%Y-%m-%d_%H"
198e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}$"
19917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        elif self.when == 'D' or self.when == 'MIDNIGHT':
20017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.interval = 60 * 60 * 24 # one day
20117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.suffix = "%Y-%m-%d"
202e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
20317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        elif self.when.startswith('W'):
20417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.interval = 60 * 60 * 24 * 7 # one week
20517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            if len(self.when) != 2:
20617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip                raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
20717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            if self.when[1] < '0' or self.when[1] > '6':
20817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip                raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
20917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.dayOfWeek = int(self.when[1])
21017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            self.suffix = "%Y-%m-%d"
211e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
21217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        else:
21317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            raise ValueError("Invalid rollover interval specified: %s" % self.when)
21417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
215e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        self.extMatch = re.compile(self.extMatch)
216e7d4066cdf1868a31f3ad5c6a271df045abe8255Vinay Sajip        self.interval = self.interval * interval # multiply by units requested
2179098ee43607340769d1dddf88522cc1bf9b1788fVinay Sajip        if os.path.exists(filename):
2189098ee43607340769d1dddf88522cc1bf9b1788fVinay Sajip            t = os.stat(filename)[ST_MTIME]
2199098ee43607340769d1dddf88522cc1bf9b1788fVinay Sajip        else:
2209098ee43607340769d1dddf88522cc1bf9b1788fVinay Sajip            t = int(time.time())
2219098ee43607340769d1dddf88522cc1bf9b1788fVinay Sajip        self.rolloverAt = self.computeRollover(t)
22283da034c9af59b73aa4f175eaebdc5f39d461656Vinay Sajip
22391290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip    def computeRollover(self, currentTime):
22491290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip        """
22591290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip        Work out the rollover time based on the specified time.
22691290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip        """
22791290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip        result = currentTime + self.interval
22817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # If we are rolling over at midnight or weekly, then the interval is already known.
22917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # What we need to figure out is WHEN the next interval is.  In other words,
23017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # if you are rolling over at midnight, then your base interval is 1 day,
23117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # but you want to start that one day clock at midnight, not now.  So, we
23217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # have to fudge the rolloverAt value in order to trigger the first rollover
23317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # at the right time.  After that, the regular interval will take care of
23417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # the rest.  Note that this code doesn't care about leap seconds. :)
23517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        if self.when == 'MIDNIGHT' or self.when.startswith('W'):
23617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # This could be done with less code, but I wanted it to be clear
23783da034c9af59b73aa4f175eaebdc5f39d461656Vinay Sajip            if self.utc:
238a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                t = time.gmtime(currentTime)
239a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip            else:
240a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                t = time.localtime(currentTime)
24117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            currentHour = t[3]
24217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            currentMinute = t[4]
24317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            currentSecond = t[5]
24417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # r is the number of seconds left between now and midnight
2454b4a63e30a8f300e545f2c44b4c456c74718aa79Vinay Sajip            r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
2464b4a63e30a8f300e545f2c44b4c456c74718aa79Vinay Sajip                    currentSecond)
24791290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip            result = currentTime + r
24817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # If we are rolling over on a certain day, add in the number of days until
24917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # the next rollover, but offset by 1 since we just calculated the time
25017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # until the next day starts.  There are three cases:
25117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # Case 1) The day to rollover is today; in this case, do nothing
25217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # Case 2) The day to rollover is further in the interval (i.e., today is
25317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            #         day 2 (Wednesday) and rollover is on day 6 (Sunday).  Days to
25417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            #         next rollover is simply 6 - 2 - 1, or 3.
25517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            # Case 3) The day to rollover is behind us in the interval (i.e., today
25617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            #         is day 5 (Saturday) and rollover is on day 3 (Thursday).
25717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            #         Days to rollover is 6 - 5 + 3, or 4.  In this case, it's the
25817c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            #         number of days left in the current week (1) plus the number
25917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            #         of days in the next week until the rollover day (3).
260ae747dccab69cb3828a3d51fafe41021f50882c9Vinay Sajip            # The calculations described in 2) and 3) above need to have a day added.
261ae747dccab69cb3828a3d51fafe41021f50882c9Vinay Sajip            # This is because the above time calculation takes us to midnight on this
262ae747dccab69cb3828a3d51fafe41021f50882c9Vinay Sajip            # day, i.e. the start of the next day.
26383da034c9af59b73aa4f175eaebdc5f39d461656Vinay Sajip            if self.when.startswith('W'):
26417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip                day = t[6] # 0 is Monday
265bababa3eccbff2d7c2c8bf3fc5730cf90825d300Vinay Sajip                if day != self.dayOfWeek:
266bababa3eccbff2d7c2c8bf3fc5730cf90825d300Vinay Sajip                    if day < self.dayOfWeek:
267ae747dccab69cb3828a3d51fafe41021f50882c9Vinay Sajip                        daysToWait = self.dayOfWeek - day
268bababa3eccbff2d7c2c8bf3fc5730cf90825d300Vinay Sajip                    else:
269ae747dccab69cb3828a3d51fafe41021f50882c9Vinay Sajip                        daysToWait = 6 - day + self.dayOfWeek + 1
27091290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip                    newRolloverAt = result + (daysToWait * (60 * 60 * 24))
27183da034c9af59b73aa4f175eaebdc5f39d461656Vinay Sajip                    if not self.utc:
272a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                        dstNow = t[-1]
273a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                        dstAtRollover = time.localtime(newRolloverAt)[-1]
274a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                        if dstNow != dstAtRollover:
275a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                            if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
2769790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                                addend = -3600
277a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                            else:           # DST bows out before next rollover, so we need to add an hour
2789790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                                addend = 3600
2799790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                            newRolloverAt += addend
28091290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip                    result = newRolloverAt
28191290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip        return result
28217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
28317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    def shouldRollover(self, record):
28417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
285e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        Determine if rollover should occur.
28617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
28717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        record is not used, as we are just comparing times, but it is needed so
288e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        the method signatures are the same
28917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
29017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        t = int(time.time())
29117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        if t >= self.rolloverAt:
29217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            return 1
2935e9e9e19f7fae86e5d234a5cd8386bc6e34a36abVinay Sajip        #print "No need to rollover: %d, %d" % (t, self.rolloverAt)
29417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        return 0
29517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip
296e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip    def getFilesToDelete(self):
297e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        """
298e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        Determine the files to delete when rolling over.
299e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip
300e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        More specific than the earlier method, which just used glob.glob().
301e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        """
302e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        dirName, baseName = os.path.split(self.baseFilename)
303e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        fileNames = os.listdir(dirName)
304e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        result = []
305e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        prefix = baseName + "."
306e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        plen = len(prefix)
307e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        for fileName in fileNames:
308e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            if fileName[:plen] == prefix:
309e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip                suffix = fileName[plen:]
310e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip                if self.extMatch.match(suffix):
311a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip                    result.append(os.path.join(dirName, fileName))
312e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        result.sort()
313e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        if len(result) < self.backupCount:
314e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            result = []
315e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        else:
316e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            result = result[:len(result) - self.backupCount]
317e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        return result
318e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip
31917c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip    def doRollover(self):
32017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
32117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        do a rollover; in this case, a date/time stamp is appended to the filename
32217c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        when the rollover happens.  However, you want the file to be named for the
32317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        start of the interval, not the current time.  If there is a backup count,
32417c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        then we have to get a list of matching filenames, sort them and remove
32517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        the one with the oldest suffix.
32617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        """
327aecf36a0b532260d38297e323c978bf6f0be2c83Vinay Sajip        if self.stream:
328aecf36a0b532260d38297e323c978bf6f0be2c83Vinay Sajip            self.stream.close()
329cdb8388cad74e0f2910d5b43531daf6271467292Vinay Sajip            self.stream = None
33017c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        # get the time that this sequence started at and make it a TimeTuple
3319790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip        currentTime = int(time.time())
3329790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip        dstNow = time.localtime(currentTime)[-1]
33317c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        t = self.rolloverAt - self.interval
334a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip        if self.utc:
335a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip            timeTuple = time.gmtime(t)
336a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip        else:
337a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip            timeTuple = time.localtime(t)
3389790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip            dstThen = timeTuple[-1]
3399790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip            if dstNow != dstThen:
3409790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                if dstNow:
3419790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                    addend = 3600
3429790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                else:
3439790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                    addend = -3600
3449790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                timeTuple = time.localtime(t + addend)
34517c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
34617c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        if os.path.exists(dfn):
34717c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip            os.remove(dfn)
3486d7e29651cf6a2232d66e47974c522cc2927a6c2Vinay Sajip        # Issue 18940: A file may not have been created if delay is True.
3496d7e29651cf6a2232d66e47974c522cc2927a6c2Vinay Sajip        if os.path.exists(self.baseFilename):
3506d7e29651cf6a2232d66e47974c522cc2927a6c2Vinay Sajip            os.rename(self.baseFilename, dfn)
35117c52d84934ff85efc26db3a040ce85cfb154488Vinay Sajip        if self.backupCount > 0:
352e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            for s in self.getFilesToDelete():
353e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip                os.remove(s)
354fb03696fdabeb850cdc714cd1abb45f6d93fcdf6Vinay Sajip        if not self.delay:
355fb03696fdabeb850cdc714cd1abb45f6d93fcdf6Vinay Sajip            self.stream = self._open()
35691290b5f53c1ddc81f30eacf6cf93d087b7c74eeVinay Sajip        newRolloverAt = self.computeRollover(currentTime)
357e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        while newRolloverAt <= currentTime:
358e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            newRolloverAt = newRolloverAt + self.interval
359e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        #If DST changes and midnight or weekly rollover, adjust for this.
360a12da73c468ca08a15671dc9a69bee3bb745cb4fVinay Sajip        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
361e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            dstAtRollover = time.localtime(newRolloverAt)[-1]
362e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip            if dstNow != dstAtRollover:
363e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip                if not dstNow:  # DST kicks in before next rollover, so we need to deduct an hour
3649790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                    addend = -3600
365e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip                else:           # DST bows out before next rollover, so we need to add an hour
3669790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                    addend = 3600
3679790e083a5330ab0f46dd0c9371ddc25cdeb9735Vinay Sajip                newRolloverAt += addend
368e5aefa0b303b3146df931bab692d0758c1aa725fVinay Sajip        self.rolloverAt = newRolloverAt
36957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
37073306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajipclass WatchedFileHandler(logging.FileHandler):
37173306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    """
37273306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    A handler for logging to a file, which watches the file
37373306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    to see if it has changed while in use. This can happen because of
37473306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    usage of programs such as newsyslog and logrotate which perform
37573306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    log file rotation. This handler, intended for use under Unix,
37673306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    watches the file to see if it has changed since the last emit.
37773306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    (A file has changed if its device or inode have changed.)
37873306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    If it has changed, the old file stream is closed, and the file
37973306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    opened to get a new stream.
38073306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip
38173306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    This handler is not appropriate for use under Windows, because
38273306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    under Windows open files cannot be moved or renamed - logging
38373306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    opens the files with exclusive locks - and so there is no need
38473306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    for such a handler. Furthermore, ST_INO is not supported under
38573306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    Windows; stat always returns zero for this value.
38673306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip
38773306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    This handler is based on a suggestion and patch by Chad J.
38873306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    Schroeder.
38973306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    """
39092aa2f8d6bfe3088d3f1b322cdf546997eaaff71Vinay Sajip    def __init__(self, filename, mode='a', encoding=None, delay=0):
39192aa2f8d6bfe3088d3f1b322cdf546997eaaff71Vinay Sajip        logging.FileHandler.__init__(self, filename, mode, encoding, delay)
392885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        self.dev, self.ino = -1, -1
393885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        self._statstream()
394885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip
395885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip    def _statstream(self):
396885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        if self.stream:
397885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip            sres = os.fstat(self.stream.fileno())
398885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip            self.dev, self.ino = sres[ST_DEV], sres[ST_INO]
39973306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip
40073306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip    def emit(self, record):
40173306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip        """
40273306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip        Emit a record.
40373306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip
40473306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip        First check if the underlying file has changed, and if it
40573306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip        has, close the old stream and reopen the file to get the
40673306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip        current stream.
40773306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip        """
408885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        # Reduce the chance of race conditions by stat'ing by path only
409885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        # once and then fstat'ing our new fd if we opened a new log stream.
410885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        # See issue #14632: Thanks to John Mulligan for the problem report
411885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        # and patch.
412885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        try:
413885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip            # stat the file by path, checking for existence
414885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip            sres = os.stat(self.baseFilename)
415885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        except OSError as err:
416885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip            if err.errno == errno.ENOENT:
417885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                sres = None
418885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip            else:
419885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                raise
420885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        # compare file system stat with that of our stream file handle
421885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip        if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
422885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip            if self.stream is not None:
423885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                # we have an open file handle, clean it up
424885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                self.stream.flush()
425885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                self.stream.close()
426ce817cb36d7e8764b5ddecadea4b02276879d631Vinay Sajip                self.stream = None  # See Issue #21742: _open () might fail.
427885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                # open a new file handle and get new stat info from that fd
428885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                self.stream = self._open()
429885f6fd472342795d54fae3309c10f87cadceec0Vinay Sajip                self._statstream()
43073306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip        logging.FileHandler.emit(self, record)
43173306b07edda7e922d8f90f14dd97ec390467f1bVinay Sajip
43257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass SocketHandler(logging.Handler):
43357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
43457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    A handler class which writes logging records, in pickle format, to
43557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    a streaming socket. The socket is kept open across logging calls.
43657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    If the peer resets it, an attempt is made to reconnect on the next call.
4376f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    The pickle which is sent is that of the LogRecord's attribute dictionary
4386f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    (__dict__), so that the receiver does not need to have the logging module
4396f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    installed in order to process the logging event.
4406f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger
4416f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    To unpickle the record at the receiving end into a LogRecord, use the
4426f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    makeLogRecord function.
44357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
44457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
44557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def __init__(self, host, port):
44657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
44757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initializes the handler with a specific host address and port.
44857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
44957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        The attribute 'closeOnError' is set to 1 - which means that if
45057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        a socket error occurs, the socket is silently closed and then
45157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        reopened on the next logging call.
45257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
45357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        logging.Handler.__init__(self)
45457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.host = host
45557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.port = port
45657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.sock = None
45757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.closeOnError = 0
45848cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        self.retryTime = None
45948cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        #
46048cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        # Exponential backoff parameters.
46148cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        #
46248cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        self.retryStart = 1.0
46348cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        self.retryMax = 30.0
46448cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        self.retryFactor = 2.0
46557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
466aa7b16a888eb53fdc4d7f194f3d49e6e8fe3ac24Vinay Sajip    def makeSocket(self, timeout=1):
46757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
46857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        A factory method which allows subclasses to define the precise
46957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        type of socket they want.
47057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
47157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
472aa7b16a888eb53fdc4d7f194f3d49e6e8fe3ac24Vinay Sajip        if hasattr(s, 'settimeout'):
473aa7b16a888eb53fdc4d7f194f3d49e6e8fe3ac24Vinay Sajip            s.settimeout(timeout)
47457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        s.connect((self.host, self.port))
47557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return s
47657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
47748cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip    def createSocket(self):
47848cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        """
47948cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        Try to create a socket, using an exponential backoff with
48048cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        a max retry time. Thanks to Robert Olson for the original patch
48148cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        (SF #815911) which has been slightly refactored.
48248cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        """
48348cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        now = time.time()
48448cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        # Either retryTime is None, in which case this
48548cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        # is the first time back after a disconnect, or
48648cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        # we've waited long enough.
48748cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        if self.retryTime is None:
4884e0e1b6a540e664e89739456db4c706701bf062bTim Peters            attempt = 1
48948cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        else:
4904e0e1b6a540e664e89739456db4c706701bf062bTim Peters            attempt = (now >= self.retryTime)
49148cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        if attempt:
49248cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip            try:
49348cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                self.sock = self.makeSocket()
49448cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                self.retryTime = None # next time, no delay before trying
495c683a87ca6d026e33c07758bf4802c009727b5f2Vinay Sajip            except socket.error:
49648cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                #Creation failed, so set the retry time and return.
49748cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                if self.retryTime is None:
49848cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                    self.retryPeriod = self.retryStart
49948cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                else:
50048cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                    self.retryPeriod = self.retryPeriod * self.retryFactor
50148cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                    if self.retryPeriod > self.retryMax:
50248cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                        self.retryPeriod = self.retryMax
50348cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                self.retryTime = now + self.retryPeriod
50448cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip
50557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def send(self, s):
50657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
50757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Send a pickled string to the socket.
50857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
50957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        This function allows for partial sends which can happen when the
51057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        network is busy.
51157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
51248cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        if self.sock is None:
51348cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip            self.createSocket()
51448cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        #self.sock can be None either because we haven't reached the retry
51548cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        #time yet, or because we have reached the retry time and retried,
51648cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        #but are still unable to connect.
51748cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        if self.sock:
51848cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip            try:
51948cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                if hasattr(self.sock, "sendall"):
52048cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                    self.sock.sendall(s)
52148cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                else:
52248cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                    sentsofar = 0
52348cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                    left = len(s)
52448cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                    while left > 0:
52548cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                        sent = self.sock.send(s[sentsofar:])
52648cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                        sentsofar = sentsofar + sent
52748cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                        left = left - sent
52848cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip            except socket.error:
52948cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                self.sock.close()
53048cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip                self.sock = None  # so we can call createSocket next time
53157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
53257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def makePickle(self, record):
53357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
53457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Pickles the record in binary format with a length prefix, and
53557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        returns it ready for transmission across the socket.
53657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
53748cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        ei = record.exc_info
53848cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        if ei:
5397ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip            # just to get traceback text into record.exc_text ...
5407ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip            dummy = self.format(record)
5414e0e1b6a540e664e89739456db4c706701bf062bTim Peters            record.exc_info = None  # to avoid Unpickleable error
5427ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip        # See issue #14436: If msg or args are objects, they may not be
5437ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip        # available on the receiving end. So we convert the msg % args
5447ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip        # to a string, save it as msg and zap the args.
5457ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip        d = dict(record.__dict__)
5467ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip        d['msg'] = record.getMessage()
5477ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip        d['args'] = None
5487ce9bda575313b66ca0a5cbcf6d6d30f9546a894Vinay Sajip        s = cPickle.dumps(d, 1)
54948cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        if ei:
5504e0e1b6a540e664e89739456db4c706701bf062bTim Peters            record.exc_info = ei  # for next handler
55157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        slen = struct.pack(">L", len(s))
55257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return slen + s
55357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
5546fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz    def handleError(self, record):
55557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
55657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Handle an error during logging.
55757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
55857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        An error has occurred during logging. Most likely cause -
55957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        connection lost. Close the socket so that we can retry on the
56057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        next event.
56157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
56257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        if self.closeOnError and self.sock:
56357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.sock.close()
56457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.sock = None        #try to reconnect next time
56557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        else:
5666fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz            logging.Handler.handleError(self, record)
56757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
56857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def emit(self, record):
56957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
57057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Emit a record.
57157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
57257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Pickles the record and writes it to the socket in binary format.
57357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        If there is an error with the socket, silently drop the packet.
57457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        If there was a problem with the socket, re-establishes the
57557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        socket.
57657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
57757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        try:
57857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            s = self.makePickle(record)
57957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.send(s)
58085c1909a78c97745cd41a56be7bce372b7d60d64Vinay Sajip        except (KeyboardInterrupt, SystemExit):
58185c1909a78c97745cd41a56be7bce372b7d60d64Vinay Sajip            raise
58257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        except:
5836fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz            self.handleError(record)
58457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
58557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def close(self):
58657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
58757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Closes the socket.
58857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
589501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        self.acquire()
590501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        try:
5911aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            sock = self.sock
5921aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            if sock:
593d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip                self.sock = None
5941aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka                sock.close()
595501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        finally:
596501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip            self.release()
59748cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        logging.Handler.close(self)
59857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
59957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass DatagramHandler(SocketHandler):
60057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
60157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    A handler class which writes logging records, in pickle format, to
6026f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    a datagram socket.  The pickle which is sent is that of the LogRecord's
6036f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    attribute dictionary (__dict__), so that the receiver does not need to
6046f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    have the logging module installed in order to process the logging event.
6056f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger
6066f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    To unpickle the record at the receiving end into a LogRecord, use the
6076f3eaa67e51ed0c1b493a26afdf4417d4105d96dRaymond Hettinger    makeLogRecord function.
60857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
60957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
61057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def __init__(self, host, port):
61157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
61257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initializes the handler with a specific host address and port.
61357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
61457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        SocketHandler.__init__(self, host, port)
61557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.closeOnError = 0
61657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
61757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def makeSocket(self):
61857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
61957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        The factory method of SocketHandler is here overridden to create
62057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        a UDP socket (SOCK_DGRAM).
62157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
62257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
62357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return s
62457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
62557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def send(self, s):
62657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
62757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Send a pickled string to a socket.
62857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
62957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        This function no longer allows for partial sends which can happen
63057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        when the network is busy - UDP does not guarantee delivery and
63157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        can deliver packets out of sequence.
63257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
633fb154171c4ace44dec817a3e52a0b83bbbb0f4f5Vinay Sajip        if self.sock is None:
634fb154171c4ace44dec817a3e52a0b83bbbb0f4f5Vinay Sajip            self.createSocket()
63557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.sock.sendto(s, (self.host, self.port))
63657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
63757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass SysLogHandler(logging.Handler):
63857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
63957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    A handler class which sends formatted logging records to a syslog
64057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    server. Based on Sam Rushing's syslog module:
64157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    http://www.nightmare.com/squirl/python-ext/misc/syslog.py
64257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    Contributed by Nicolas Untz (after which minor refactoring changes
64357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    have been made).
64457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
64557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
64657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # from <linux/sys/syslog.h>:
64757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # ======================================================================
64857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # priorities/facilities are encoded into a single 32-bit quantity, where
64957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
65057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # facility (0-big number). Both the priorities and the facilities map
65157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # roughly one-to-one to strings in the syslogd(8) source code.  This
65257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # mapping is included in this file.
65357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    #
65457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # priorities (these are ordered)
65557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
65657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_EMERG     = 0       #  system is unusable
65757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_ALERT     = 1       #  action must be taken immediately
65857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_CRIT      = 2       #  critical conditions
65957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_ERR       = 3       #  error conditions
66057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_WARNING   = 4       #  warning conditions
66157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_NOTICE    = 5       #  normal but significant condition
66257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_INFO      = 6       #  informational
66357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_DEBUG     = 7       #  debug-level messages
66457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
66557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    #  facility codes
66657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_KERN      = 0       #  kernel messages
66757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_USER      = 1       #  random user-level messages
66857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_MAIL      = 2       #  mail system
66957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_DAEMON    = 3       #  system daemons
67057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_AUTH      = 4       #  security/authorization messages
67157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_SYSLOG    = 5       #  messages generated internally by syslogd
67257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LPR       = 6       #  line printer subsystem
67357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_NEWS      = 7       #  network news subsystem
67457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_UUCP      = 8       #  UUCP subsystem
67557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_CRON      = 9       #  clock daemon
676b0623d64a8a689ace0d0fbdc0227f363b5c1af1fVinay Sajip    LOG_AUTHPRIV  = 10      #  security/authorization messages (private)
677b0623d64a8a689ace0d0fbdc0227f363b5c1af1fVinay Sajip    LOG_FTP       = 11      #  FTP daemon
67857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
67957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    #  other codes through 15 reserved for system use
68057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL0    = 16      #  reserved for local use
68157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL1    = 17      #  reserved for local use
68257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL2    = 18      #  reserved for local use
68357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL3    = 19      #  reserved for local use
68457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL4    = 20      #  reserved for local use
68557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL5    = 21      #  reserved for local use
68657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL6    = 22      #  reserved for local use
68757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    LOG_LOCAL7    = 23      #  reserved for local use
68857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
68957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    priority_names = {
69057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "alert":    LOG_ALERT,
69157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "crit":     LOG_CRIT,
69257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "critical": LOG_CRIT,
69357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "debug":    LOG_DEBUG,
69457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "emerg":    LOG_EMERG,
69557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "err":      LOG_ERR,
69657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "error":    LOG_ERR,        #  DEPRECATED
69757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "info":     LOG_INFO,
69857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "notice":   LOG_NOTICE,
69957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "panic":    LOG_EMERG,      #  DEPRECATED
70057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "warn":     LOG_WARNING,    #  DEPRECATED
70157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "warning":  LOG_WARNING,
70257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        }
70357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
70457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    facility_names = {
70557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "auth":     LOG_AUTH,
70657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "authpriv": LOG_AUTHPRIV,
70757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "cron":     LOG_CRON,
70857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "daemon":   LOG_DAEMON,
709b0623d64a8a689ace0d0fbdc0227f363b5c1af1fVinay Sajip        "ftp":      LOG_FTP,
71057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "kern":     LOG_KERN,
71157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "lpr":      LOG_LPR,
71257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "mail":     LOG_MAIL,
71357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "news":     LOG_NEWS,
71457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "security": LOG_AUTH,       #  DEPRECATED
71557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "syslog":   LOG_SYSLOG,
71657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "user":     LOG_USER,
71757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "uucp":     LOG_UUCP,
71857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local0":   LOG_LOCAL0,
71957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local1":   LOG_LOCAL1,
72057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local2":   LOG_LOCAL2,
72157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local3":   LOG_LOCAL3,
72257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local4":   LOG_LOCAL4,
72357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local5":   LOG_LOCAL5,
72457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local6":   LOG_LOCAL6,
72557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        "local7":   LOG_LOCAL7,
72657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        }
72757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
728dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    #The map below appears to be trivially lowercasing the key. However,
729dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    #there's more to it than meets the eye - in some locales, lowercasing
730dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    #gives unexpected results. See SF #1524081: in the Turkish locale,
731dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    #"INFO".lower() != "info"
732dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    priority_map = {
733dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        "DEBUG" : "debug",
734dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        "INFO" : "info",
735dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        "WARNING" : "warning",
736dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        "ERROR" : "error",
737dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        "CRITICAL" : "critical"
738dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    }
739dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip
7401c77b7f84c5fca050980854a677539ba377439ddVinay Sajip    def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
7414dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                 facility=LOG_USER, socktype=None):
74257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
74357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initialize a handler.
74457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
745754a5fb640237a7b6c89bacf7c1c304273b0fd6bVinay Sajip        If address is specified as a string, a UNIX socket is used. To log to a
746754a5fb640237a7b6c89bacf7c1c304273b0fd6bVinay Sajip        local syslogd, "SysLogHandler(address="/dev/log")" can be used.
7474dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        If facility is not specified, LOG_USER is used. If socktype is
7484dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
7494dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        socket type will be used. For Unix sockets, you can also specify a
7504dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        socktype of None, in which case socket.SOCK_DGRAM will be used, falling
7514dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        back to socket.SOCK_STREAM.
75257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
75357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        logging.Handler.__init__(self)
75457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
75557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.address = address
75657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.facility = facility
7571c77b7f84c5fca050980854a677539ba377439ddVinay Sajip        self.socktype = socktype
7581c77b7f84c5fca050980854a677539ba377439ddVinay Sajip
7591c77b7f84c5fca050980854a677539ba377439ddVinay Sajip        if isinstance(address, basestring):
76057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.unixsocket = 1
7615492e1722af1ce85b8248e30cf35e957429d161dVinay Sajip            self._connect_unixsocket(address)
76257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        else:
76357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.unixsocket = 0
7644dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            if socktype is None:
7654dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                socktype = socket.SOCK_DGRAM
7661c77b7f84c5fca050980854a677539ba377439ddVinay Sajip            self.socket = socket.socket(socket.AF_INET, socktype)
7671c77b7f84c5fca050980854a677539ba377439ddVinay Sajip            if socktype == socket.SOCK_STREAM:
7681c77b7f84c5fca050980854a677539ba377439ddVinay Sajip                self.socket.connect(address)
7694dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            self.socktype = socktype
77057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.formatter = None
77157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
772a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip    def _connect_unixsocket(self, address):
7734dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        use_socktype = self.socktype
7744dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        if use_socktype is None:
7754dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            use_socktype = socket.SOCK_DGRAM
7764dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip        self.socket = socket.socket(socket.AF_UNIX, use_socktype)
777a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip        try:
778a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip            self.socket.connect(address)
7794dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            # it worked, so set self.socktype to the used type
7804dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            self.socktype = use_socktype
781a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip        except socket.error:
782a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip            self.socket.close()
7834dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            if self.socktype is not None:
7844dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                # user didn't specify falling back, so fail
7854dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                raise
7864dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            use_socktype = socket.SOCK_STREAM
7874dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            self.socket = socket.socket(socket.AF_UNIX, use_socktype)
7884dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            try:
7894dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                self.socket.connect(address)
7904dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                # it worked, so set self.socktype to the used type
7914dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                self.socktype = use_socktype
7924dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip            except socket.error:
7934dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                self.socket.close()
7944dc385b4e3a9f7c140ac9725f85d0e60ccce66e6Vinay Sajip                raise
795a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip
79657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    # curious: when talking to the unix-domain '/dev/log' socket, a
79757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    #   zero-terminator seems to be required.  this string is placed
79857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    #   into a class variable so that it can be overridden if
79957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    #   necessary.
80057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    log_format_string = '<%d>%s\000'
80157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
802dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    def encodePriority(self, facility, priority):
80357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
80457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Encode the facility and priority. You can pass in strings or
80557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        integers - if strings are passed, the facility_names and
80657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        priority_names mapping dictionaries are used to convert them to
80757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        integers.
80857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
8091c77b7f84c5fca050980854a677539ba377439ddVinay Sajip        if isinstance(facility, basestring):
81057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            facility = self.facility_names[facility]
8111c77b7f84c5fca050980854a677539ba377439ddVinay Sajip        if isinstance(priority, basestring):
81257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            priority = self.priority_names[priority]
81357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return (facility << 3) | priority
81457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
81557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def close (self):
81657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
81757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Closes the socket.
81857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
819501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        self.acquire()
820501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        try:
821d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip            if self.unixsocket:
822d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip                self.socket.close()
823501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        finally:
824501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip            self.release()
82548cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        logging.Handler.close(self)
82657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
827dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip    def mapPriority(self, levelName):
828dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        """
829dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        Map a logging level name to a key in the priority_names map.
830dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        This is useful in two scenarios: when custom levels are being
831dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        used, and in the case where you can't do a straightforward
832dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        mapping by lowercasing the logging level name because of locale-
833dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        specific issues (see SF #1524081).
834dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        """
835dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip        return self.priority_map.get(levelName, "warning")
836dc57936b63043ad4f1bbb85e91399b02b7b22f21Vinay Sajip
83757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def emit(self, record):
83857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
83957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Emit a record.
84057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
84157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        The record is formatted, and then sent to the syslog server. If
84257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        exception information is present, it is NOT sent to the server.
84357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
84457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        try:
845a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            msg = self.format(record) + '\000'
846a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            """
847a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            We need to convert record level to lowercase, maybe this will
848a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            change in the future.
849a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            """
850a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            prio = '<%d>' % self.encodePriority(self.facility,
851a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip                                                self.mapPriority(record.levelname))
852a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            # Message is a string. Convert to bytes as required by RFC 5424
853a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            if type(msg) is unicode:
854a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip                msg = msg.encode('utf-8')
855a79d6f40dfa6dc0df03db489e27532a5a1b644ccVinay Sajip            msg = prio + msg
85657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            if self.unixsocket:
857a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip                try:
858a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip                    self.socket.send(msg)
859a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip                except socket.error:
860bb6b51ca2543967d0cbc5398500eee5a0826b3eaVinay Sajip                    self.socket.close() # See issue 17981
861a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip                    self._connect_unixsocket(self.address)
862a1974c1459a424fdc9d8bbce55500f6006d0297dVinay Sajip                    self.socket.send(msg)
8631c77b7f84c5fca050980854a677539ba377439ddVinay Sajip            elif self.socktype == socket.SOCK_DGRAM:
86457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                self.socket.sendto(msg, self.address)
8651c77b7f84c5fca050980854a677539ba377439ddVinay Sajip            else:
8661c77b7f84c5fca050980854a677539ba377439ddVinay Sajip                self.socket.sendall(msg)
86785c1909a78c97745cd41a56be7bce372b7d60d64Vinay Sajip        except (KeyboardInterrupt, SystemExit):
86885c1909a78c97745cd41a56be7bce372b7d60d64Vinay Sajip            raise
86957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        except:
8706fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz            self.handleError(record)
87157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
87257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass SMTPHandler(logging.Handler):
87357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
87457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    A handler class which sends an SMTP email for each logging event.
87557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
876483056675169a004a221e21036ed8224dbab8d19Vinay Sajip    def __init__(self, mailhost, fromaddr, toaddrs, subject,
877bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip                 credentials=None, secure=None):
87857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
87957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initialize the handler.
88057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
88157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initialize the instance with the from and to addresses and subject
88257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        line of the email. To specify a non-standard SMTP port, use the
88370c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip        (host, port) tuple format for the mailhost argument. To specify
88470c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip        authentication credentials, supply a (username, password) tuple
885483056675169a004a221e21036ed8224dbab8d19Vinay Sajip        for the credentials argument. To specify the use of a secure
886bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip        protocol (TLS), pass in a tuple for the secure argument. This will
887bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip        only be used when authentication credentials are supplied. The tuple
888bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip        will be either an empty tuple, or a single-value tuple with the name
889bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip        of a keyfile, or a 2-value tuple with the names of the keyfile and
890bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip        certificate file. (This tuple is passed to the `starttls` method).
89157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
89257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        logging.Handler.__init__(self)
8930746b002880ef22385135c85e03ddc3d1570a411Vinay Sajip        if isinstance(mailhost, (list, tuple)):
89470c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip            self.mailhost, self.mailport = mailhost
89557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        else:
89670c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip            self.mailhost, self.mailport = mailhost, None
8970746b002880ef22385135c85e03ddc3d1570a411Vinay Sajip        if isinstance(credentials, (list, tuple)):
89870c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip            self.username, self.password = credentials
89970c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip        else:
90070c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip            self.username = None
90157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.fromaddr = fromaddr
9021c77b7f84c5fca050980854a677539ba377439ddVinay Sajip        if isinstance(toaddrs, basestring):
9036fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz            toaddrs = [toaddrs]
90457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.toaddrs = toaddrs
90557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.subject = subject
906483056675169a004a221e21036ed8224dbab8d19Vinay Sajip        self.secure = secure
90750d6f54b1aae4aadf6f080148ec2858663312cd7Vinay Sajip        self._timeout = 5.0
90857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
90957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def getSubject(self, record):
91057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
91157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Determine the subject for the email.
91257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
91357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        If you want to specify a subject line which is record-dependent,
91457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        override this method.
91557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
91657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return self.subject
91757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
91857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def emit(self, record):
91957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
92057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Emit a record.
92157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
92257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Format the record and send it to the specified addressees.
92357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
92457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        try:
92557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            import smtplib
9267ce5c831ccde730a201a9b7a95e5d54cd9b98285Vinay Sajip            from email.utils import formatdate
92757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            port = self.mailport
92857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            if not port:
92957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                port = smtplib.SMTP_PORT
93050d6f54b1aae4aadf6f080148ec2858663312cd7Vinay Sajip            smtp = smtplib.SMTP(self.mailhost, port, timeout=self._timeout)
93157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            msg = self.format(record)
932f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz            msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
93357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                            self.fromaddr,
9341c77b7f84c5fca050980854a677539ba377439ddVinay Sajip                            ",".join(self.toaddrs),
935f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz                            self.getSubject(record),
936318a12eb0129bd75754cb3cc68076cc3b737074fMartin v. Löwis                            formatdate(), msg)
93770c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip            if self.username:
938bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip                if self.secure is not None:
939483056675169a004a221e21036ed8224dbab8d19Vinay Sajip                    smtp.ehlo()
940bd1094a4a506e7444350c822a700c82dc91d7728Vinay Sajip                    smtp.starttls(*self.secure)
941483056675169a004a221e21036ed8224dbab8d19Vinay Sajip                    smtp.ehlo()
94270c8e8b8613c2af0025d5c950e0be8d1b6296294Vinay Sajip                smtp.login(self.username, self.password)
94357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            smtp.sendmail(self.fromaddr, self.toaddrs, msg)
94457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            smtp.quit()
945245a5ab31bbaa48776736bdd101b0df7164125c9Vinay Sajip        except (KeyboardInterrupt, SystemExit):
946245a5ab31bbaa48776736bdd101b0df7164125c9Vinay Sajip            raise
94757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        except:
9486fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz            self.handleError(record)
94957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
95057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass NTEventLogHandler(logging.Handler):
95157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
95257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    A handler class which sends events to the NT Event Log. Adds a
95357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    registry entry for the specified application name. If no dllname is
95457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    provided, win32service.pyd (which contains some basic message
95557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    placeholders) is used. Note that use of these placeholders will make
95657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    your event logs big, as the entire message source is held in the log.
95757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    If you want slimmer logs, you have to pass in the name of your own DLL
95857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    which contains the message definitions you want to use in the event log.
95957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
96057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def __init__(self, appname, dllname=None, logtype="Application"):
96157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        logging.Handler.__init__(self)
96257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        try:
96357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            import win32evtlogutil, win32evtlog
96457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.appname = appname
96557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self._welu = win32evtlogutil
96657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            if not dllname:
96757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                dllname = os.path.split(self._welu.__file__)
96857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                dllname = os.path.split(dllname[0])
96957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                dllname = os.path.join(dllname[0], r'win32service.pyd')
97057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.dllname = dllname
97157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.logtype = logtype
97257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self._welu.AddSourceToRegistry(appname, dllname, logtype)
97357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
97457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.typemap = {
97557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                logging.DEBUG   : win32evtlog.EVENTLOG_INFORMATION_TYPE,
97657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                logging.INFO    : win32evtlog.EVENTLOG_INFORMATION_TYPE,
9776fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz                logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
97857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                logging.ERROR   : win32evtlog.EVENTLOG_ERROR_TYPE,
97957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
98057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum         }
98157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        except ImportError:
9821c77b7f84c5fca050980854a677539ba377439ddVinay Sajip            print("The Python Win32 extensions for NT (service, event "\
9831c77b7f84c5fca050980854a677539ba377439ddVinay Sajip                        "logging) appear not to be available.")
98457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self._welu = None
98557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
98657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def getMessageID(self, record):
98757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
98857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Return the message ID for the event record. If you are using your
98957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        own messages, you could do this by having the msg passed to the
99057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        logger being an ID rather than a formatting string. Then, in here,
99157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        you could use a dictionary lookup to get the message ID. This
99257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        version returns 1, which is the base message ID in win32service.pyd.
99357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
99457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return 1
99557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
99657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def getEventCategory(self, record):
99757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
99857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Return the event category for the record.
99957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
100057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Override this if you want to specify your own categories. This version
100157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        returns 0.
100257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
100357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return 0
100457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
100557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def getEventType(self, record):
100657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
100757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Return the event type for the record.
100857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
100957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Override this if you want to specify your own types. This version does
101057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        a mapping using the handler's typemap attribute, which is set up in
101157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        __init__() to a dictionary which contains mappings for DEBUG, INFO,
10126fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz        WARNING, ERROR and CRITICAL. If you are using your own levels you will
101357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        either need to override this method or place a suitable dictionary in
101457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        the handler's typemap attribute.
101557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
101657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return self.typemap.get(record.levelno, self.deftype)
101757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
101857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def emit(self, record):
101957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
102057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Emit a record.
102157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
102257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Determine the message ID, event category and event type. Then
102357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        log the message in the NT event log.
102457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
102557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        if self._welu:
102657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            try:
102757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                id = self.getMessageID(record)
102857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                cat = self.getEventCategory(record)
102957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                type = self.getEventType(record)
103057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                msg = self.format(record)
103157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                self._welu.ReportEvent(self.appname, id, cat, type, [msg])
1032245a5ab31bbaa48776736bdd101b0df7164125c9Vinay Sajip            except (KeyboardInterrupt, SystemExit):
1033245a5ab31bbaa48776736bdd101b0df7164125c9Vinay Sajip                raise
103457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            except:
10356fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz                self.handleError(record)
103657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
103757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def close(self):
103857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
103957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Clean up this handler.
104057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
104157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        You can remove the application name from the registry as a
104257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        source of event log entries. However, if you do this, you will
104357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        not be able to see the events as you intended in the Event Log
104457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Viewer - it needs to be able to access the registry to get the
104557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        DLL name.
104657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
104757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
104848cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        logging.Handler.close(self)
104957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
105057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass HTTPHandler(logging.Handler):
105157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
105257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    A class which sends records to a Web server, using either GET or
105357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    POST semantics.
105457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
105557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def __init__(self, host, url, method="GET"):
105657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
105757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initialize the instance with the host, the request URL, and the method
105857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        ("GET" or "POST")
105957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
106057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        logging.Handler.__init__(self)
10611c77b7f84c5fca050980854a677539ba377439ddVinay Sajip        method = method.upper()
106257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        if method not in ["GET", "POST"]:
10631c77b7f84c5fca050980854a677539ba377439ddVinay Sajip            raise ValueError("method must be GET or POST")
106457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.host = host
106557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.url = url
106657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.method = method
106757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
1068f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz    def mapLogRecord(self, record):
1069f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz        """
1070f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz        Default implementation of mapping the log record into a dict
107148cfe38e799394e2fdf81d95af3bdbfaf8e01360Vinay Sajip        that is sent as the CGI data. Overwrite in your class.
1072ab2db5815c28314d3b7bd3d7c3d2e1b5932ea524Vinay Sajip        Contributed by Franz Glasner.
1073f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz        """
1074f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz        return record.__dict__
1075f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz
107657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def emit(self, record):
107757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
107857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Emit a record.
107957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
1080a5ba05cd31134287f7016cd66898c861f80256c0Senthil Kumaran        Send the record to the Web server as a percent-encoded dictionary
108157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
108257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        try:
108357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            import httplib, urllib
1084b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip            host = self.host
1085b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip            h = httplib.HTTP(host)
108657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            url = self.url
1087f297bd1937f1fb73c7f734f6410e294da8dad12dNeal Norwitz            data = urllib.urlencode(self.mapLogRecord(record))
108857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            if self.method == "GET":
10891c77b7f84c5fca050980854a677539ba377439ddVinay Sajip                if (url.find('?') >= 0):
109057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                    sep = '&'
109157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                else:
109257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                    sep = '?'
109357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                url = url + "%c%s" % (sep, data)
109457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            h.putrequest(self.method, url)
1095b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip            # support multiple hosts on one IP address...
1096b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip            # need to strip optional :port from host, if present
10971c77b7f84c5fca050980854a677539ba377439ddVinay Sajip            i = host.find(":")
1098b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip            if i >= 0:
1099b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip                host = host[:i]
1100b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip            h.putheader("Host", host)
110157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            if self.method == "POST":
1102b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip                h.putheader("Content-type",
1103b79350601bc3faea4ca97d537ba887810daf0ba0Vinay Sajip                            "application/x-www-form-urlencoded")
110457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                h.putheader("Content-length", str(len(data)))
110584040dbe8170864ba673321ec7568974bdabf5a4Kristján Valur Jónsson            h.endheaders(data if self.method == "POST" else None)
110657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            h.getreply()    #can't do anything with the result
1107245a5ab31bbaa48776736bdd101b0df7164125c9Vinay Sajip        except (KeyboardInterrupt, SystemExit):
1108245a5ab31bbaa48776736bdd101b0df7164125c9Vinay Sajip            raise
110957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        except:
11106fa635df7aa88ae9fd8b41ae42743341316c90f7Neal Norwitz            self.handleError(record)
111157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
111257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass BufferingHandler(logging.Handler):
111357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
111457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum  A handler class which buffers logging records in memory. Whenever each
111557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum  record is added to the buffer, a check is made to see if the buffer should
111657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum  be flushed. If it should, then flush() is expected to do what's needed.
111757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
111857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def __init__(self, capacity):
111957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
112057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initialize the handler with the buffer size.
112157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
112257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        logging.Handler.__init__(self)
112357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.capacity = capacity
112457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.buffer = []
112557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
112657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def shouldFlush(self, record):
112757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
112857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Should the handler flush its buffer?
112957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
113057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Returns true if the buffer is up to capacity. This method can be
113157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        overridden to implement custom flushing strategies.
113257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
113357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return (len(self.buffer) >= self.capacity)
113457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
113557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def emit(self, record):
113657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
113757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Emit a record.
113857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
113957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Append the record. If shouldFlush() tells us to, call flush() to process
114057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        the buffer.
114157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
114257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.buffer.append(record)
114357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        if self.shouldFlush(record):
114457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum            self.flush()
114557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
114657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def flush(self):
114757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
114857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Override to implement custom flushing behaviour.
114957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
115057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        This version just zaps the buffer to empty.
115157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
1152501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        self.acquire()
1153501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        try:
1154d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip            self.buffer = []
1155501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        finally:
1156501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip            self.release()
115757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
1158f42d95ebd167b4a0b0edfdfc6f15bd8c5cec799bVinay Sajip    def close(self):
1159f42d95ebd167b4a0b0edfdfc6f15bd8c5cec799bVinay Sajip        """
1160f42d95ebd167b4a0b0edfdfc6f15bd8c5cec799bVinay Sajip        Close the handler.
1161f42d95ebd167b4a0b0edfdfc6f15bd8c5cec799bVinay Sajip
1162f42d95ebd167b4a0b0edfdfc6f15bd8c5cec799bVinay Sajip        This version just flushes and chains to the parent class' close().
1163f42d95ebd167b4a0b0edfdfc6f15bd8c5cec799bVinay Sajip        """
11641aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka        try:
11651aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            self.flush()
11661aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka        finally:
11671aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            logging.Handler.close(self)
1168f42d95ebd167b4a0b0edfdfc6f15bd8c5cec799bVinay Sajip
116957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossumclass MemoryHandler(BufferingHandler):
117057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
117157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    A handler class which buffers logging records in memory, periodically
117257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    flushing them to a target handler. Flushing occurs whenever the buffer
117357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    is full, or when an event of a certain severity or greater is seen.
117457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    """
117557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
117657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
117757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Initialize the handler with the buffer size, the level at which
117857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        flushing should occur and an optional target.
117957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
118057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Note that without a target being set either here or via setTarget(),
118157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        a MemoryHandler is no use to anyone!
118257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
118357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        BufferingHandler.__init__(self, capacity)
118457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.flushLevel = flushLevel
118557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.target = target
118657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
118757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def shouldFlush(self, record):
118857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
118957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Check for buffer full or a record at the flushLevel or higher.
119057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
119157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        return (len(self.buffer) >= self.capacity) or \
119257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum                (record.levelno >= self.flushLevel)
119357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
119457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def setTarget(self, target):
119557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
119657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Set the target handler for this handler.
119757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
119857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        self.target = target
119957102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
120057102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def flush(self):
120157102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
120257102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        For a MemoryHandler, flushing means just sending the buffered
120357102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        records to the target, if there is one. Override if you want
120457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        different behaviour.
120557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
1206501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        self.acquire()
1207501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        try:
1208d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip            if self.target:
1209d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip                for record in self.buffer:
1210d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip                    self.target.handle(record)
1211d23845e2701c72bf064e508af2012a1a5d342ddaVinay Sajip                self.buffer = []
1212501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        finally:
1213501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip            self.release()
121457102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum
121557102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum    def close(self):
121657102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
121757102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        Flush, set the target to None and lose the buffer.
121857102f861d506b6c2d2215d100dac9143574fa77Guido van Rossum        """
1219501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        try:
12201aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            self.flush()
1221501982226a175a9e7eeaee597e25ee6319815b7eVinay Sajip        finally:
12221aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            self.acquire()
12231aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            try:
12241aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka                self.target = None
12251aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka                BufferingHandler.close(self)
12261aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka            finally:
12271aa2c0f073bdbed4fa824591d53e20bbf3d01addSerhiy Storchaka                self.release()
1228