blob: 52e18e5410f8975d5531fba30064b5048c9a2948 [file] [log] [blame]
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001# Copyright 2001-2010 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 Sajipdb81c4c2010-02-25 23:13:06 +000022Copyright (C) 2001-2010 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000023
Vinay Sajip6268cbc2009-01-21 00:19:28 +000024To use, simply 'import logging.handlers' and log away!
Guido van Rossum57102f82002-11-13 16:15:58 +000025"""
26
Benjamin Petersonad9d48d2008-04-02 21:49:44 +000027import logging, socket, os, pickle, struct, time, re
Florent Xicluna5252f9f2011-11-07 19:43:05 +010028from codecs import BOM_UTF8
Benjamin Peterson9451a1c2010-03-13 22:30:34 +000029from stat import ST_DEV, ST_INO, ST_MTIME
Vinay Sajipe723e962011-04-15 22:27:17 +010030import queue
Victor Stinnercafa2ef2011-05-02 16:11:28 +020031try:
32 import threading
Vinay Sajip0372e102011-05-05 12:59:14 +010033except ImportError: #pragma: no cover
Victor Stinnercafa2ef2011-05-02 16:11:28 +020034 threading = None
Guido van Rossum57102f82002-11-13 16:15:58 +000035
Guido van Rossum57102f82002-11-13 16:15:58 +000036#
37# Some constants...
38#
39
40DEFAULT_TCP_LOGGING_PORT = 9020
41DEFAULT_UDP_LOGGING_PORT = 9021
42DEFAULT_HTTP_LOGGING_PORT = 9022
43DEFAULT_SOAP_LOGGING_PORT = 9023
44SYSLOG_UDP_PORT = 514
Vinay Sajipcbabd7e2009-10-10 20:32:36 +000045SYSLOG_TCP_PORT = 514
Guido van Rossum57102f82002-11-13 16:15:58 +000046
Thomas Wouters477c8d52006-05-27 19:21:47 +000047_MIDNIGHT = 24 * 60 * 60 # number of seconds in a day
48
Vinay Sajip17c52d82004-07-03 11:48:34 +000049class BaseRotatingHandler(logging.FileHandler):
50 """
51 Base class for handlers that rotate log files at a certain point.
52 Not meant to be instantiated directly. Instead, use RotatingFileHandler
53 or TimedRotatingFileHandler.
54 """
Christian Heimese7a15bb2008-01-24 16:21:45 +000055 def __init__(self, filename, mode, encoding=None, delay=0):
Vinay Sajip17c52d82004-07-03 11:48:34 +000056 """
57 Use the specified filename for streamed logging
58 """
Christian Heimese7a15bb2008-01-24 16:21:45 +000059 logging.FileHandler.__init__(self, filename, mode, encoding, delay)
Vinay Sajip4600f112005-03-13 09:56:36 +000060 self.mode = mode
61 self.encoding = encoding
Guido van Rossum57102f82002-11-13 16:15:58 +000062
Vinay Sajip17c52d82004-07-03 11:48:34 +000063 def emit(self, record):
64 """
65 Emit a record.
66
67 Output the record to the file, catering for rollover as described
68 in doRollover().
69 """
Vinay Sajip3970c112004-07-08 10:24:04 +000070 try:
71 if self.shouldRollover(record):
72 self.doRollover()
73 logging.FileHandler.emit(self, record)
Vinay Sajip985ef872011-04-26 19:34:04 +010074 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip85c19092005-10-31 13:14:19 +000075 raise
Vinay Sajip3970c112004-07-08 10:24:04 +000076 except:
77 self.handleError(record)
Vinay Sajip17c52d82004-07-03 11:48:34 +000078
79class RotatingFileHandler(BaseRotatingHandler):
80 """
81 Handler for logging to a set of files, which switches from one file
82 to the next when the current file reaches a certain size.
83 """
Christian Heimese7a15bb2008-01-24 16:21:45 +000084 def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0):
Guido van Rossum57102f82002-11-13 16:15:58 +000085 """
86 Open the specified file and use it as the stream for logging.
87
88 By default, the file grows indefinitely. You can specify particular
89 values of maxBytes and backupCount to allow the file to rollover at
90 a predetermined size.
91
92 Rollover occurs whenever the current log file is nearly maxBytes in
93 length. If backupCount is >= 1, the system will successively create
94 new files with the same pathname as the base file, but with extensions
95 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
96 and a base file name of "app.log", you would get "app.log",
97 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
98 written to is always "app.log" - when it gets filled up, it is closed
99 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
100 exist, then they are renamed to "app.log.2", "app.log.3" etc.
101 respectively.
102
103 If maxBytes is zero, rollover never occurs.
104 """
Vinay Sajip89994b22010-08-22 18:11:02 +0000105 # If rotation/rollover is wanted, it doesn't make sense to use another
106 # mode. If for example 'w' were specified, then if there were multiple
107 # runs of the calling application, the logs from previous runs would be
108 # lost if the 'w' is respected, because the log file would be truncated
109 # on each run.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000110 if maxBytes > 0:
Vinay Sajip89994b22010-08-22 18:11:02 +0000111 mode = 'a'
Christian Heimese7a15bb2008-01-24 16:21:45 +0000112 BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
Guido van Rossum57102f82002-11-13 16:15:58 +0000113 self.maxBytes = maxBytes
114 self.backupCount = backupCount
Guido van Rossum57102f82002-11-13 16:15:58 +0000115
116 def doRollover(self):
117 """
118 Do a rollover, as described in __init__().
119 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000120 if self.stream:
121 self.stream.close()
Vinay Sajip01241d62011-01-21 23:35:57 +0000122 self.stream = None
Guido van Rossum57102f82002-11-13 16:15:58 +0000123 if self.backupCount > 0:
124 for i in range(self.backupCount - 1, 0, -1):
125 sfn = "%s.%d" % (self.baseFilename, i)
126 dfn = "%s.%d" % (self.baseFilename, i + 1)
127 if os.path.exists(sfn):
Guido van Rossum57102f82002-11-13 16:15:58 +0000128 if os.path.exists(dfn):
129 os.remove(dfn)
130 os.rename(sfn, dfn)
131 dfn = self.baseFilename + ".1"
132 if os.path.exists(dfn):
133 os.remove(dfn)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000134 os.rename(self.baseFilename, dfn)
Thomas Woutersb2137042007-02-01 18:02:27 +0000135 self.mode = 'w'
136 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000137
Vinay Sajip17c52d82004-07-03 11:48:34 +0000138 def shouldRollover(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000139 """
Vinay Sajip17c52d82004-07-03 11:48:34 +0000140 Determine if rollover should occur.
Guido van Rossum57102f82002-11-13 16:15:58 +0000141
Vinay Sajip17c52d82004-07-03 11:48:34 +0000142 Basically, see if the supplied record would cause the file to exceed
143 the size limit we have.
Guido van Rossum57102f82002-11-13 16:15:58 +0000144 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000145 if self.stream is None: # delay was set...
146 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000147 if self.maxBytes > 0: # are we rolling over?
148 msg = "%s\n" % self.format(record)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000149 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
Guido van Rossum57102f82002-11-13 16:15:58 +0000150 if self.stream.tell() + len(msg) >= self.maxBytes:
Vinay Sajip17c52d82004-07-03 11:48:34 +0000151 return 1
152 return 0
Guido van Rossum57102f82002-11-13 16:15:58 +0000153
Vinay Sajip17c52d82004-07-03 11:48:34 +0000154class TimedRotatingFileHandler(BaseRotatingHandler):
155 """
156 Handler for logging to a file, rotating the log file at certain timed
157 intervals.
158
159 If backupCount is > 0, when rollover is done, no more than backupCount
160 files are kept - the oldest ones are deleted.
161 """
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000162 def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
Christian Heimese7a15bb2008-01-24 16:21:45 +0000163 BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000164 self.when = when.upper()
Vinay Sajip17c52d82004-07-03 11:48:34 +0000165 self.backupCount = backupCount
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000166 self.utc = utc
Vinay Sajip17c52d82004-07-03 11:48:34 +0000167 # Calculate the real rollover interval, which is just the number of
168 # seconds between rollovers. Also set the filename suffix used when
169 # a rollover occurs. Current 'when' events supported:
170 # S - Seconds
171 # M - Minutes
172 # H - Hours
173 # D - Days
174 # midnight - roll over at midnight
175 # W{0-6} - roll over on a certain day; 0 - Monday
176 #
177 # Case of the 'when' specifier is not important; lower or upper case
178 # will work.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000179 if self.when == 'S':
180 self.interval = 1 # one second
181 self.suffix = "%Y-%m-%d_%H-%M-%S"
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000182 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000183 elif self.when == 'M':
184 self.interval = 60 # one minute
185 self.suffix = "%Y-%m-%d_%H-%M"
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000186 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000187 elif self.when == 'H':
188 self.interval = 60 * 60 # one hour
189 self.suffix = "%Y-%m-%d_%H"
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000190 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000191 elif self.when == 'D' or self.when == 'MIDNIGHT':
192 self.interval = 60 * 60 * 24 # one day
193 self.suffix = "%Y-%m-%d"
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000194 self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000195 elif self.when.startswith('W'):
196 self.interval = 60 * 60 * 24 * 7 # one week
197 if len(self.when) != 2:
198 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
199 if self.when[1] < '0' or self.when[1] > '6':
200 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
201 self.dayOfWeek = int(self.when[1])
202 self.suffix = "%Y-%m-%d"
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000203 self.extMatch = r"^\d{4}-\d{2}-\d{2}$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000204 else:
205 raise ValueError("Invalid rollover interval specified: %s" % self.when)
206
Antoine Pitroufd036452008-08-19 17:56:33 +0000207 self.extMatch = re.compile(self.extMatch, re.ASCII)
Vinay Sajipe7d40662004-10-03 19:12:07 +0000208 self.interval = self.interval * interval # multiply by units requested
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000209 if os.path.exists(filename):
210 t = os.stat(filename)[ST_MTIME]
211 else:
212 t = int(time.time())
213 self.rolloverAt = self.computeRollover(t)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000214
Vinay Sajipaffbd872009-06-11 10:11:47 +0000215 def computeRollover(self, currentTime):
216 """
217 Work out the rollover time based on the specified time.
218 """
219 result = currentTime + self.interval
Vinay Sajip17c52d82004-07-03 11:48:34 +0000220 # If we are rolling over at midnight or weekly, then the interval is already known.
221 # What we need to figure out is WHEN the next interval is. In other words,
222 # if you are rolling over at midnight, then your base interval is 1 day,
223 # but you want to start that one day clock at midnight, not now. So, we
224 # have to fudge the rolloverAt value in order to trigger the first rollover
225 # at the right time. After that, the regular interval will take care of
226 # the rest. Note that this code doesn't care about leap seconds. :)
227 if self.when == 'MIDNIGHT' or self.when.startswith('W'):
228 # This could be done with less code, but I wanted it to be clear
Vinay Sajipaffbd872009-06-11 10:11:47 +0000229 if self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000230 t = time.gmtime(currentTime)
231 else:
232 t = time.localtime(currentTime)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000233 currentHour = t[3]
234 currentMinute = t[4]
235 currentSecond = t[5]
236 # r is the number of seconds left between now and midnight
Thomas Wouters477c8d52006-05-27 19:21:47 +0000237 r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
238 currentSecond)
Vinay Sajipaffbd872009-06-11 10:11:47 +0000239 result = currentTime + r
Vinay Sajip17c52d82004-07-03 11:48:34 +0000240 # If we are rolling over on a certain day, add in the number of days until
241 # the next rollover, but offset by 1 since we just calculated the time
242 # until the next day starts. There are three cases:
243 # Case 1) The day to rollover is today; in this case, do nothing
244 # Case 2) The day to rollover is further in the interval (i.e., today is
245 # day 2 (Wednesday) and rollover is on day 6 (Sunday). Days to
246 # next rollover is simply 6 - 2 - 1, or 3.
247 # Case 3) The day to rollover is behind us in the interval (i.e., today
248 # is day 5 (Saturday) and rollover is on day 3 (Thursday).
249 # Days to rollover is 6 - 5 + 3, or 4. In this case, it's the
250 # number of days left in the current week (1) plus the number
251 # of days in the next week until the rollover day (3).
Georg Brandl86def6c2008-01-21 20:36:10 +0000252 # The calculations described in 2) and 3) above need to have a day added.
253 # This is because the above time calculation takes us to midnight on this
254 # day, i.e. the start of the next day.
Vinay Sajipaffbd872009-06-11 10:11:47 +0000255 if self.when.startswith('W'):
Vinay Sajip17c52d82004-07-03 11:48:34 +0000256 day = t[6] # 0 is Monday
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000257 if day != self.dayOfWeek:
258 if day < self.dayOfWeek:
Georg Brandl86def6c2008-01-21 20:36:10 +0000259 daysToWait = self.dayOfWeek - day
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000260 else:
Georg Brandl86def6c2008-01-21 20:36:10 +0000261 daysToWait = 6 - day + self.dayOfWeek + 1
Vinay Sajipaffbd872009-06-11 10:11:47 +0000262 newRolloverAt = result + (daysToWait * (60 * 60 * 24))
263 if not self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000264 dstNow = t[-1]
265 dstAtRollover = time.localtime(newRolloverAt)[-1]
266 if dstNow != dstAtRollover:
267 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
268 newRolloverAt = newRolloverAt - 3600
269 else: # DST bows out before next rollover, so we need to add an hour
270 newRolloverAt = newRolloverAt + 3600
Vinay Sajipaffbd872009-06-11 10:11:47 +0000271 result = newRolloverAt
272 return result
Vinay Sajip17c52d82004-07-03 11:48:34 +0000273
274 def shouldRollover(self, record):
275 """
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000276 Determine if rollover should occur.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000277
278 record is not used, as we are just comparing times, but it is needed so
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000279 the method signatures are the same
Vinay Sajip17c52d82004-07-03 11:48:34 +0000280 """
281 t = int(time.time())
282 if t >= self.rolloverAt:
283 return 1
Vinay Sajip17c52d82004-07-03 11:48:34 +0000284 return 0
285
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000286 def getFilesToDelete(self):
287 """
288 Determine the files to delete when rolling over.
289
290 More specific than the earlier method, which just used glob.glob().
291 """
292 dirName, baseName = os.path.split(self.baseFilename)
293 fileNames = os.listdir(dirName)
294 result = []
295 prefix = baseName + "."
296 plen = len(prefix)
297 for fileName in fileNames:
298 if fileName[:plen] == prefix:
299 suffix = fileName[plen:]
300 if self.extMatch.match(suffix):
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000301 result.append(os.path.join(dirName, fileName))
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000302 result.sort()
303 if len(result) < self.backupCount:
304 result = []
305 else:
306 result = result[:len(result) - self.backupCount]
307 return result
308
Vinay Sajip17c52d82004-07-03 11:48:34 +0000309 def doRollover(self):
310 """
311 do a rollover; in this case, a date/time stamp is appended to the filename
312 when the rollover happens. However, you want the file to be named for the
313 start of the interval, not the current time. If there is a backup count,
314 then we have to get a list of matching filenames, sort them and remove
315 the one with the oldest suffix.
316 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000317 if self.stream:
318 self.stream.close()
Vinay Sajip01241d62011-01-21 23:35:57 +0000319 self.stream = None
Vinay Sajip17c52d82004-07-03 11:48:34 +0000320 # get the time that this sequence started at and make it a TimeTuple
321 t = self.rolloverAt - self.interval
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000322 if self.utc:
323 timeTuple = time.gmtime(t)
324 else:
325 timeTuple = time.localtime(t)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000326 dfn = self.baseFilename + "." + time.strftime(self.suffix, timeTuple)
327 if os.path.exists(dfn):
328 os.remove(dfn)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000329 os.rename(self.baseFilename, dfn)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000330 if self.backupCount > 0:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000331 for s in self.getFilesToDelete():
332 os.remove(s)
Thomas Woutersb2137042007-02-01 18:02:27 +0000333 self.mode = 'w'
334 self.stream = self._open()
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000335 currentTime = int(time.time())
Vinay Sajipaffbd872009-06-11 10:11:47 +0000336 newRolloverAt = self.computeRollover(currentTime)
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000337 while newRolloverAt <= currentTime:
338 newRolloverAt = newRolloverAt + self.interval
339 #If DST changes and midnight or weekly rollover, adjust for this.
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000340 if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000341 dstNow = time.localtime(currentTime)[-1]
342 dstAtRollover = time.localtime(newRolloverAt)[-1]
343 if dstNow != dstAtRollover:
344 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
345 newRolloverAt = newRolloverAt - 3600
346 else: # DST bows out before next rollover, so we need to add an hour
347 newRolloverAt = newRolloverAt + 3600
348 self.rolloverAt = newRolloverAt
Guido van Rossum57102f82002-11-13 16:15:58 +0000349
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000350class WatchedFileHandler(logging.FileHandler):
351 """
352 A handler for logging to a file, which watches the file
353 to see if it has changed while in use. This can happen because of
354 usage of programs such as newsyslog and logrotate which perform
355 log file rotation. This handler, intended for use under Unix,
356 watches the file to see if it has changed since the last emit.
357 (A file has changed if its device or inode have changed.)
358 If it has changed, the old file stream is closed, and the file
359 opened to get a new stream.
360
361 This handler is not appropriate for use under Windows, because
362 under Windows open files cannot be moved or renamed - logging
363 opens the files with exclusive locks - and so there is no need
364 for such a handler. Furthermore, ST_INO is not supported under
365 Windows; stat always returns zero for this value.
366
367 This handler is based on a suggestion and patch by Chad J.
368 Schroeder.
369 """
Christian Heimese7a15bb2008-01-24 16:21:45 +0000370 def __init__(self, filename, mode='a', encoding=None, delay=0):
371 logging.FileHandler.__init__(self, filename, mode, encoding, delay)
372 if not os.path.exists(self.baseFilename):
373 self.dev, self.ino = -1, -1
374 else:
375 stat = os.stat(self.baseFilename)
376 self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000377
378 def emit(self, record):
379 """
380 Emit a record.
381
382 First check if the underlying file has changed, and if it
383 has, close the old stream and reopen the file to get the
384 current stream.
385 """
386 if not os.path.exists(self.baseFilename):
387 stat = None
Vinay Sajipde19e082011-04-30 21:52:26 +0100388 changed = True
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000389 else:
390 stat = os.stat(self.baseFilename)
391 changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino)
Christian Heimese7a15bb2008-01-24 16:21:45 +0000392 if changed and self.stream is not None:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000393 self.stream.flush()
394 self.stream.close()
395 self.stream = self._open()
396 if stat is None:
397 stat = os.stat(self.baseFilename)
398 self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
399 logging.FileHandler.emit(self, record)
400
Guido van Rossum57102f82002-11-13 16:15:58 +0000401class SocketHandler(logging.Handler):
402 """
403 A handler class which writes logging records, in pickle format, to
404 a streaming socket. The socket is kept open across logging calls.
405 If the peer resets it, an attempt is made to reconnect on the next call.
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000406 The pickle which is sent is that of the LogRecord's attribute dictionary
407 (__dict__), so that the receiver does not need to have the logging module
408 installed in order to process the logging event.
409
410 To unpickle the record at the receiving end into a LogRecord, use the
411 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000412 """
413
414 def __init__(self, host, port):
415 """
416 Initializes the handler with a specific host address and port.
417
Vinay Sajipde19e082011-04-30 21:52:26 +0100418 When the attribute *closeOnError* is set to True - if a socket error
419 occurs, the socket is silently closed and then reopened on the next
420 logging call.
Guido van Rossum57102f82002-11-13 16:15:58 +0000421 """
422 logging.Handler.__init__(self)
423 self.host = host
424 self.port = port
425 self.sock = None
Vinay Sajipde19e082011-04-30 21:52:26 +0100426 self.closeOnError = False
Vinay Sajip48cfe382004-02-20 13:17:27 +0000427 self.retryTime = None
428 #
429 # Exponential backoff parameters.
430 #
431 self.retryStart = 1.0
432 self.retryMax = 30.0
433 self.retryFactor = 2.0
Guido van Rossum57102f82002-11-13 16:15:58 +0000434
Guido van Rossumd8faa362007-04-27 19:54:29 +0000435 def makeSocket(self, timeout=1):
Guido van Rossum57102f82002-11-13 16:15:58 +0000436 """
437 A factory method which allows subclasses to define the precise
438 type of socket they want.
439 """
440 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000441 if hasattr(s, 'settimeout'):
442 s.settimeout(timeout)
Vinay Sajipb37cd392011-05-07 15:55:47 +0100443 try:
444 s.connect((self.host, self.port))
445 return s
446 except socket.error:
447 s.close()
448 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000449
Vinay Sajip48cfe382004-02-20 13:17:27 +0000450 def createSocket(self):
451 """
452 Try to create a socket, using an exponential backoff with
453 a max retry time. Thanks to Robert Olson for the original patch
454 (SF #815911) which has been slightly refactored.
455 """
456 now = time.time()
457 # Either retryTime is None, in which case this
458 # is the first time back after a disconnect, or
459 # we've waited long enough.
460 if self.retryTime is None:
Vinay Sajipde19e082011-04-30 21:52:26 +0100461 attempt = True
Vinay Sajip48cfe382004-02-20 13:17:27 +0000462 else:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000463 attempt = (now >= self.retryTime)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000464 if attempt:
465 try:
466 self.sock = self.makeSocket()
467 self.retryTime = None # next time, no delay before trying
Thomas Wouters902d6eb2007-01-09 23:18:33 +0000468 except socket.error:
Vinay Sajip48cfe382004-02-20 13:17:27 +0000469 #Creation failed, so set the retry time and return.
470 if self.retryTime is None:
471 self.retryPeriod = self.retryStart
472 else:
473 self.retryPeriod = self.retryPeriod * self.retryFactor
474 if self.retryPeriod > self.retryMax:
475 self.retryPeriod = self.retryMax
476 self.retryTime = now + self.retryPeriod
477
Guido van Rossum57102f82002-11-13 16:15:58 +0000478 def send(self, s):
479 """
480 Send a pickled string to the socket.
481
482 This function allows for partial sends which can happen when the
483 network is busy.
484 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000485 if self.sock is None:
486 self.createSocket()
487 #self.sock can be None either because we haven't reached the retry
488 #time yet, or because we have reached the retry time and retried,
489 #but are still unable to connect.
490 if self.sock:
491 try:
492 if hasattr(self.sock, "sendall"):
493 self.sock.sendall(s)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100494 else: #pragma: no cover
Vinay Sajip48cfe382004-02-20 13:17:27 +0000495 sentsofar = 0
496 left = len(s)
497 while left > 0:
498 sent = self.sock.send(s[sentsofar:])
499 sentsofar = sentsofar + sent
500 left = left - sent
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100501 except socket.error: #pragma: no cover
Vinay Sajip48cfe382004-02-20 13:17:27 +0000502 self.sock.close()
503 self.sock = None # so we can call createSocket next time
Guido van Rossum57102f82002-11-13 16:15:58 +0000504
505 def makePickle(self, record):
506 """
507 Pickles the record in binary format with a length prefix, and
508 returns it ready for transmission across the socket.
509 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000510 ei = record.exc_info
511 if ei:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000512 dummy = self.format(record) # just to get traceback text into record.exc_text
513 record.exc_info = None # to avoid Unpickleable error
Guido van Rossumba205d62006-08-17 08:57:26 +0000514 s = pickle.dumps(record.__dict__, 1)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000515 if ei:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000516 record.exc_info = ei # for next handler
Guido van Rossum57102f82002-11-13 16:15:58 +0000517 slen = struct.pack(">L", len(s))
518 return slen + s
519
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000520 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000521 """
522 Handle an error during logging.
523
524 An error has occurred during logging. Most likely cause -
525 connection lost. Close the socket so that we can retry on the
526 next event.
527 """
528 if self.closeOnError and self.sock:
529 self.sock.close()
530 self.sock = None #try to reconnect next time
531 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000532 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000533
534 def emit(self, record):
535 """
536 Emit a record.
537
538 Pickles the record and writes it to the socket in binary format.
539 If there is an error with the socket, silently drop the packet.
540 If there was a problem with the socket, re-establishes the
541 socket.
542 """
543 try:
544 s = self.makePickle(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000545 self.send(s)
Vinay Sajip985ef872011-04-26 19:34:04 +0100546 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip85c19092005-10-31 13:14:19 +0000547 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000548 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000549 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000550
551 def close(self):
552 """
553 Closes the socket.
554 """
555 if self.sock:
556 self.sock.close()
557 self.sock = None
Vinay Sajip48cfe382004-02-20 13:17:27 +0000558 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000559
560class DatagramHandler(SocketHandler):
561 """
562 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000563 a datagram socket. The pickle which is sent is that of the LogRecord's
564 attribute dictionary (__dict__), so that the receiver does not need to
565 have the logging module installed in order to process the logging event.
566
567 To unpickle the record at the receiving end into a LogRecord, use the
568 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000569
570 """
571 def __init__(self, host, port):
572 """
573 Initializes the handler with a specific host address and port.
574 """
575 SocketHandler.__init__(self, host, port)
Vinay Sajipde19e082011-04-30 21:52:26 +0100576 self.closeOnError = False
Guido van Rossum57102f82002-11-13 16:15:58 +0000577
578 def makeSocket(self):
579 """
580 The factory method of SocketHandler is here overridden to create
581 a UDP socket (SOCK_DGRAM).
582 """
583 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
584 return s
585
586 def send(self, s):
587 """
588 Send a pickled string to a socket.
589
590 This function no longer allows for partial sends which can happen
591 when the network is busy - UDP does not guarantee delivery and
592 can deliver packets out of sequence.
593 """
Vinay Sajipfb154172004-08-24 09:36:23 +0000594 if self.sock is None:
595 self.createSocket()
Guido van Rossum57102f82002-11-13 16:15:58 +0000596 self.sock.sendto(s, (self.host, self.port))
597
598class SysLogHandler(logging.Handler):
599 """
600 A handler class which sends formatted logging records to a syslog
601 server. Based on Sam Rushing's syslog module:
602 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
603 Contributed by Nicolas Untz (after which minor refactoring changes
604 have been made).
605 """
606
607 # from <linux/sys/syslog.h>:
608 # ======================================================================
609 # priorities/facilities are encoded into a single 32-bit quantity, where
610 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
611 # facility (0-big number). Both the priorities and the facilities map
612 # roughly one-to-one to strings in the syslogd(8) source code. This
613 # mapping is included in this file.
614 #
615 # priorities (these are ordered)
616
617 LOG_EMERG = 0 # system is unusable
618 LOG_ALERT = 1 # action must be taken immediately
619 LOG_CRIT = 2 # critical conditions
620 LOG_ERR = 3 # error conditions
621 LOG_WARNING = 4 # warning conditions
622 LOG_NOTICE = 5 # normal but significant condition
623 LOG_INFO = 6 # informational
624 LOG_DEBUG = 7 # debug-level messages
625
626 # facility codes
627 LOG_KERN = 0 # kernel messages
628 LOG_USER = 1 # random user-level messages
629 LOG_MAIL = 2 # mail system
630 LOG_DAEMON = 3 # system daemons
631 LOG_AUTH = 4 # security/authorization messages
632 LOG_SYSLOG = 5 # messages generated internally by syslogd
633 LOG_LPR = 6 # line printer subsystem
634 LOG_NEWS = 7 # network news subsystem
635 LOG_UUCP = 8 # UUCP subsystem
636 LOG_CRON = 9 # clock daemon
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000637 LOG_AUTHPRIV = 10 # security/authorization messages (private)
638 LOG_FTP = 11 # FTP daemon
Guido van Rossum57102f82002-11-13 16:15:58 +0000639
640 # other codes through 15 reserved for system use
641 LOG_LOCAL0 = 16 # reserved for local use
642 LOG_LOCAL1 = 17 # reserved for local use
643 LOG_LOCAL2 = 18 # reserved for local use
644 LOG_LOCAL3 = 19 # reserved for local use
645 LOG_LOCAL4 = 20 # reserved for local use
646 LOG_LOCAL5 = 21 # reserved for local use
647 LOG_LOCAL6 = 22 # reserved for local use
648 LOG_LOCAL7 = 23 # reserved for local use
649
650 priority_names = {
651 "alert": LOG_ALERT,
652 "crit": LOG_CRIT,
653 "critical": LOG_CRIT,
654 "debug": LOG_DEBUG,
655 "emerg": LOG_EMERG,
656 "err": LOG_ERR,
657 "error": LOG_ERR, # DEPRECATED
658 "info": LOG_INFO,
659 "notice": LOG_NOTICE,
660 "panic": LOG_EMERG, # DEPRECATED
661 "warn": LOG_WARNING, # DEPRECATED
662 "warning": LOG_WARNING,
663 }
664
665 facility_names = {
666 "auth": LOG_AUTH,
667 "authpriv": LOG_AUTHPRIV,
668 "cron": LOG_CRON,
669 "daemon": LOG_DAEMON,
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000670 "ftp": LOG_FTP,
Guido van Rossum57102f82002-11-13 16:15:58 +0000671 "kern": LOG_KERN,
672 "lpr": LOG_LPR,
673 "mail": LOG_MAIL,
674 "news": LOG_NEWS,
675 "security": LOG_AUTH, # DEPRECATED
676 "syslog": LOG_SYSLOG,
677 "user": LOG_USER,
678 "uucp": LOG_UUCP,
679 "local0": LOG_LOCAL0,
680 "local1": LOG_LOCAL1,
681 "local2": LOG_LOCAL2,
682 "local3": LOG_LOCAL3,
683 "local4": LOG_LOCAL4,
684 "local5": LOG_LOCAL5,
685 "local6": LOG_LOCAL6,
686 "local7": LOG_LOCAL7,
687 }
688
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000689 #The map below appears to be trivially lowercasing the key. However,
690 #there's more to it than meets the eye - in some locales, lowercasing
691 #gives unexpected results. See SF #1524081: in the Turkish locale,
692 #"INFO".lower() != "info"
693 priority_map = {
694 "DEBUG" : "debug",
695 "INFO" : "info",
696 "WARNING" : "warning",
697 "ERROR" : "error",
698 "CRITICAL" : "critical"
699 }
700
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000701 def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
702 facility=LOG_USER, socktype=socket.SOCK_DGRAM):
Guido van Rossum57102f82002-11-13 16:15:58 +0000703 """
704 Initialize a handler.
705
Guido van Rossume7ba4952007-06-06 23:52:48 +0000706 If address is specified as a string, a UNIX socket is used. To log to a
707 local syslogd, "SysLogHandler(address="/dev/log")" can be used.
Guido van Rossum57102f82002-11-13 16:15:58 +0000708 If facility is not specified, LOG_USER is used.
709 """
710 logging.Handler.__init__(self)
711
712 self.address = address
713 self.facility = facility
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000714 self.socktype = socktype
715
Guido van Rossum13257902007-06-07 23:15:56 +0000716 if isinstance(address, str):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100717 self.unixsocket = True
Thomas Wouters89f507f2006-12-13 04:49:30 +0000718 self._connect_unixsocket(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000719 else:
Vinay Sajip5a35b062011-04-27 11:31:14 +0100720 self.unixsocket = False
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000721 self.socket = socket.socket(socket.AF_INET, socktype)
722 if socktype == socket.SOCK_STREAM:
723 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000724 self.formatter = None
725
Vinay Sajipa1974c12005-01-13 08:23:56 +0000726 def _connect_unixsocket(self, address):
727 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
728 # syslog may require either DGRAM or STREAM sockets
729 try:
730 self.socket.connect(address)
731 except socket.error:
732 self.socket.close()
733 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Vinay Sajip8b6b53f2005-11-09 13:55:13 +0000734 self.socket.connect(address)
Vinay Sajipa1974c12005-01-13 08:23:56 +0000735
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000736 def encodePriority(self, facility, priority):
Guido van Rossum57102f82002-11-13 16:15:58 +0000737 """
738 Encode the facility and priority. You can pass in strings or
739 integers - if strings are passed, the facility_names and
740 priority_names mapping dictionaries are used to convert them to
741 integers.
742 """
Guido van Rossum13257902007-06-07 23:15:56 +0000743 if isinstance(facility, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000744 facility = self.facility_names[facility]
Guido van Rossum13257902007-06-07 23:15:56 +0000745 if isinstance(priority, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000746 priority = self.priority_names[priority]
747 return (facility << 3) | priority
748
749 def close (self):
750 """
751 Closes the socket.
752 """
Vinay Sajipfd285022011-05-07 17:01:22 +0100753 self.socket.close()
Vinay Sajip48cfe382004-02-20 13:17:27 +0000754 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000755
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000756 def mapPriority(self, levelName):
757 """
758 Map a logging level name to a key in the priority_names map.
759 This is useful in two scenarios: when custom levels are being
760 used, and in the case where you can't do a straightforward
761 mapping by lowercasing the logging level name because of locale-
762 specific issues (see SF #1524081).
763 """
764 return self.priority_map.get(levelName, "warning")
765
Vinay Sajip2353e352011-06-27 15:40:06 +0100766 ident = '' # prepended to all messages
Vinay Sajip8168d102011-06-09 16:50:49 +0100767 append_nul = True # some old syslog daemons expect a NUL terminator
768
Guido van Rossum57102f82002-11-13 16:15:58 +0000769 def emit(self, record):
770 """
771 Emit a record.
772
773 The record is formatted, and then sent to the syslog server. If
774 exception information is present, it is NOT sent to the server.
775 """
Vinay Sajip8168d102011-06-09 16:50:49 +0100776 msg = self.format(record)
Vinay Sajip2353e352011-06-27 15:40:06 +0100777 if self.ident:
778 msg = self.ident + msg
Vinay Sajip8168d102011-06-09 16:50:49 +0100779 if self.append_nul:
780 msg += '\000'
Guido van Rossum57102f82002-11-13 16:15:58 +0000781 """
782 We need to convert record level to lowercase, maybe this will
783 change in the future.
784 """
Vinay Sajip467d12f2010-08-19 22:20:22 +0000785 prio = '<%d>' % self.encodePriority(self.facility,
786 self.mapPriority(record.levelname))
787 prio = prio.encode('utf-8')
Vinay Sajip609364a2010-08-30 18:31:13 +0000788 # Message is a string. Convert to bytes as required by RFC 5424
Vinay Sajip42ead482009-10-21 20:22:14 +0000789 msg = msg.encode('utf-8')
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100790 msg = prio + BOM_UTF8 + msg
Guido van Rossum57102f82002-11-13 16:15:58 +0000791 try:
792 if self.unixsocket:
Vinay Sajipa1974c12005-01-13 08:23:56 +0000793 try:
794 self.socket.send(msg)
795 except socket.error:
796 self._connect_unixsocket(self.address)
797 self.socket.send(msg)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000798 elif self.socktype == socket.SOCK_DGRAM:
Guido van Rossum57102f82002-11-13 16:15:58 +0000799 self.socket.sendto(msg, self.address)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000800 else:
801 self.socket.sendall(msg)
Vinay Sajip985ef872011-04-26 19:34:04 +0100802 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip85c19092005-10-31 13:14:19 +0000803 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000804 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000805 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000806
807class SMTPHandler(logging.Handler):
808 """
809 A handler class which sends an SMTP email for each logging event.
810 """
Vinay Sajip540f2152009-12-06 17:57:11 +0000811 def __init__(self, mailhost, fromaddr, toaddrs, subject,
Vinay Sajip25fcd222009-12-06 18:05:04 +0000812 credentials=None, secure=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000813 """
814 Initialize the handler.
815
816 Initialize the instance with the from and to addresses and subject
817 line of the email. To specify a non-standard SMTP port, use the
Guido van Rossum360e4b82007-05-14 22:51:27 +0000818 (host, port) tuple format for the mailhost argument. To specify
819 authentication credentials, supply a (username, password) tuple
Vinay Sajip25fcd222009-12-06 18:05:04 +0000820 for the credentials argument. To specify the use of a secure
821 protocol (TLS), pass in a tuple for the secure argument. This will
822 only be used when authentication credentials are supplied. The tuple
823 will be either an empty tuple, or a single-value tuple with the name
824 of a keyfile, or a 2-value tuple with the names of the keyfile and
825 certificate file. (This tuple is passed to the `starttls` method).
Guido van Rossum57102f82002-11-13 16:15:58 +0000826 """
827 logging.Handler.__init__(self)
Guido van Rossum13257902007-06-07 23:15:56 +0000828 if isinstance(mailhost, tuple):
Guido van Rossum360e4b82007-05-14 22:51:27 +0000829 self.mailhost, self.mailport = mailhost
Guido van Rossum57102f82002-11-13 16:15:58 +0000830 else:
Guido van Rossum360e4b82007-05-14 22:51:27 +0000831 self.mailhost, self.mailport = mailhost, None
Guido van Rossum13257902007-06-07 23:15:56 +0000832 if isinstance(credentials, tuple):
Guido van Rossum360e4b82007-05-14 22:51:27 +0000833 self.username, self.password = credentials
834 else:
835 self.username = None
Guido van Rossum57102f82002-11-13 16:15:58 +0000836 self.fromaddr = fromaddr
Guido van Rossum13257902007-06-07 23:15:56 +0000837 if isinstance(toaddrs, str):
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000838 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000839 self.toaddrs = toaddrs
840 self.subject = subject
Vinay Sajip540f2152009-12-06 17:57:11 +0000841 self.secure = secure
Guido van Rossum57102f82002-11-13 16:15:58 +0000842
843 def getSubject(self, record):
844 """
845 Determine the subject for the email.
846
847 If you want to specify a subject line which is record-dependent,
848 override this method.
849 """
850 return self.subject
851
852 def emit(self, record):
853 """
854 Emit a record.
855
856 Format the record and send it to the specified addressees.
857 """
858 try:
859 import smtplib
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000860 from email.utils import formatdate
Guido van Rossum57102f82002-11-13 16:15:58 +0000861 port = self.mailport
862 if not port:
863 port = smtplib.SMTP_PORT
864 smtp = smtplib.SMTP(self.mailhost, port)
865 msg = self.format(record)
Neal Norwitzf297bd12003-04-23 03:49:43 +0000866 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 +0000867 self.fromaddr,
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000868 ",".join(self.toaddrs),
Neal Norwitzf297bd12003-04-23 03:49:43 +0000869 self.getSubject(record),
Martin v. Löwis318a12e2004-08-18 12:27:40 +0000870 formatdate(), msg)
Guido van Rossum360e4b82007-05-14 22:51:27 +0000871 if self.username:
Vinay Sajip25fcd222009-12-06 18:05:04 +0000872 if self.secure is not None:
Vinay Sajip540f2152009-12-06 17:57:11 +0000873 smtp.ehlo()
Vinay Sajip25fcd222009-12-06 18:05:04 +0000874 smtp.starttls(*self.secure)
Vinay Sajip540f2152009-12-06 17:57:11 +0000875 smtp.ehlo()
Guido van Rossum360e4b82007-05-14 22:51:27 +0000876 smtp.login(self.username, self.password)
Guido van Rossum57102f82002-11-13 16:15:58 +0000877 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
878 smtp.quit()
Vinay Sajip985ef872011-04-26 19:34:04 +0100879 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip245a5ab2005-10-31 14:27:01 +0000880 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000881 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000882 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000883
884class NTEventLogHandler(logging.Handler):
885 """
886 A handler class which sends events to the NT Event Log. Adds a
887 registry entry for the specified application name. If no dllname is
888 provided, win32service.pyd (which contains some basic message
889 placeholders) is used. Note that use of these placeholders will make
890 your event logs big, as the entire message source is held in the log.
891 If you want slimmer logs, you have to pass in the name of your own DLL
892 which contains the message definitions you want to use in the event log.
893 """
894 def __init__(self, appname, dllname=None, logtype="Application"):
895 logging.Handler.__init__(self)
896 try:
897 import win32evtlogutil, win32evtlog
898 self.appname = appname
899 self._welu = win32evtlogutil
900 if not dllname:
901 dllname = os.path.split(self._welu.__file__)
902 dllname = os.path.split(dllname[0])
903 dllname = os.path.join(dllname[0], r'win32service.pyd')
904 self.dllname = dllname
905 self.logtype = logtype
906 self._welu.AddSourceToRegistry(appname, dllname, logtype)
907 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
908 self.typemap = {
909 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
910 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000911 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000912 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
913 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
914 }
915 except ImportError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000916 print("The Python Win32 extensions for NT (service, event "\
917 "logging) appear not to be available.")
Guido van Rossum57102f82002-11-13 16:15:58 +0000918 self._welu = None
919
920 def getMessageID(self, record):
921 """
922 Return the message ID for the event record. If you are using your
923 own messages, you could do this by having the msg passed to the
924 logger being an ID rather than a formatting string. Then, in here,
925 you could use a dictionary lookup to get the message ID. This
926 version returns 1, which is the base message ID in win32service.pyd.
927 """
928 return 1
929
930 def getEventCategory(self, record):
931 """
932 Return the event category for the record.
933
934 Override this if you want to specify your own categories. This version
935 returns 0.
936 """
937 return 0
938
939 def getEventType(self, record):
940 """
941 Return the event type for the record.
942
943 Override this if you want to specify your own types. This version does
944 a mapping using the handler's typemap attribute, which is set up in
945 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000946 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000947 either need to override this method or place a suitable dictionary in
948 the handler's typemap attribute.
949 """
950 return self.typemap.get(record.levelno, self.deftype)
951
952 def emit(self, record):
953 """
954 Emit a record.
955
956 Determine the message ID, event category and event type. Then
957 log the message in the NT event log.
958 """
959 if self._welu:
960 try:
961 id = self.getMessageID(record)
962 cat = self.getEventCategory(record)
963 type = self.getEventType(record)
964 msg = self.format(record)
965 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
Vinay Sajip985ef872011-04-26 19:34:04 +0100966 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip245a5ab2005-10-31 14:27:01 +0000967 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000968 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000969 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000970
971 def close(self):
972 """
973 Clean up this handler.
974
975 You can remove the application name from the registry as a
976 source of event log entries. However, if you do this, you will
977 not be able to see the events as you intended in the Event Log
978 Viewer - it needs to be able to access the registry to get the
979 DLL name.
980 """
981 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000982 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000983
984class HTTPHandler(logging.Handler):
985 """
986 A class which sends records to a Web server, using either GET or
987 POST semantics.
988 """
Vinay Sajipaf9d10a2010-09-13 20:40:30 +0000989 def __init__(self, host, url, method="GET", secure=False, credentials=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000990 """
991 Initialize the instance with the host, the request URL, and the method
992 ("GET" or "POST")
993 """
994 logging.Handler.__init__(self)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000995 method = method.upper()
Guido van Rossum57102f82002-11-13 16:15:58 +0000996 if method not in ["GET", "POST"]:
Collin Winterce36ad82007-08-30 01:19:48 +0000997 raise ValueError("method must be GET or POST")
Guido van Rossum57102f82002-11-13 16:15:58 +0000998 self.host = host
999 self.url = url
1000 self.method = method
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001001 self.secure = secure
1002 self.credentials = credentials
Guido van Rossum57102f82002-11-13 16:15:58 +00001003
Neal Norwitzf297bd12003-04-23 03:49:43 +00001004 def mapLogRecord(self, record):
1005 """
1006 Default implementation of mapping the log record into a dict
Vinay Sajip48cfe382004-02-20 13:17:27 +00001007 that is sent as the CGI data. Overwrite in your class.
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001008 Contributed by Franz Glasner.
Neal Norwitzf297bd12003-04-23 03:49:43 +00001009 """
1010 return record.__dict__
1011
Guido van Rossum57102f82002-11-13 16:15:58 +00001012 def emit(self, record):
1013 """
1014 Emit a record.
1015
Senthil Kumaran30e86a42010-08-09 20:01:35 +00001016 Send the record to the Web server as a percent-encoded dictionary
Guido van Rossum57102f82002-11-13 16:15:58 +00001017 """
1018 try:
Georg Brandl029986a2008-06-23 11:44:14 +00001019 import http.client, urllib.parse
Vinay Sajipb7935062005-10-11 13:15:31 +00001020 host = self.host
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001021 if self.secure:
1022 h = http.client.HTTPSConnection(host)
1023 else:
1024 h = http.client.HTTPConnection(host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001025 url = self.url
Georg Brandl029986a2008-06-23 11:44:14 +00001026 data = urllib.parse.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +00001027 if self.method == "GET":
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001028 if (url.find('?') >= 0):
Guido van Rossum57102f82002-11-13 16:15:58 +00001029 sep = '&'
1030 else:
1031 sep = '?'
1032 url = url + "%c%s" % (sep, data)
1033 h.putrequest(self.method, url)
Vinay Sajipb7935062005-10-11 13:15:31 +00001034 # support multiple hosts on one IP address...
1035 # need to strip optional :port from host, if present
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001036 i = host.find(":")
Vinay Sajipb7935062005-10-11 13:15:31 +00001037 if i >= 0:
1038 host = host[:i]
1039 h.putheader("Host", host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001040 if self.method == "POST":
Vinay Sajipb7935062005-10-11 13:15:31 +00001041 h.putheader("Content-type",
1042 "application/x-www-form-urlencoded")
Guido van Rossum57102f82002-11-13 16:15:58 +00001043 h.putheader("Content-length", str(len(data)))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001044 if self.credentials:
1045 import base64
1046 s = ('u%s:%s' % self.credentials).encode('utf-8')
1047 s = 'Basic ' + base64.b64encode(s).strip()
1048 h.putheader('Authorization', s)
Vinay Sajip0372e102011-05-05 12:59:14 +01001049 h.endheaders()
1050 if self.method == "POST":
1051 h.send(data.encode('utf-8'))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001052 h.getresponse() #can't do anything with the result
Vinay Sajip985ef872011-04-26 19:34:04 +01001053 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip245a5ab2005-10-31 14:27:01 +00001054 raise
Guido van Rossum57102f82002-11-13 16:15:58 +00001055 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001056 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001057
1058class BufferingHandler(logging.Handler):
1059 """
1060 A handler class which buffers logging records in memory. Whenever each
1061 record is added to the buffer, a check is made to see if the buffer should
1062 be flushed. If it should, then flush() is expected to do what's needed.
1063 """
1064 def __init__(self, capacity):
1065 """
1066 Initialize the handler with the buffer size.
1067 """
1068 logging.Handler.__init__(self)
1069 self.capacity = capacity
1070 self.buffer = []
1071
1072 def shouldFlush(self, record):
1073 """
1074 Should the handler flush its buffer?
1075
1076 Returns true if the buffer is up to capacity. This method can be
1077 overridden to implement custom flushing strategies.
1078 """
1079 return (len(self.buffer) >= self.capacity)
1080
1081 def emit(self, record):
1082 """
1083 Emit a record.
1084
1085 Append the record. If shouldFlush() tells us to, call flush() to process
1086 the buffer.
1087 """
1088 self.buffer.append(record)
1089 if self.shouldFlush(record):
1090 self.flush()
1091
1092 def flush(self):
1093 """
1094 Override to implement custom flushing behaviour.
1095
1096 This version just zaps the buffer to empty.
1097 """
1098 self.buffer = []
1099
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001100 def close(self):
1101 """
1102 Close the handler.
1103
1104 This version just flushes and chains to the parent class' close().
1105 """
1106 self.flush()
1107 logging.Handler.close(self)
1108
Guido van Rossum57102f82002-11-13 16:15:58 +00001109class MemoryHandler(BufferingHandler):
1110 """
1111 A handler class which buffers logging records in memory, periodically
1112 flushing them to a target handler. Flushing occurs whenever the buffer
1113 is full, or when an event of a certain severity or greater is seen.
1114 """
1115 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
1116 """
1117 Initialize the handler with the buffer size, the level at which
1118 flushing should occur and an optional target.
1119
1120 Note that without a target being set either here or via setTarget(),
1121 a MemoryHandler is no use to anyone!
1122 """
1123 BufferingHandler.__init__(self, capacity)
1124 self.flushLevel = flushLevel
1125 self.target = target
1126
1127 def shouldFlush(self, record):
1128 """
1129 Check for buffer full or a record at the flushLevel or higher.
1130 """
1131 return (len(self.buffer) >= self.capacity) or \
1132 (record.levelno >= self.flushLevel)
1133
1134 def setTarget(self, target):
1135 """
1136 Set the target handler for this handler.
1137 """
1138 self.target = target
1139
1140 def flush(self):
1141 """
1142 For a MemoryHandler, flushing means just sending the buffered
1143 records to the target, if there is one. Override if you want
1144 different behaviour.
Vinay Sajipc84f0162010-09-21 11:25:39 +00001145
1146 The record buffer is also cleared by this operation.
Guido van Rossum57102f82002-11-13 16:15:58 +00001147 """
1148 if self.target:
1149 for record in self.buffer:
1150 self.target.handle(record)
1151 self.buffer = []
1152
1153 def close(self):
1154 """
1155 Flush, set the target to None and lose the buffer.
1156 """
1157 self.flush()
1158 self.target = None
Vinay Sajip48cfe382004-02-20 13:17:27 +00001159 BufferingHandler.close(self)
Vinay Sajip121a1c42010-09-08 10:46:15 +00001160
1161
1162class QueueHandler(logging.Handler):
1163 """
1164 This handler sends events to a queue. Typically, it would be used together
1165 with a multiprocessing Queue to centralise logging to file in one process
1166 (in a multi-process application), so as to avoid file write contention
1167 between processes.
1168
1169 This code is new in Python 3.2, but this class can be copy pasted into
1170 user code for use with earlier Python versions.
1171 """
1172
1173 def __init__(self, queue):
1174 """
1175 Initialise an instance, using the passed queue.
1176 """
1177 logging.Handler.__init__(self)
1178 self.queue = queue
1179
1180 def enqueue(self, record):
1181 """
1182 Enqueue a record.
1183
1184 The base implementation uses put_nowait. You may want to override
1185 this method if you want to use blocking, timeouts or custom queue
1186 implementations.
1187 """
1188 self.queue.put_nowait(record)
1189
Vinay Sajip0258ce82010-09-22 20:34:53 +00001190 def prepare(self, record):
1191 """
Vinay Sajip0637d492010-09-23 08:15:54 +00001192 Prepares a record for queuing. The object returned by this method is
1193 enqueued.
Vinay Sajip0258ce82010-09-22 20:34:53 +00001194
1195 The base implementation formats the record to merge the message
1196 and arguments, and removes unpickleable items from the record
1197 in-place.
1198
1199 You might want to override this method if you want to convert
1200 the record to a dict or JSON string, or send a modified copy
1201 of the record while leaving the original intact.
1202 """
1203 # The format operation gets traceback text into record.exc_text
1204 # (if there's exception data), and also puts the message into
1205 # record.message. We can then use this to replace the original
1206 # msg + args, as these might be unpickleable. We also zap the
1207 # exc_info attribute, as it's no longer needed and, if not None,
1208 # will typically not be pickleable.
1209 self.format(record)
1210 record.msg = record.message
1211 record.args = None
1212 record.exc_info = None
1213 return record
1214
Vinay Sajip121a1c42010-09-08 10:46:15 +00001215 def emit(self, record):
1216 """
1217 Emit a record.
1218
Vinay Sajip0637d492010-09-23 08:15:54 +00001219 Writes the LogRecord to the queue, preparing it for pickling first.
Vinay Sajip121a1c42010-09-08 10:46:15 +00001220 """
1221 try:
Vinay Sajip0258ce82010-09-22 20:34:53 +00001222 self.enqueue(self.prepare(record))
Vinay Sajip985ef872011-04-26 19:34:04 +01001223 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip121a1c42010-09-08 10:46:15 +00001224 raise
1225 except:
1226 self.handleError(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001227
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001228if threading:
1229 class QueueListener(object):
1230 """
1231 This class implements an internal threaded listener which watches for
1232 LogRecords being added to a queue, removes them and passes them to a
1233 list of handlers for processing.
1234 """
1235 _sentinel = None
Vinay Sajip0637d492010-09-23 08:15:54 +00001236
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001237 def __init__(self, queue, *handlers):
1238 """
1239 Initialise an instance with the specified queue and
1240 handlers.
1241 """
1242 self.queue = queue
1243 self.handlers = handlers
1244 self._stop = threading.Event()
1245 self._thread = None
Vinay Sajip0637d492010-09-23 08:15:54 +00001246
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001247 def dequeue(self, block):
1248 """
1249 Dequeue a record and return it, optionally blocking.
Vinay Sajip0637d492010-09-23 08:15:54 +00001250
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001251 The base implementation uses get. You may want to override this method
1252 if you want to use timeouts or work with custom queue implementations.
1253 """
1254 return self.queue.get(block)
Vinay Sajip0637d492010-09-23 08:15:54 +00001255
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001256 def start(self):
1257 """
1258 Start the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001259
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001260 This starts up a background thread to monitor the queue for
1261 LogRecords to process.
1262 """
1263 self._thread = t = threading.Thread(target=self._monitor)
1264 t.setDaemon(True)
1265 t.start()
Vinay Sajip0637d492010-09-23 08:15:54 +00001266
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001267 def prepare(self , record):
1268 """
1269 Prepare a record for handling.
Vinay Sajip0637d492010-09-23 08:15:54 +00001270
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001271 This method just returns the passed-in record. You may want to
1272 override this method if you need to do any custom marshalling or
1273 manipulation of the record before passing it to the handlers.
1274 """
1275 return record
Vinay Sajip0637d492010-09-23 08:15:54 +00001276
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001277 def handle(self, record):
1278 """
1279 Handle a record.
Vinay Sajip0637d492010-09-23 08:15:54 +00001280
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001281 This just loops through the handlers offering them the record
1282 to handle.
1283 """
1284 record = self.prepare(record)
1285 for handler in self.handlers:
1286 handler.handle(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001287
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001288 def _monitor(self):
1289 """
1290 Monitor the queue for records, and ask the handler
1291 to deal with them.
Vinay Sajip0637d492010-09-23 08:15:54 +00001292
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001293 This method runs on a separate, internal thread.
1294 The thread will terminate if it sees a sentinel object in the queue.
1295 """
1296 q = self.queue
1297 has_task_done = hasattr(q, 'task_done')
1298 while not self._stop.isSet():
1299 try:
1300 record = self.dequeue(True)
1301 if record is self._sentinel:
1302 break
1303 self.handle(record)
1304 if has_task_done:
1305 q.task_done()
1306 except queue.Empty:
1307 pass
1308 # There might still be records in the queue.
1309 while True:
1310 try:
1311 record = self.dequeue(False)
1312 if record is self._sentinel:
1313 break
1314 self.handle(record)
1315 if has_task_done:
1316 q.task_done()
1317 except queue.Empty:
Vinay Sajip0637d492010-09-23 08:15:54 +00001318 break
Vinay Sajip0637d492010-09-23 08:15:54 +00001319
Victor Stinner59bec362011-05-02 16:14:16 +02001320 def enqueue_sentinel(self):
1321 """
1322 This is used to enqueue the sentinel record.
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001323
Victor Stinner59bec362011-05-02 16:14:16 +02001324 The base implementation uses put_nowait. You may want to override this
1325 method if you want to use timeouts or work with custom queue
1326 implementations.
1327 """
1328 self.queue.put_nowait(self._sentinel)
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001329
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001330 def stop(self):
1331 """
1332 Stop the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001333
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001334 This asks the thread to terminate, and then waits for it to do so.
1335 Note that if you don't call this before your application exits, there
1336 may be some records still left on the queue, which won't be processed.
1337 """
1338 self._stop.set()
Victor Stinner59bec362011-05-02 16:14:16 +02001339 self.enqueue_sentinel()
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001340 self._thread.join()
1341 self._thread = None