blob: 95543f97fdda3cb965d387ea8cbaf12539908a16 [file] [log] [blame]
Vinay Sajipd23845e2012-02-23 19:37:18 +00001# Copyright 2001-2012 by Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +00002#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose and without fee is hereby granted,
5# provided that the above copyright notice appear in all copies and that
6# both that copyright notice and this permission notice appear in
7# supporting documentation, and that the name of Vinay Sajip
8# not be used in advertising or publicity pertaining to distribution
9# of the software without specific, written prior permission.
10# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
11# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
13# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
14# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Guido van Rossum57102f82002-11-13 16:15:58 +000016
17"""
Vinay Sajip3f742842004-02-28 16:07:46 +000018Additional handlers for the logging package for Python. The core package is
19based on PEP 282 and comments thereto in comp.lang.python, and influenced by
20Apache's log4j system.
Guido van Rossum57102f82002-11-13 16:15:58 +000021
Vinay Sajip7ce5c832010-02-07 13:06:51 +000022Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000023
Vinay Sajipaecf36a2009-01-20 23:16:08 +000024To use, simply 'import logging.handlers' and log away!
Guido van Rossum57102f82002-11-13 16:15:58 +000025"""
26
Vinay Sajip1c77b7f2009-10-10 20:32:36 +000027import logging, socket, os, cPickle, struct, time, re
Vinay Sajip9098ee42010-03-12 06:01:21 +000028from stat import ST_DEV, ST_INO, ST_MTIME
Guido van Rossum57102f82002-11-13 16:15:58 +000029
Vinay Sajip4600f112005-03-13 09:56:36 +000030try:
31 import codecs
32except ImportError:
33 codecs = None
Vinay Sajip5ac65282009-10-21 20:22:14 +000034try:
35 unicode
36 _unicode = True
37except NameError:
38 _unicode = False
Vinay Sajip4600f112005-03-13 09:56:36 +000039
Guido van Rossum57102f82002-11-13 16:15:58 +000040#
41# Some constants...
42#
43
44DEFAULT_TCP_LOGGING_PORT = 9020
45DEFAULT_UDP_LOGGING_PORT = 9021
46DEFAULT_HTTP_LOGGING_PORT = 9022
47DEFAULT_SOAP_LOGGING_PORT = 9023
48SYSLOG_UDP_PORT = 514
Vinay Sajip1c77b7f2009-10-10 20:32:36 +000049SYSLOG_TCP_PORT = 514
Guido van Rossum57102f82002-11-13 16:15:58 +000050
Vinay Sajip4b4a63e2006-05-02 08:35:36 +000051_MIDNIGHT = 24 * 60 * 60 # number of seconds in a day
52
Vinay Sajip17c52d82004-07-03 11:48:34 +000053class BaseRotatingHandler(logging.FileHandler):
54 """
55 Base class for handlers that rotate log files at a certain point.
56 Not meant to be instantiated directly. Instead, use RotatingFileHandler
57 or TimedRotatingFileHandler.
58 """
Vinay Sajip92aa2f82008-01-24 12:37:33 +000059 def __init__(self, filename, mode, encoding=None, delay=0):
Vinay Sajip17c52d82004-07-03 11:48:34 +000060 """
61 Use the specified filename for streamed logging
62 """
Vinay Sajip4600f112005-03-13 09:56:36 +000063 if codecs is None:
64 encoding = None
Vinay Sajip92aa2f82008-01-24 12:37:33 +000065 logging.FileHandler.__init__(self, filename, mode, encoding, delay)
Vinay Sajip4600f112005-03-13 09:56:36 +000066 self.mode = mode
67 self.encoding = encoding
Guido van Rossum57102f82002-11-13 16:15:58 +000068
Vinay Sajip17c52d82004-07-03 11:48:34 +000069 def emit(self, record):
70 """
71 Emit a record.
72
73 Output the record to the file, catering for rollover as described
74 in doRollover().
75 """
Vinay Sajip3970c112004-07-08 10:24:04 +000076 try:
77 if self.shouldRollover(record):
78 self.doRollover()
79 logging.FileHandler.emit(self, record)
Vinay Sajip85c19092005-10-31 13:14:19 +000080 except (KeyboardInterrupt, SystemExit):
81 raise
Vinay Sajip3970c112004-07-08 10:24:04 +000082 except:
83 self.handleError(record)
Vinay Sajip17c52d82004-07-03 11:48:34 +000084
85class RotatingFileHandler(BaseRotatingHandler):
86 """
87 Handler for logging to a set of files, which switches from one file
88 to the next when the current file reaches a certain size.
89 """
Vinay Sajip92aa2f82008-01-24 12:37:33 +000090 def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0):
Guido van Rossum57102f82002-11-13 16:15:58 +000091 """
92 Open the specified file and use it as the stream for logging.
93
94 By default, the file grows indefinitely. You can specify particular
95 values of maxBytes and backupCount to allow the file to rollover at
96 a predetermined size.
97
98 Rollover occurs whenever the current log file is nearly maxBytes in
99 length. If backupCount is >= 1, the system will successively create
100 new files with the same pathname as the base file, but with extensions
101 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
102 and a base file name of "app.log", you would get "app.log",
103 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
104 written to is always "app.log" - when it gets filled up, it is closed
105 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
106 exist, then they are renamed to "app.log.2", "app.log.3" etc.
107 respectively.
108
109 If maxBytes is zero, rollover never occurs.
110 """
Vinay Sajipe8b1eaf2010-08-22 18:11:02 +0000111 # If rotation/rollover is wanted, it doesn't make sense to use another
112 # mode. If for example 'w' were specified, then if there were multiple
113 # runs of the calling application, the logs from previous runs would be
114 # lost if the 'w' is respected, because the log file would be truncated
115 # on each run.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000116 if maxBytes > 0:
Vinay Sajipe8b1eaf2010-08-22 18:11:02 +0000117 mode = 'a'
Vinay Sajip92aa2f82008-01-24 12:37:33 +0000118 BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
Guido van Rossum57102f82002-11-13 16:15:58 +0000119 self.maxBytes = maxBytes
120 self.backupCount = backupCount
Guido van Rossum57102f82002-11-13 16:15:58 +0000121
122 def doRollover(self):
123 """
124 Do a rollover, as described in __init__().
125 """
Vinay Sajipaecf36a2009-01-20 23:16:08 +0000126 if self.stream:
127 self.stream.close()
Vinay Sajipcdb83882011-01-21 23:35:57 +0000128 self.stream = None
Guido van Rossum57102f82002-11-13 16:15:58 +0000129 if self.backupCount > 0:
130 for i in range(self.backupCount - 1, 0, -1):
131 sfn = "%s.%d" % (self.baseFilename, i)
132 dfn = "%s.%d" % (self.baseFilename, i + 1)
133 if os.path.exists(sfn):
134 #print "%s -> %s" % (sfn, dfn)
135 if os.path.exists(dfn):
136 os.remove(dfn)
137 os.rename(sfn, dfn)
138 dfn = self.baseFilename + ".1"
139 if os.path.exists(dfn):
140 os.remove(dfn)
Vinay Sajip6dd59f12006-06-27 07:34:37 +0000141 os.rename(self.baseFilename, dfn)
Guido van Rossum57102f82002-11-13 16:15:58 +0000142 #print "%s -> %s" % (self.baseFilename, dfn)
Vinay Sajipb3d8a062007-01-16 09:50:07 +0000143 self.mode = 'w'
144 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000145
Vinay Sajip17c52d82004-07-03 11:48:34 +0000146 def shouldRollover(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000147 """
Vinay Sajip17c52d82004-07-03 11:48:34 +0000148 Determine if rollover should occur.
Guido van Rossum57102f82002-11-13 16:15:58 +0000149
Vinay Sajip17c52d82004-07-03 11:48:34 +0000150 Basically, see if the supplied record would cause the file to exceed
151 the size limit we have.
Guido van Rossum57102f82002-11-13 16:15:58 +0000152 """
Vinay Sajipaecf36a2009-01-20 23:16:08 +0000153 if self.stream is None: # delay was set...
154 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000155 if self.maxBytes > 0: # are we rolling over?
156 msg = "%s\n" % self.format(record)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000157 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
Guido van Rossum57102f82002-11-13 16:15:58 +0000158 if self.stream.tell() + len(msg) >= self.maxBytes:
Vinay Sajip17c52d82004-07-03 11:48:34 +0000159 return 1
160 return 0
Guido van Rossum57102f82002-11-13 16:15:58 +0000161
Vinay Sajip17c52d82004-07-03 11:48:34 +0000162class TimedRotatingFileHandler(BaseRotatingHandler):
163 """
164 Handler for logging to a file, rotating the log file at certain timed
165 intervals.
166
167 If backupCount is > 0, when rollover is done, no more than backupCount
168 files are kept - the oldest ones are deleted.
169 """
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000170 def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
Vinay Sajip92aa2f82008-01-24 12:37:33 +0000171 BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000172 self.when = when.upper()
Vinay Sajip17c52d82004-07-03 11:48:34 +0000173 self.backupCount = backupCount
Vinay Sajipa12da732008-05-20 15:34:36 +0000174 self.utc = utc
Vinay Sajip17c52d82004-07-03 11:48:34 +0000175 # Calculate the real rollover interval, which is just the number of
176 # seconds between rollovers. Also set the filename suffix used when
177 # a rollover occurs. Current 'when' events supported:
178 # S - Seconds
179 # M - Minutes
180 # H - Hours
181 # D - Days
182 # midnight - roll over at midnight
183 # W{0-6} - roll over on a certain day; 0 - Monday
184 #
185 # Case of the 'when' specifier is not important; lower or upper case
186 # will work.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000187 if self.when == 'S':
188 self.interval = 1 # one second
189 self.suffix = "%Y-%m-%d_%H-%M-%S"
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000190 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000191 elif self.when == 'M':
192 self.interval = 60 # one minute
193 self.suffix = "%Y-%m-%d_%H-%M"
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000194 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000195 elif self.when == 'H':
196 self.interval = 60 * 60 # one hour
197 self.suffix = "%Y-%m-%d_%H"
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000198 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000199 elif self.when == 'D' or self.when == 'MIDNIGHT':
200 self.interval = 60 * 60 * 24 # one day
201 self.suffix = "%Y-%m-%d"
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000202 self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000203 elif self.when.startswith('W'):
204 self.interval = 60 * 60 * 24 * 7 # one week
205 if len(self.when) != 2:
206 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
207 if self.when[1] < '0' or self.when[1] > '6':
208 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
209 self.dayOfWeek = int(self.when[1])
210 self.suffix = "%Y-%m-%d"
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000211 self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000212 else:
213 raise ValueError("Invalid rollover interval specified: %s" % self.when)
214
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000215 self.extMatch = re.compile(self.extMatch)
Vinay Sajipe7d40662004-10-03 19:12:07 +0000216 self.interval = self.interval * interval # multiply by units requested
Vinay Sajip9098ee42010-03-12 06:01:21 +0000217 if os.path.exists(filename):
218 t = os.stat(filename)[ST_MTIME]
219 else:
220 t = int(time.time())
221 self.rolloverAt = self.computeRollover(t)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000222
Vinay Sajip91290b52009-06-11 09:53:35 +0000223 def computeRollover(self, currentTime):
224 """
225 Work out the rollover time based on the specified time.
226 """
227 result = currentTime + self.interval
Vinay Sajip17c52d82004-07-03 11:48:34 +0000228 # If we are rolling over at midnight or weekly, then the interval is already known.
229 # What we need to figure out is WHEN the next interval is. In other words,
230 # if you are rolling over at midnight, then your base interval is 1 day,
231 # but you want to start that one day clock at midnight, not now. So, we
232 # have to fudge the rolloverAt value in order to trigger the first rollover
233 # at the right time. After that, the regular interval will take care of
234 # the rest. Note that this code doesn't care about leap seconds. :)
235 if self.when == 'MIDNIGHT' or self.when.startswith('W'):
236 # This could be done with less code, but I wanted it to be clear
Vinay Sajip83da0342009-06-11 09:23:41 +0000237 if self.utc:
Vinay Sajipa12da732008-05-20 15:34:36 +0000238 t = time.gmtime(currentTime)
239 else:
240 t = time.localtime(currentTime)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000241 currentHour = t[3]
242 currentMinute = t[4]
243 currentSecond = t[5]
244 # r is the number of seconds left between now and midnight
Vinay Sajip4b4a63e2006-05-02 08:35:36 +0000245 r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
246 currentSecond)
Vinay Sajip91290b52009-06-11 09:53:35 +0000247 result = currentTime + r
Vinay Sajip17c52d82004-07-03 11:48:34 +0000248 # If we are rolling over on a certain day, add in the number of days until
249 # the next rollover, but offset by 1 since we just calculated the time
250 # until the next day starts. There are three cases:
251 # Case 1) The day to rollover is today; in this case, do nothing
252 # Case 2) The day to rollover is further in the interval (i.e., today is
253 # day 2 (Wednesday) and rollover is on day 6 (Sunday). Days to
254 # next rollover is simply 6 - 2 - 1, or 3.
255 # Case 3) The day to rollover is behind us in the interval (i.e., today
256 # is day 5 (Saturday) and rollover is on day 3 (Thursday).
257 # Days to rollover is 6 - 5 + 3, or 4. In this case, it's the
258 # number of days left in the current week (1) plus the number
259 # of days in the next week until the rollover day (3).
Vinay Sajipae747dc2008-01-21 17:02:26 +0000260 # The calculations described in 2) and 3) above need to have a day added.
261 # This is because the above time calculation takes us to midnight on this
262 # day, i.e. the start of the next day.
Vinay Sajip83da0342009-06-11 09:23:41 +0000263 if self.when.startswith('W'):
Vinay Sajip17c52d82004-07-03 11:48:34 +0000264 day = t[6] # 0 is Monday
Vinay Sajipbababa32007-10-24 10:47:06 +0000265 if day != self.dayOfWeek:
266 if day < self.dayOfWeek:
Vinay Sajipae747dc2008-01-21 17:02:26 +0000267 daysToWait = self.dayOfWeek - day
Vinay Sajipbababa32007-10-24 10:47:06 +0000268 else:
Vinay Sajipae747dc2008-01-21 17:02:26 +0000269 daysToWait = 6 - day + self.dayOfWeek + 1
Vinay Sajip91290b52009-06-11 09:53:35 +0000270 newRolloverAt = result + (daysToWait * (60 * 60 * 24))
Vinay Sajip83da0342009-06-11 09:23:41 +0000271 if not self.utc:
Vinay Sajipa12da732008-05-20 15:34:36 +0000272 dstNow = t[-1]
273 dstAtRollover = time.localtime(newRolloverAt)[-1]
274 if dstNow != dstAtRollover:
275 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
276 newRolloverAt = newRolloverAt - 3600
277 else: # DST bows out before next rollover, so we need to add an hour
278 newRolloverAt = newRolloverAt + 3600
Vinay Sajip91290b52009-06-11 09:53:35 +0000279 result = newRolloverAt
280 return result
Vinay Sajip17c52d82004-07-03 11:48:34 +0000281
Vinay Sajip17c52d82004-07-03 11:48:34 +0000282 def shouldRollover(self, record):
283 """
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000284 Determine if rollover should occur.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000285
286 record is not used, as we are just comparing times, but it is needed so
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000287 the method signatures are the same
Vinay Sajip17c52d82004-07-03 11:48:34 +0000288 """
289 t = int(time.time())
290 if t >= self.rolloverAt:
291 return 1
Vinay Sajip5e9e9e12004-07-12 09:21:41 +0000292 #print "No need to rollover: %d, %d" % (t, self.rolloverAt)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000293 return 0
294
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000295 def getFilesToDelete(self):
296 """
297 Determine the files to delete when rolling over.
298
299 More specific than the earlier method, which just used glob.glob().
300 """
301 dirName, baseName = os.path.split(self.baseFilename)
302 fileNames = os.listdir(dirName)
303 result = []
304 prefix = baseName + "."
305 plen = len(prefix)
306 for fileName in fileNames:
307 if fileName[:plen] == prefix:
308 suffix = fileName[plen:]
309 if self.extMatch.match(suffix):
Vinay Sajipa12da732008-05-20 15:34:36 +0000310 result.append(os.path.join(dirName, fileName))
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000311 result.sort()
312 if len(result) < self.backupCount:
313 result = []
314 else:
315 result = result[:len(result) - self.backupCount]
316 return result
317
Vinay Sajip17c52d82004-07-03 11:48:34 +0000318 def doRollover(self):
319 """
320 do a rollover; in this case, a date/time stamp is appended to the filename
321 when the rollover happens. However, you want the file to be named for the
322 start of the interval, not the current time. If there is a backup count,
323 then we have to get a list of matching filenames, sort them and remove
324 the one with the oldest suffix.
325 """
Vinay Sajipaecf36a2009-01-20 23:16:08 +0000326 if self.stream:
327 self.stream.close()
Vinay Sajipcdb83882011-01-21 23:35:57 +0000328 self.stream = None
Vinay Sajip17c52d82004-07-03 11:48:34 +0000329 # get the time that this sequence started at and make it a TimeTuple
330 t = self.rolloverAt - self.interval
Vinay Sajipa12da732008-05-20 15:34:36 +0000331 if self.utc:
332 timeTuple = time.gmtime(t)
333 else:
334 timeTuple = time.localtime(t)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000335 dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
336 if os.path.exists(dfn):
337 os.remove(dfn)
Vinay Sajip6dd59f12006-06-27 07:34:37 +0000338 os.rename(self.baseFilename, dfn)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000339 if self.backupCount > 0:
340 # find the oldest log file and delete it
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000341 #s = glob.glob(self.baseFilename + ".20*")
342 #if len(s) > self.backupCount:
343 # s.sort()
344 # os.remove(s[0])
345 for s in self.getFilesToDelete():
346 os.remove(s)
Vinay Sajip5e9e9e12004-07-12 09:21:41 +0000347 #print "%s -> %s" % (self.baseFilename, dfn)
Vinay Sajipb3d8a062007-01-16 09:50:07 +0000348 self.mode = 'w'
349 self.stream = self._open()
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000350 currentTime = int(time.time())
Vinay Sajip91290b52009-06-11 09:53:35 +0000351 newRolloverAt = self.computeRollover(currentTime)
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000352 while newRolloverAt <= currentTime:
353 newRolloverAt = newRolloverAt + self.interval
354 #If DST changes and midnight or weekly rollover, adjust for this.
Vinay Sajipa12da732008-05-20 15:34:36 +0000355 if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
Vinay Sajipe5aefa02008-04-02 21:09:27 +0000356 dstNow = time.localtime(currentTime)[-1]
357 dstAtRollover = time.localtime(newRolloverAt)[-1]
358 if dstNow != dstAtRollover:
359 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
360 newRolloverAt = newRolloverAt - 3600
361 else: # DST bows out before next rollover, so we need to add an hour
362 newRolloverAt = newRolloverAt + 3600
363 self.rolloverAt = newRolloverAt
Guido van Rossum57102f82002-11-13 16:15:58 +0000364
Vinay Sajip73306b02007-01-14 21:49:59 +0000365class WatchedFileHandler(logging.FileHandler):
366 """
367 A handler for logging to a file, which watches the file
368 to see if it has changed while in use. This can happen because of
369 usage of programs such as newsyslog and logrotate which perform
370 log file rotation. This handler, intended for use under Unix,
371 watches the file to see if it has changed since the last emit.
372 (A file has changed if its device or inode have changed.)
373 If it has changed, the old file stream is closed, and the file
374 opened to get a new stream.
375
376 This handler is not appropriate for use under Windows, because
377 under Windows open files cannot be moved or renamed - logging
378 opens the files with exclusive locks - and so there is no need
379 for such a handler. Furthermore, ST_INO is not supported under
380 Windows; stat always returns zero for this value.
381
382 This handler is based on a suggestion and patch by Chad J.
383 Schroeder.
384 """
Vinay Sajip92aa2f82008-01-24 12:37:33 +0000385 def __init__(self, filename, mode='a', encoding=None, delay=0):
386 logging.FileHandler.__init__(self, filename, mode, encoding, delay)
387 if not os.path.exists(self.baseFilename):
388 self.dev, self.ino = -1, -1
389 else:
390 stat = os.stat(self.baseFilename)
391 self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
Vinay Sajip73306b02007-01-14 21:49:59 +0000392
393 def emit(self, record):
394 """
395 Emit a record.
396
397 First check if the underlying file has changed, and if it
398 has, close the old stream and reopen the file to get the
399 current stream.
400 """
401 if not os.path.exists(self.baseFilename):
402 stat = None
403 changed = 1
404 else:
405 stat = os.stat(self.baseFilename)
406 changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino)
Vinay Sajip92aa2f82008-01-24 12:37:33 +0000407 if changed and self.stream is not None:
Vinay Sajip73306b02007-01-14 21:49:59 +0000408 self.stream.flush()
409 self.stream.close()
410 self.stream = self._open()
411 if stat is None:
412 stat = os.stat(self.baseFilename)
413 self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
414 logging.FileHandler.emit(self, record)
415
Guido van Rossum57102f82002-11-13 16:15:58 +0000416class SocketHandler(logging.Handler):
417 """
418 A handler class which writes logging records, in pickle format, to
419 a streaming socket. The socket is kept open across logging calls.
420 If the peer resets it, an attempt is made to reconnect on the next call.
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000421 The pickle which is sent is that of the LogRecord's attribute dictionary
422 (__dict__), so that the receiver does not need to have the logging module
423 installed in order to process the logging event.
424
425 To unpickle the record at the receiving end into a LogRecord, use the
426 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000427 """
428
429 def __init__(self, host, port):
430 """
431 Initializes the handler with a specific host address and port.
432
433 The attribute 'closeOnError' is set to 1 - which means that if
434 a socket error occurs, the socket is silently closed and then
435 reopened on the next logging call.
436 """
437 logging.Handler.__init__(self)
438 self.host = host
439 self.port = port
440 self.sock = None
441 self.closeOnError = 0
Vinay Sajip48cfe382004-02-20 13:17:27 +0000442 self.retryTime = None
443 #
444 # Exponential backoff parameters.
445 #
446 self.retryStart = 1.0
447 self.retryMax = 30.0
448 self.retryFactor = 2.0
Guido van Rossum57102f82002-11-13 16:15:58 +0000449
Vinay Sajipaa7b16a2007-04-09 16:16:10 +0000450 def makeSocket(self, timeout=1):
Guido van Rossum57102f82002-11-13 16:15:58 +0000451 """
452 A factory method which allows subclasses to define the precise
453 type of socket they want.
454 """
455 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Vinay Sajipaa7b16a2007-04-09 16:16:10 +0000456 if hasattr(s, 'settimeout'):
457 s.settimeout(timeout)
Guido van Rossum57102f82002-11-13 16:15:58 +0000458 s.connect((self.host, self.port))
459 return s
460
Vinay Sajip48cfe382004-02-20 13:17:27 +0000461 def createSocket(self):
462 """
463 Try to create a socket, using an exponential backoff with
464 a max retry time. Thanks to Robert Olson for the original patch
465 (SF #815911) which has been slightly refactored.
466 """
467 now = time.time()
468 # Either retryTime is None, in which case this
469 # is the first time back after a disconnect, or
470 # we've waited long enough.
471 if self.retryTime is None:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000472 attempt = 1
Vinay Sajip48cfe382004-02-20 13:17:27 +0000473 else:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000474 attempt = (now >= self.retryTime)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000475 if attempt:
476 try:
477 self.sock = self.makeSocket()
478 self.retryTime = None # next time, no delay before trying
Vinay Sajipc683a872007-01-08 18:50:32 +0000479 except socket.error:
Vinay Sajip48cfe382004-02-20 13:17:27 +0000480 #Creation failed, so set the retry time and return.
481 if self.retryTime is None:
482 self.retryPeriod = self.retryStart
483 else:
484 self.retryPeriod = self.retryPeriod * self.retryFactor
485 if self.retryPeriod > self.retryMax:
486 self.retryPeriod = self.retryMax
487 self.retryTime = now + self.retryPeriod
488
Guido van Rossum57102f82002-11-13 16:15:58 +0000489 def send(self, s):
490 """
491 Send a pickled string to the socket.
492
493 This function allows for partial sends which can happen when the
494 network is busy.
495 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000496 if self.sock is None:
497 self.createSocket()
498 #self.sock can be None either because we haven't reached the retry
499 #time yet, or because we have reached the retry time and retried,
500 #but are still unable to connect.
501 if self.sock:
502 try:
503 if hasattr(self.sock, "sendall"):
504 self.sock.sendall(s)
505 else:
506 sentsofar = 0
507 left = len(s)
508 while left > 0:
509 sent = self.sock.send(s[sentsofar:])
510 sentsofar = sentsofar + sent
511 left = left - sent
512 except socket.error:
513 self.sock.close()
514 self.sock = None # so we can call createSocket next time
Guido van Rossum57102f82002-11-13 16:15:58 +0000515
516 def makePickle(self, record):
517 """
518 Pickles the record in binary format with a length prefix, and
519 returns it ready for transmission across the socket.
520 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000521 ei = record.exc_info
522 if ei:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000523 dummy = self.format(record) # just to get traceback text into record.exc_text
524 record.exc_info = None # to avoid Unpickleable error
Guido van Rossum57102f82002-11-13 16:15:58 +0000525 s = cPickle.dumps(record.__dict__, 1)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000526 if ei:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000527 record.exc_info = ei # for next handler
Guido van Rossum57102f82002-11-13 16:15:58 +0000528 slen = struct.pack(">L", len(s))
529 return slen + s
530
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000531 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000532 """
533 Handle an error during logging.
534
535 An error has occurred during logging. Most likely cause -
536 connection lost. Close the socket so that we can retry on the
537 next event.
538 """
539 if self.closeOnError and self.sock:
540 self.sock.close()
541 self.sock = None #try to reconnect next time
542 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000543 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000544
545 def emit(self, record):
546 """
547 Emit a record.
548
549 Pickles the record and writes it to the socket in binary format.
550 If there is an error with the socket, silently drop the packet.
551 If there was a problem with the socket, re-establishes the
552 socket.
553 """
554 try:
555 s = self.makePickle(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000556 self.send(s)
Vinay Sajip85c19092005-10-31 13:14:19 +0000557 except (KeyboardInterrupt, SystemExit):
558 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000559 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000560 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000561
562 def close(self):
563 """
564 Closes the socket.
565 """
Vinay Sajipd23845e2012-02-23 19:37:18 +0000566 with self.lock:
567 if self.sock:
568 self.sock.close()
569 self.sock = None
Vinay Sajip48cfe382004-02-20 13:17:27 +0000570 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000571
572class DatagramHandler(SocketHandler):
573 """
574 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000575 a datagram socket. The pickle which is sent is that of the LogRecord's
576 attribute dictionary (__dict__), so that the receiver does not need to
577 have the logging module installed in order to process the logging event.
578
579 To unpickle the record at the receiving end into a LogRecord, use the
580 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000581
582 """
583 def __init__(self, host, port):
584 """
585 Initializes the handler with a specific host address and port.
586 """
587 SocketHandler.__init__(self, host, port)
588 self.closeOnError = 0
589
590 def makeSocket(self):
591 """
592 The factory method of SocketHandler is here overridden to create
593 a UDP socket (SOCK_DGRAM).
594 """
595 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
596 return s
597
598 def send(self, s):
599 """
600 Send a pickled string to a socket.
601
602 This function no longer allows for partial sends which can happen
603 when the network is busy - UDP does not guarantee delivery and
604 can deliver packets out of sequence.
605 """
Vinay Sajipfb154172004-08-24 09:36:23 +0000606 if self.sock is None:
607 self.createSocket()
Guido van Rossum57102f82002-11-13 16:15:58 +0000608 self.sock.sendto(s, (self.host, self.port))
609
610class SysLogHandler(logging.Handler):
611 """
612 A handler class which sends formatted logging records to a syslog
613 server. Based on Sam Rushing's syslog module:
614 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
615 Contributed by Nicolas Untz (after which minor refactoring changes
616 have been made).
617 """
618
619 # from <linux/sys/syslog.h>:
620 # ======================================================================
621 # priorities/facilities are encoded into a single 32-bit quantity, where
622 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
623 # facility (0-big number). Both the priorities and the facilities map
624 # roughly one-to-one to strings in the syslogd(8) source code. This
625 # mapping is included in this file.
626 #
627 # priorities (these are ordered)
628
629 LOG_EMERG = 0 # system is unusable
630 LOG_ALERT = 1 # action must be taken immediately
631 LOG_CRIT = 2 # critical conditions
632 LOG_ERR = 3 # error conditions
633 LOG_WARNING = 4 # warning conditions
634 LOG_NOTICE = 5 # normal but significant condition
635 LOG_INFO = 6 # informational
636 LOG_DEBUG = 7 # debug-level messages
637
638 # facility codes
639 LOG_KERN = 0 # kernel messages
640 LOG_USER = 1 # random user-level messages
641 LOG_MAIL = 2 # mail system
642 LOG_DAEMON = 3 # system daemons
643 LOG_AUTH = 4 # security/authorization messages
644 LOG_SYSLOG = 5 # messages generated internally by syslogd
645 LOG_LPR = 6 # line printer subsystem
646 LOG_NEWS = 7 # network news subsystem
647 LOG_UUCP = 8 # UUCP subsystem
648 LOG_CRON = 9 # clock daemon
Vinay Sajipb0623d62010-03-24 14:31:21 +0000649 LOG_AUTHPRIV = 10 # security/authorization messages (private)
650 LOG_FTP = 11 # FTP daemon
Guido van Rossum57102f82002-11-13 16:15:58 +0000651
652 # other codes through 15 reserved for system use
653 LOG_LOCAL0 = 16 # reserved for local use
654 LOG_LOCAL1 = 17 # reserved for local use
655 LOG_LOCAL2 = 18 # reserved for local use
656 LOG_LOCAL3 = 19 # reserved for local use
657 LOG_LOCAL4 = 20 # reserved for local use
658 LOG_LOCAL5 = 21 # reserved for local use
659 LOG_LOCAL6 = 22 # reserved for local use
660 LOG_LOCAL7 = 23 # reserved for local use
661
662 priority_names = {
663 "alert": LOG_ALERT,
664 "crit": LOG_CRIT,
665 "critical": LOG_CRIT,
666 "debug": LOG_DEBUG,
667 "emerg": LOG_EMERG,
668 "err": LOG_ERR,
669 "error": LOG_ERR, # DEPRECATED
670 "info": LOG_INFO,
671 "notice": LOG_NOTICE,
672 "panic": LOG_EMERG, # DEPRECATED
673 "warn": LOG_WARNING, # DEPRECATED
674 "warning": LOG_WARNING,
675 }
676
677 facility_names = {
678 "auth": LOG_AUTH,
679 "authpriv": LOG_AUTHPRIV,
680 "cron": LOG_CRON,
681 "daemon": LOG_DAEMON,
Vinay Sajipb0623d62010-03-24 14:31:21 +0000682 "ftp": LOG_FTP,
Guido van Rossum57102f82002-11-13 16:15:58 +0000683 "kern": LOG_KERN,
684 "lpr": LOG_LPR,
685 "mail": LOG_MAIL,
686 "news": LOG_NEWS,
687 "security": LOG_AUTH, # DEPRECATED
688 "syslog": LOG_SYSLOG,
689 "user": LOG_USER,
690 "uucp": LOG_UUCP,
691 "local0": LOG_LOCAL0,
692 "local1": LOG_LOCAL1,
693 "local2": LOG_LOCAL2,
694 "local3": LOG_LOCAL3,
695 "local4": LOG_LOCAL4,
696 "local5": LOG_LOCAL5,
697 "local6": LOG_LOCAL6,
698 "local7": LOG_LOCAL7,
699 }
700
Vinay Sajipdc579362006-07-20 23:20:12 +0000701 #The map below appears to be trivially lowercasing the key. However,
702 #there's more to it than meets the eye - in some locales, lowercasing
703 #gives unexpected results. See SF #1524081: in the Turkish locale,
704 #"INFO".lower() != "info"
705 priority_map = {
706 "DEBUG" : "debug",
707 "INFO" : "info",
708 "WARNING" : "warning",
709 "ERROR" : "error",
710 "CRITICAL" : "critical"
711 }
712
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000713 def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
714 facility=LOG_USER, socktype=socket.SOCK_DGRAM):
Guido van Rossum57102f82002-11-13 16:15:58 +0000715 """
716 Initialize a handler.
717
Vinay Sajip754a5fb2007-05-25 07:05:59 +0000718 If address is specified as a string, a UNIX socket is used. To log to a
719 local syslogd, "SysLogHandler(address="/dev/log")" can be used.
Guido van Rossum57102f82002-11-13 16:15:58 +0000720 If facility is not specified, LOG_USER is used.
721 """
722 logging.Handler.__init__(self)
723
724 self.address = address
725 self.facility = facility
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000726 self.socktype = socktype
727
728 if isinstance(address, basestring):
Guido van Rossum57102f82002-11-13 16:15:58 +0000729 self.unixsocket = 1
Vinay Sajip5492e172006-12-11 14:07:16 +0000730 self._connect_unixsocket(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000731 else:
Guido van Rossum57102f82002-11-13 16:15:58 +0000732 self.unixsocket = 0
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000733 self.socket = socket.socket(socket.AF_INET, socktype)
734 if socktype == socket.SOCK_STREAM:
735 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000736 self.formatter = None
737
Vinay Sajipa1974c12005-01-13 08:23:56 +0000738 def _connect_unixsocket(self, address):
739 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
740 # syslog may require either DGRAM or STREAM sockets
741 try:
742 self.socket.connect(address)
743 except socket.error:
744 self.socket.close()
745 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Vinay Sajip8b6b53f2005-11-09 13:55:13 +0000746 self.socket.connect(address)
Vinay Sajipa1974c12005-01-13 08:23:56 +0000747
Guido van Rossum57102f82002-11-13 16:15:58 +0000748 # curious: when talking to the unix-domain '/dev/log' socket, a
749 # zero-terminator seems to be required. this string is placed
750 # into a class variable so that it can be overridden if
751 # necessary.
752 log_format_string = '<%d>%s\000'
753
Vinay Sajipdc579362006-07-20 23:20:12 +0000754 def encodePriority(self, facility, priority):
Guido van Rossum57102f82002-11-13 16:15:58 +0000755 """
756 Encode the facility and priority. You can pass in strings or
757 integers - if strings are passed, the facility_names and
758 priority_names mapping dictionaries are used to convert them to
759 integers.
760 """
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000761 if isinstance(facility, basestring):
Guido van Rossum57102f82002-11-13 16:15:58 +0000762 facility = self.facility_names[facility]
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000763 if isinstance(priority, basestring):
Guido van Rossum57102f82002-11-13 16:15:58 +0000764 priority = self.priority_names[priority]
765 return (facility << 3) | priority
766
767 def close (self):
768 """
769 Closes the socket.
770 """
Vinay Sajipd23845e2012-02-23 19:37:18 +0000771 with self.lock:
772 if self.unixsocket:
773 self.socket.close()
Vinay Sajip48cfe382004-02-20 13:17:27 +0000774 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000775
Vinay Sajipdc579362006-07-20 23:20:12 +0000776 def mapPriority(self, levelName):
777 """
778 Map a logging level name to a key in the priority_names map.
779 This is useful in two scenarios: when custom levels are being
780 used, and in the case where you can't do a straightforward
781 mapping by lowercasing the logging level name because of locale-
782 specific issues (see SF #1524081).
783 """
784 return self.priority_map.get(levelName, "warning")
785
Guido van Rossum57102f82002-11-13 16:15:58 +0000786 def emit(self, record):
787 """
788 Emit a record.
789
790 The record is formatted, and then sent to the syslog server. If
791 exception information is present, it is NOT sent to the server.
792 """
Vinay Sajipe900b492010-09-03 09:06:07 +0000793 msg = self.format(record) + '\000'
Guido van Rossum57102f82002-11-13 16:15:58 +0000794 """
795 We need to convert record level to lowercase, maybe this will
796 change in the future.
797 """
Vinay Sajipe900b492010-09-03 09:06:07 +0000798 prio = '<%d>' % self.encodePriority(self.facility,
799 self.mapPriority(record.levelname))
800 # Message is a string. Convert to bytes as required by RFC 5424
801 if type(msg) is unicode:
Vinay Sajip5ac65282009-10-21 20:22:14 +0000802 msg = msg.encode('utf-8')
803 if codecs:
804 msg = codecs.BOM_UTF8 + msg
Vinay Sajipe900b492010-09-03 09:06:07 +0000805 msg = prio + msg
Guido van Rossum57102f82002-11-13 16:15:58 +0000806 try:
807 if self.unixsocket:
Vinay Sajipa1974c12005-01-13 08:23:56 +0000808 try:
809 self.socket.send(msg)
810 except socket.error:
811 self._connect_unixsocket(self.address)
812 self.socket.send(msg)
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000813 elif self.socktype == socket.SOCK_DGRAM:
Guido van Rossum57102f82002-11-13 16:15:58 +0000814 self.socket.sendto(msg, self.address)
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000815 else:
816 self.socket.sendall(msg)
Vinay Sajip85c19092005-10-31 13:14:19 +0000817 except (KeyboardInterrupt, SystemExit):
818 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000819 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000820 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000821
822class SMTPHandler(logging.Handler):
823 """
824 A handler class which sends an SMTP email for each logging event.
825 """
Vinay Sajip48305662009-12-06 17:57:11 +0000826 def __init__(self, mailhost, fromaddr, toaddrs, subject,
Vinay Sajipbd1094a2009-12-06 18:05:04 +0000827 credentials=None, secure=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000828 """
829 Initialize the handler.
830
831 Initialize the instance with the from and to addresses and subject
832 line of the email. To specify a non-standard SMTP port, use the
Vinay Sajip70c8e8b2007-05-01 10:20:03 +0000833 (host, port) tuple format for the mailhost argument. To specify
834 authentication credentials, supply a (username, password) tuple
Vinay Sajip48305662009-12-06 17:57:11 +0000835 for the credentials argument. To specify the use of a secure
Vinay Sajipbd1094a2009-12-06 18:05:04 +0000836 protocol (TLS), pass in a tuple for the secure argument. This will
837 only be used when authentication credentials are supplied. The tuple
838 will be either an empty tuple, or a single-value tuple with the name
839 of a keyfile, or a 2-value tuple with the names of the keyfile and
840 certificate file. (This tuple is passed to the `starttls` method).
Guido van Rossum57102f82002-11-13 16:15:58 +0000841 """
842 logging.Handler.__init__(self)
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000843 if isinstance(mailhost, tuple):
Vinay Sajip70c8e8b2007-05-01 10:20:03 +0000844 self.mailhost, self.mailport = mailhost
Guido van Rossum57102f82002-11-13 16:15:58 +0000845 else:
Vinay Sajip70c8e8b2007-05-01 10:20:03 +0000846 self.mailhost, self.mailport = mailhost, None
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000847 if isinstance(credentials, tuple):
Vinay Sajip70c8e8b2007-05-01 10:20:03 +0000848 self.username, self.password = credentials
849 else:
850 self.username = None
Guido van Rossum57102f82002-11-13 16:15:58 +0000851 self.fromaddr = fromaddr
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000852 if isinstance(toaddrs, basestring):
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000853 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000854 self.toaddrs = toaddrs
855 self.subject = subject
Vinay Sajip48305662009-12-06 17:57:11 +0000856 self.secure = secure
Guido van Rossum57102f82002-11-13 16:15:58 +0000857
858 def getSubject(self, record):
859 """
860 Determine the subject for the email.
861
862 If you want to specify a subject line which is record-dependent,
863 override this method.
864 """
865 return self.subject
866
867 def emit(self, record):
868 """
869 Emit a record.
870
871 Format the record and send it to the specified addressees.
872 """
873 try:
874 import smtplib
Vinay Sajip7ce5c832010-02-07 13:06:51 +0000875 from email.utils import formatdate
Guido van Rossum57102f82002-11-13 16:15:58 +0000876 port = self.mailport
877 if not port:
878 port = smtplib.SMTP_PORT
879 smtp = smtplib.SMTP(self.mailhost, port)
880 msg = self.format(record)
Neal Norwitzf297bd12003-04-23 03:49:43 +0000881 msg = "From: %s\r\nTo: %s\r\nSubject: %s\r\nDate: %s\r\n\r\n%s" % (
Guido van Rossum57102f82002-11-13 16:15:58 +0000882 self.fromaddr,
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000883 ",".join(self.toaddrs),
Neal Norwitzf297bd12003-04-23 03:49:43 +0000884 self.getSubject(record),
Martin v. Löwis318a12e2004-08-18 12:27:40 +0000885 formatdate(), msg)
Vinay Sajip70c8e8b2007-05-01 10:20:03 +0000886 if self.username:
Vinay Sajipbd1094a2009-12-06 18:05:04 +0000887 if self.secure is not None:
Vinay Sajip48305662009-12-06 17:57:11 +0000888 smtp.ehlo()
Vinay Sajipbd1094a2009-12-06 18:05:04 +0000889 smtp.starttls(*self.secure)
Vinay Sajip48305662009-12-06 17:57:11 +0000890 smtp.ehlo()
Vinay Sajip70c8e8b2007-05-01 10:20:03 +0000891 smtp.login(self.username, self.password)
Guido van Rossum57102f82002-11-13 16:15:58 +0000892 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
893 smtp.quit()
Vinay Sajip245a5ab2005-10-31 14:27:01 +0000894 except (KeyboardInterrupt, SystemExit):
895 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000896 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000897 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000898
899class NTEventLogHandler(logging.Handler):
900 """
901 A handler class which sends events to the NT Event Log. Adds a
902 registry entry for the specified application name. If no dllname is
903 provided, win32service.pyd (which contains some basic message
904 placeholders) is used. Note that use of these placeholders will make
905 your event logs big, as the entire message source is held in the log.
906 If you want slimmer logs, you have to pass in the name of your own DLL
907 which contains the message definitions you want to use in the event log.
908 """
909 def __init__(self, appname, dllname=None, logtype="Application"):
910 logging.Handler.__init__(self)
911 try:
912 import win32evtlogutil, win32evtlog
913 self.appname = appname
914 self._welu = win32evtlogutil
915 if not dllname:
916 dllname = os.path.split(self._welu.__file__)
917 dllname = os.path.split(dllname[0])
918 dllname = os.path.join(dllname[0], r'win32service.pyd')
919 self.dllname = dllname
920 self.logtype = logtype
921 self._welu.AddSourceToRegistry(appname, dllname, logtype)
922 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
923 self.typemap = {
924 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
925 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000926 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000927 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
928 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
929 }
930 except ImportError:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +0000931 print("The Python Win32 extensions for NT (service, event "\
932 "logging) appear not to be available.")
Guido van Rossum57102f82002-11-13 16:15:58 +0000933 self._welu = None
934
935 def getMessageID(self, record):
936 """
937 Return the message ID for the event record. If you are using your
938 own messages, you could do this by having the msg passed to the
939 logger being an ID rather than a formatting string. Then, in here,
940 you could use a dictionary lookup to get the message ID. This
941 version returns 1, which is the base message ID in win32service.pyd.
942 """
943 return 1
944
945 def getEventCategory(self, record):
946 """
947 Return the event category for the record.
948
949 Override this if you want to specify your own categories. This version
950 returns 0.
951 """
952 return 0
953
954 def getEventType(self, record):
955 """
956 Return the event type for the record.
957
958 Override this if you want to specify your own types. This version does
959 a mapping using the handler's typemap attribute, which is set up in
960 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000961 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000962 either need to override this method or place a suitable dictionary in
963 the handler's typemap attribute.
964 """
965 return self.typemap.get(record.levelno, self.deftype)
966
967 def emit(self, record):
968 """
969 Emit a record.
970
971 Determine the message ID, event category and event type. Then
972 log the message in the NT event log.
973 """
974 if self._welu:
975 try:
976 id = self.getMessageID(record)
977 cat = self.getEventCategory(record)
978 type = self.getEventType(record)
979 msg = self.format(record)
980 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
Vinay Sajip245a5ab2005-10-31 14:27:01 +0000981 except (KeyboardInterrupt, SystemExit):
982 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000983 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000984 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000985
986 def close(self):
987 """
988 Clean up this handler.
989
990 You can remove the application name from the registry as a
991 source of event log entries. However, if you do this, you will
992 not be able to see the events as you intended in the Event Log
993 Viewer - it needs to be able to access the registry to get the
994 DLL name.
995 """
996 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000997 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000998
999class HTTPHandler(logging.Handler):
1000 """
1001 A class which sends records to a Web server, using either GET or
1002 POST semantics.
1003 """
1004 def __init__(self, host, url, method="GET"):
1005 """
1006 Initialize the instance with the host, the request URL, and the method
1007 ("GET" or "POST")
1008 """
1009 logging.Handler.__init__(self)
Vinay Sajip1c77b7f2009-10-10 20:32:36 +00001010 method = method.upper()
Guido van Rossum57102f82002-11-13 16:15:58 +00001011 if method not in ["GET", "POST"]:
Vinay Sajip1c77b7f2009-10-10 20:32:36 +00001012 raise ValueError("method must be GET or POST")
Guido van Rossum57102f82002-11-13 16:15:58 +00001013 self.host = host
1014 self.url = url
1015 self.method = method
1016
Neal Norwitzf297bd12003-04-23 03:49:43 +00001017 def mapLogRecord(self, record):
1018 """
1019 Default implementation of mapping the log record into a dict
Vinay Sajip48cfe382004-02-20 13:17:27 +00001020 that is sent as the CGI data. Overwrite in your class.
Neal Norwitzf297bd12003-04-23 03:49:43 +00001021 Contributed by Franz Glasner.
1022 """
1023 return record.__dict__
1024
Guido van Rossum57102f82002-11-13 16:15:58 +00001025 def emit(self, record):
1026 """
1027 Emit a record.
1028
Senthil Kumarana5ba05c2010-08-09 20:18:04 +00001029 Send the record to the Web server as a percent-encoded dictionary
Guido van Rossum57102f82002-11-13 16:15:58 +00001030 """
1031 try:
1032 import httplib, urllib
Vinay Sajipb7935062005-10-11 13:15:31 +00001033 host = self.host
1034 h = httplib.HTTP(host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001035 url = self.url
Neal Norwitzf297bd12003-04-23 03:49:43 +00001036 data = urllib.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +00001037 if self.method == "GET":
Vinay Sajip1c77b7f2009-10-10 20:32:36 +00001038 if (url.find('?') >= 0):
Guido van Rossum57102f82002-11-13 16:15:58 +00001039 sep = '&'
1040 else:
1041 sep = '?'
1042 url = url + "%c%s" % (sep, data)
1043 h.putrequest(self.method, url)
Vinay Sajipb7935062005-10-11 13:15:31 +00001044 # support multiple hosts on one IP address...
1045 # need to strip optional :port from host, if present
Vinay Sajip1c77b7f2009-10-10 20:32:36 +00001046 i = host.find(":")
Vinay Sajipb7935062005-10-11 13:15:31 +00001047 if i >= 0:
1048 host = host[:i]
1049 h.putheader("Host", host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001050 if self.method == "POST":
Vinay Sajipb7935062005-10-11 13:15:31 +00001051 h.putheader("Content-type",
1052 "application/x-www-form-urlencoded")
Guido van Rossum57102f82002-11-13 16:15:58 +00001053 h.putheader("Content-length", str(len(data)))
Kristján Valur Jónsson84040db2009-01-09 20:27:16 +00001054 h.endheaders(data if self.method == "POST" else None)
Guido van Rossum57102f82002-11-13 16:15:58 +00001055 h.getreply() #can't do anything with the result
Vinay Sajip245a5ab2005-10-31 14:27:01 +00001056 except (KeyboardInterrupt, SystemExit):
1057 raise
Guido van Rossum57102f82002-11-13 16:15:58 +00001058 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001059 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001060
1061class BufferingHandler(logging.Handler):
1062 """
1063 A handler class which buffers logging records in memory. Whenever each
1064 record is added to the buffer, a check is made to see if the buffer should
1065 be flushed. If it should, then flush() is expected to do what's needed.
1066 """
1067 def __init__(self, capacity):
1068 """
1069 Initialize the handler with the buffer size.
1070 """
1071 logging.Handler.__init__(self)
1072 self.capacity = capacity
1073 self.buffer = []
1074
1075 def shouldFlush(self, record):
1076 """
1077 Should the handler flush its buffer?
1078
1079 Returns true if the buffer is up to capacity. This method can be
1080 overridden to implement custom flushing strategies.
1081 """
1082 return (len(self.buffer) >= self.capacity)
1083
1084 def emit(self, record):
1085 """
1086 Emit a record.
1087
1088 Append the record. If shouldFlush() tells us to, call flush() to process
1089 the buffer.
1090 """
1091 self.buffer.append(record)
1092 if self.shouldFlush(record):
1093 self.flush()
1094
1095 def flush(self):
1096 """
1097 Override to implement custom flushing behaviour.
1098
1099 This version just zaps the buffer to empty.
1100 """
Vinay Sajipd23845e2012-02-23 19:37:18 +00001101 with self.lock:
1102 self.buffer = []
Guido van Rossum57102f82002-11-13 16:15:58 +00001103
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001104 def close(self):
1105 """
1106 Close the handler.
1107
1108 This version just flushes and chains to the parent class' close().
1109 """
1110 self.flush()
1111 logging.Handler.close(self)
1112
Guido van Rossum57102f82002-11-13 16:15:58 +00001113class MemoryHandler(BufferingHandler):
1114 """
1115 A handler class which buffers logging records in memory, periodically
1116 flushing them to a target handler. Flushing occurs whenever the buffer
1117 is full, or when an event of a certain severity or greater is seen.
1118 """
1119 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
1120 """
1121 Initialize the handler with the buffer size, the level at which
1122 flushing should occur and an optional target.
1123
1124 Note that without a target being set either here or via setTarget(),
1125 a MemoryHandler is no use to anyone!
1126 """
1127 BufferingHandler.__init__(self, capacity)
1128 self.flushLevel = flushLevel
1129 self.target = target
1130
1131 def shouldFlush(self, record):
1132 """
1133 Check for buffer full or a record at the flushLevel or higher.
1134 """
1135 return (len(self.buffer) >= self.capacity) or \
1136 (record.levelno >= self.flushLevel)
1137
1138 def setTarget(self, target):
1139 """
1140 Set the target handler for this handler.
1141 """
1142 self.target = target
1143
1144 def flush(self):
1145 """
1146 For a MemoryHandler, flushing means just sending the buffered
1147 records to the target, if there is one. Override if you want
1148 different behaviour.
1149 """
Vinay Sajipd23845e2012-02-23 19:37:18 +00001150 with self.lock:
1151 if self.target:
1152 for record in self.buffer:
1153 self.target.handle(record)
1154 self.buffer = []
Guido van Rossum57102f82002-11-13 16:15:58 +00001155
1156 def close(self):
1157 """
1158 Flush, set the target to None and lose the buffer.
1159 """
1160 self.flush()
Vinay Sajipd23845e2012-02-23 19:37:18 +00001161 with self.lock:
1162 self.target = None
1163 BufferingHandler.close(self)