blob: 822b4efc7c4f7c7395e3c6fa2f22dfc52c797d01 [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 """
Vinay Sajip23b94d02012-01-04 12:02:26 +000055 def __init__(self, filename, mode, encoding=None, delay=False):
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
Vinay Sajip23b94d02012-01-04 12:02:26 +000062 self.namer = None
63 self.rotator = None
Guido van Rossum57102f82002-11-13 16:15:58 +000064
Vinay Sajip17c52d82004-07-03 11:48:34 +000065 def emit(self, record):
66 """
67 Emit a record.
68
69 Output the record to the file, catering for rollover as described
70 in doRollover().
71 """
Vinay Sajip3970c112004-07-08 10:24:04 +000072 try:
73 if self.shouldRollover(record):
74 self.doRollover()
75 logging.FileHandler.emit(self, record)
Vinay Sajip985ef872011-04-26 19:34:04 +010076 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip85c19092005-10-31 13:14:19 +000077 raise
Vinay Sajip3970c112004-07-08 10:24:04 +000078 except:
79 self.handleError(record)
Vinay Sajip17c52d82004-07-03 11:48:34 +000080
Vinay Sajip23b94d02012-01-04 12:02:26 +000081 def rotation_filename(self, default_name):
82 """
83 Modify the filename of a log file when rotating.
84
85 This is provided so that a custom filename can be provided.
86
87 The default implementation calls the 'namer' attribute of the
88 handler, if it's callable, passing the default name to
89 it. If the attribute isn't callable (the default is None), the name
90 is returned unchanged.
91
92 :param default_name: The default name for the log file.
93 """
94 if not callable(self.namer):
95 result = default_name
96 else:
97 result = self.namer(default_name)
98 return result
99
100 def rotate(self, source, dest):
101 """
102 When rotating, rotate the current log.
103
104 The default implementation calls the 'rotator' attribute of the
105 handler, if it's callable, passing the source and dest arguments to
106 it. If the attribute isn't callable (the default is None), the source
107 is simply renamed to the destination.
108
109 :param source: The source filename. This is normally the base
110 filename, e.g. 'test.log'
111 :param dest: The destination filename. This is normally
112 what the source is rotated to, e.g. 'test.log.1'.
113 """
114 if not callable(self.rotator):
115 os.rename(source, dest)
116 else:
117 self.rotator(source, dest)
118
Vinay Sajip17c52d82004-07-03 11:48:34 +0000119class RotatingFileHandler(BaseRotatingHandler):
120 """
121 Handler for logging to a set of files, which switches from one file
122 to the next when the current file reaches a certain size.
123 """
Vinay Sajip23b94d02012-01-04 12:02:26 +0000124 def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False):
Guido van Rossum57102f82002-11-13 16:15:58 +0000125 """
126 Open the specified file and use it as the stream for logging.
127
128 By default, the file grows indefinitely. You can specify particular
129 values of maxBytes and backupCount to allow the file to rollover at
130 a predetermined size.
131
132 Rollover occurs whenever the current log file is nearly maxBytes in
133 length. If backupCount is >= 1, the system will successively create
134 new files with the same pathname as the base file, but with extensions
135 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
136 and a base file name of "app.log", you would get "app.log",
137 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
138 written to is always "app.log" - when it gets filled up, it is closed
139 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
140 exist, then they are renamed to "app.log.2", "app.log.3" etc.
141 respectively.
142
143 If maxBytes is zero, rollover never occurs.
144 """
Vinay Sajip89994b22010-08-22 18:11:02 +0000145 # If rotation/rollover is wanted, it doesn't make sense to use another
146 # mode. If for example 'w' were specified, then if there were multiple
147 # runs of the calling application, the logs from previous runs would be
148 # lost if the 'w' is respected, because the log file would be truncated
149 # on each run.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000150 if maxBytes > 0:
Vinay Sajip89994b22010-08-22 18:11:02 +0000151 mode = 'a'
Christian Heimese7a15bb2008-01-24 16:21:45 +0000152 BaseRotatingHandler.__init__(self, filename, mode, encoding, delay)
Guido van Rossum57102f82002-11-13 16:15:58 +0000153 self.maxBytes = maxBytes
154 self.backupCount = backupCount
Guido van Rossum57102f82002-11-13 16:15:58 +0000155
156 def doRollover(self):
157 """
158 Do a rollover, as described in __init__().
159 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000160 if self.stream:
161 self.stream.close()
Vinay Sajip01241d62011-01-21 23:35:57 +0000162 self.stream = None
Guido van Rossum57102f82002-11-13 16:15:58 +0000163 if self.backupCount > 0:
164 for i in range(self.backupCount - 1, 0, -1):
Vinay Sajip23b94d02012-01-04 12:02:26 +0000165 sfn = self.rotation_filename("%s.%d" % (self.baseFilename, i))
166 dfn = self.rotation_filename("%s.%d" % (self.baseFilename,
167 i + 1))
Guido van Rossum57102f82002-11-13 16:15:58 +0000168 if os.path.exists(sfn):
Guido van Rossum57102f82002-11-13 16:15:58 +0000169 if os.path.exists(dfn):
170 os.remove(dfn)
171 os.rename(sfn, dfn)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000172 dfn = self.rotation_filename(self.baseFilename + ".1")
Guido van Rossum57102f82002-11-13 16:15:58 +0000173 if os.path.exists(dfn):
174 os.remove(dfn)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000175 self.rotate(self.baseFilename, dfn)
Thomas Woutersb2137042007-02-01 18:02:27 +0000176 self.mode = 'w'
177 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000178
Vinay Sajip17c52d82004-07-03 11:48:34 +0000179 def shouldRollover(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000180 """
Vinay Sajip17c52d82004-07-03 11:48:34 +0000181 Determine if rollover should occur.
Guido van Rossum57102f82002-11-13 16:15:58 +0000182
Vinay Sajip17c52d82004-07-03 11:48:34 +0000183 Basically, see if the supplied record would cause the file to exceed
184 the size limit we have.
Guido van Rossum57102f82002-11-13 16:15:58 +0000185 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000186 if self.stream is None: # delay was set...
187 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000188 if self.maxBytes > 0: # are we rolling over?
189 msg = "%s\n" % self.format(record)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000190 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
Guido van Rossum57102f82002-11-13 16:15:58 +0000191 if self.stream.tell() + len(msg) >= self.maxBytes:
Vinay Sajip17c52d82004-07-03 11:48:34 +0000192 return 1
193 return 0
Guido van Rossum57102f82002-11-13 16:15:58 +0000194
Vinay Sajip17c52d82004-07-03 11:48:34 +0000195class TimedRotatingFileHandler(BaseRotatingHandler):
196 """
197 Handler for logging to a file, rotating the log file at certain timed
198 intervals.
199
200 If backupCount is > 0, when rollover is done, no more than backupCount
201 files are kept - the oldest ones are deleted.
202 """
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000203 def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False):
Christian Heimese7a15bb2008-01-24 16:21:45 +0000204 BaseRotatingHandler.__init__(self, filename, 'a', encoding, delay)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000205 self.when = when.upper()
Vinay Sajip17c52d82004-07-03 11:48:34 +0000206 self.backupCount = backupCount
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000207 self.utc = utc
Vinay Sajip17c52d82004-07-03 11:48:34 +0000208 # Calculate the real rollover interval, which is just the number of
209 # seconds between rollovers. Also set the filename suffix used when
210 # a rollover occurs. Current 'when' events supported:
211 # S - Seconds
212 # M - Minutes
213 # H - Hours
214 # D - Days
215 # midnight - roll over at midnight
216 # W{0-6} - roll over on a certain day; 0 - Monday
217 #
218 # Case of the 'when' specifier is not important; lower or upper case
219 # will work.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000220 if self.when == 'S':
221 self.interval = 1 # one second
222 self.suffix = "%Y-%m-%d_%H-%M-%S"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000223 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000224 elif self.when == 'M':
225 self.interval = 60 # one minute
226 self.suffix = "%Y-%m-%d_%H-%M"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000227 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000228 elif self.when == 'H':
229 self.interval = 60 * 60 # one hour
230 self.suffix = "%Y-%m-%d_%H"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000231 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000232 elif self.when == 'D' or self.when == 'MIDNIGHT':
233 self.interval = 60 * 60 * 24 # one day
234 self.suffix = "%Y-%m-%d"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000235 self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000236 elif self.when.startswith('W'):
237 self.interval = 60 * 60 * 24 * 7 # one week
238 if len(self.when) != 2:
239 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
240 if self.when[1] < '0' or self.when[1] > '6':
241 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
242 self.dayOfWeek = int(self.when[1])
243 self.suffix = "%Y-%m-%d"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000244 self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000245 else:
246 raise ValueError("Invalid rollover interval specified: %s" % self.when)
247
Antoine Pitroufd036452008-08-19 17:56:33 +0000248 self.extMatch = re.compile(self.extMatch, re.ASCII)
Vinay Sajipe7d40662004-10-03 19:12:07 +0000249 self.interval = self.interval * interval # multiply by units requested
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000250 if os.path.exists(filename):
251 t = os.stat(filename)[ST_MTIME]
252 else:
253 t = int(time.time())
254 self.rolloverAt = self.computeRollover(t)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000255
Vinay Sajipaffbd872009-06-11 10:11:47 +0000256 def computeRollover(self, currentTime):
257 """
258 Work out the rollover time based on the specified time.
259 """
260 result = currentTime + self.interval
Vinay Sajip17c52d82004-07-03 11:48:34 +0000261 # If we are rolling over at midnight or weekly, then the interval is already known.
262 # What we need to figure out is WHEN the next interval is. In other words,
263 # if you are rolling over at midnight, then your base interval is 1 day,
264 # but you want to start that one day clock at midnight, not now. So, we
265 # have to fudge the rolloverAt value in order to trigger the first rollover
266 # at the right time. After that, the regular interval will take care of
267 # the rest. Note that this code doesn't care about leap seconds. :)
268 if self.when == 'MIDNIGHT' or self.when.startswith('W'):
269 # This could be done with less code, but I wanted it to be clear
Vinay Sajipaffbd872009-06-11 10:11:47 +0000270 if self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000271 t = time.gmtime(currentTime)
272 else:
273 t = time.localtime(currentTime)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000274 currentHour = t[3]
275 currentMinute = t[4]
276 currentSecond = t[5]
277 # r is the number of seconds left between now and midnight
Thomas Wouters477c8d52006-05-27 19:21:47 +0000278 r = _MIDNIGHT - ((currentHour * 60 + currentMinute) * 60 +
279 currentSecond)
Vinay Sajipaffbd872009-06-11 10:11:47 +0000280 result = currentTime + r
Vinay Sajip17c52d82004-07-03 11:48:34 +0000281 # If we are rolling over on a certain day, add in the number of days until
282 # the next rollover, but offset by 1 since we just calculated the time
283 # until the next day starts. There are three cases:
284 # Case 1) The day to rollover is today; in this case, do nothing
285 # Case 2) The day to rollover is further in the interval (i.e., today is
286 # day 2 (Wednesday) and rollover is on day 6 (Sunday). Days to
287 # next rollover is simply 6 - 2 - 1, or 3.
288 # Case 3) The day to rollover is behind us in the interval (i.e., today
289 # is day 5 (Saturday) and rollover is on day 3 (Thursday).
290 # Days to rollover is 6 - 5 + 3, or 4. In this case, it's the
291 # number of days left in the current week (1) plus the number
292 # of days in the next week until the rollover day (3).
Georg Brandl86def6c2008-01-21 20:36:10 +0000293 # The calculations described in 2) and 3) above need to have a day added.
294 # This is because the above time calculation takes us to midnight on this
295 # day, i.e. the start of the next day.
Vinay Sajipaffbd872009-06-11 10:11:47 +0000296 if self.when.startswith('W'):
Vinay Sajip17c52d82004-07-03 11:48:34 +0000297 day = t[6] # 0 is Monday
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000298 if day != self.dayOfWeek:
299 if day < self.dayOfWeek:
Georg Brandl86def6c2008-01-21 20:36:10 +0000300 daysToWait = self.dayOfWeek - day
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000301 else:
Georg Brandl86def6c2008-01-21 20:36:10 +0000302 daysToWait = 6 - day + self.dayOfWeek + 1
Vinay Sajipaffbd872009-06-11 10:11:47 +0000303 newRolloverAt = result + (daysToWait * (60 * 60 * 24))
304 if not self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000305 dstNow = t[-1]
306 dstAtRollover = time.localtime(newRolloverAt)[-1]
307 if dstNow != dstAtRollover:
308 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
309 newRolloverAt = newRolloverAt - 3600
310 else: # DST bows out before next rollover, so we need to add an hour
311 newRolloverAt = newRolloverAt + 3600
Vinay Sajipaffbd872009-06-11 10:11:47 +0000312 result = newRolloverAt
313 return result
Vinay Sajip17c52d82004-07-03 11:48:34 +0000314
315 def shouldRollover(self, record):
316 """
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000317 Determine if rollover should occur.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000318
319 record is not used, as we are just comparing times, but it is needed so
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000320 the method signatures are the same
Vinay Sajip17c52d82004-07-03 11:48:34 +0000321 """
322 t = int(time.time())
323 if t >= self.rolloverAt:
324 return 1
Vinay Sajip17c52d82004-07-03 11:48:34 +0000325 return 0
326
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000327 def getFilesToDelete(self):
328 """
329 Determine the files to delete when rolling over.
330
331 More specific than the earlier method, which just used glob.glob().
332 """
333 dirName, baseName = os.path.split(self.baseFilename)
334 fileNames = os.listdir(dirName)
335 result = []
336 prefix = baseName + "."
337 plen = len(prefix)
338 for fileName in fileNames:
339 if fileName[:plen] == prefix:
340 suffix = fileName[plen:]
341 if self.extMatch.match(suffix):
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000342 result.append(os.path.join(dirName, fileName))
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000343 result.sort()
344 if len(result) < self.backupCount:
345 result = []
346 else:
347 result = result[:len(result) - self.backupCount]
348 return result
349
Vinay Sajip17c52d82004-07-03 11:48:34 +0000350 def doRollover(self):
351 """
352 do a rollover; in this case, a date/time stamp is appended to the filename
353 when the rollover happens. However, you want the file to be named for the
354 start of the interval, not the current time. If there is a backup count,
355 then we have to get a list of matching filenames, sort them and remove
356 the one with the oldest suffix.
357 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000358 if self.stream:
359 self.stream.close()
Vinay Sajip01241d62011-01-21 23:35:57 +0000360 self.stream = None
Vinay Sajip17c52d82004-07-03 11:48:34 +0000361 # get the time that this sequence started at and make it a TimeTuple
362 t = self.rolloverAt - self.interval
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000363 if self.utc:
364 timeTuple = time.gmtime(t)
365 else:
366 timeTuple = time.localtime(t)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000367 dfn = self.rotation_filename(self.baseFilename + "." +
368 time.strftime(self.suffix, timeTuple))
Vinay Sajip17c52d82004-07-03 11:48:34 +0000369 if os.path.exists(dfn):
370 os.remove(dfn)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000371 self.rotate(self.baseFilename, dfn)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000372 if self.backupCount > 0:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000373 for s in self.getFilesToDelete():
374 os.remove(s)
Thomas Woutersb2137042007-02-01 18:02:27 +0000375 self.mode = 'w'
376 self.stream = self._open()
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000377 currentTime = int(time.time())
Vinay Sajipaffbd872009-06-11 10:11:47 +0000378 newRolloverAt = self.computeRollover(currentTime)
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000379 while newRolloverAt <= currentTime:
380 newRolloverAt = newRolloverAt + self.interval
381 #If DST changes and midnight or weekly rollover, adjust for this.
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000382 if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000383 dstNow = time.localtime(currentTime)[-1]
384 dstAtRollover = time.localtime(newRolloverAt)[-1]
385 if dstNow != dstAtRollover:
386 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
387 newRolloverAt = newRolloverAt - 3600
388 else: # DST bows out before next rollover, so we need to add an hour
389 newRolloverAt = newRolloverAt + 3600
390 self.rolloverAt = newRolloverAt
Guido van Rossum57102f82002-11-13 16:15:58 +0000391
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000392class WatchedFileHandler(logging.FileHandler):
393 """
394 A handler for logging to a file, which watches the file
395 to see if it has changed while in use. This can happen because of
396 usage of programs such as newsyslog and logrotate which perform
397 log file rotation. This handler, intended for use under Unix,
398 watches the file to see if it has changed since the last emit.
399 (A file has changed if its device or inode have changed.)
400 If it has changed, the old file stream is closed, and the file
401 opened to get a new stream.
402
403 This handler is not appropriate for use under Windows, because
404 under Windows open files cannot be moved or renamed - logging
405 opens the files with exclusive locks - and so there is no need
406 for such a handler. Furthermore, ST_INO is not supported under
407 Windows; stat always returns zero for this value.
408
409 This handler is based on a suggestion and patch by Chad J.
410 Schroeder.
411 """
Vinay Sajip23b94d02012-01-04 12:02:26 +0000412 def __init__(self, filename, mode='a', encoding=None, delay=False):
Christian Heimese7a15bb2008-01-24 16:21:45 +0000413 logging.FileHandler.__init__(self, filename, mode, encoding, delay)
414 if not os.path.exists(self.baseFilename):
415 self.dev, self.ino = -1, -1
416 else:
417 stat = os.stat(self.baseFilename)
418 self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000419
420 def emit(self, record):
421 """
422 Emit a record.
423
424 First check if the underlying file has changed, and if it
425 has, close the old stream and reopen the file to get the
426 current stream.
427 """
428 if not os.path.exists(self.baseFilename):
429 stat = None
Vinay Sajipde19e082011-04-30 21:52:26 +0100430 changed = True
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000431 else:
432 stat = os.stat(self.baseFilename)
433 changed = (stat[ST_DEV] != self.dev) or (stat[ST_INO] != self.ino)
Christian Heimese7a15bb2008-01-24 16:21:45 +0000434 if changed and self.stream is not None:
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000435 self.stream.flush()
436 self.stream.close()
437 self.stream = self._open()
438 if stat is None:
439 stat = os.stat(self.baseFilename)
440 self.dev, self.ino = stat[ST_DEV], stat[ST_INO]
441 logging.FileHandler.emit(self, record)
442
Guido van Rossum57102f82002-11-13 16:15:58 +0000443class SocketHandler(logging.Handler):
444 """
445 A handler class which writes logging records, in pickle format, to
446 a streaming socket. The socket is kept open across logging calls.
447 If the peer resets it, an attempt is made to reconnect on the next call.
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000448 The pickle which is sent is that of the LogRecord's attribute dictionary
449 (__dict__), so that the receiver does not need to have the logging module
450 installed in order to process the logging event.
451
452 To unpickle the record at the receiving end into a LogRecord, use the
453 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000454 """
455
456 def __init__(self, host, port):
457 """
458 Initializes the handler with a specific host address and port.
459
Vinay Sajipde19e082011-04-30 21:52:26 +0100460 When the attribute *closeOnError* is set to True - if a socket error
461 occurs, the socket is silently closed and then reopened on the next
462 logging call.
Guido van Rossum57102f82002-11-13 16:15:58 +0000463 """
464 logging.Handler.__init__(self)
465 self.host = host
466 self.port = port
467 self.sock = None
Vinay Sajipde19e082011-04-30 21:52:26 +0100468 self.closeOnError = False
Vinay Sajip48cfe382004-02-20 13:17:27 +0000469 self.retryTime = None
470 #
471 # Exponential backoff parameters.
472 #
473 self.retryStart = 1.0
474 self.retryMax = 30.0
475 self.retryFactor = 2.0
Guido van Rossum57102f82002-11-13 16:15:58 +0000476
Guido van Rossumd8faa362007-04-27 19:54:29 +0000477 def makeSocket(self, timeout=1):
Guido van Rossum57102f82002-11-13 16:15:58 +0000478 """
479 A factory method which allows subclasses to define the precise
480 type of socket they want.
481 """
482 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000483 if hasattr(s, 'settimeout'):
484 s.settimeout(timeout)
Vinay Sajipb37cd392011-05-07 15:55:47 +0100485 try:
486 s.connect((self.host, self.port))
487 return s
488 except socket.error:
489 s.close()
490 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000491
Vinay Sajip48cfe382004-02-20 13:17:27 +0000492 def createSocket(self):
493 """
494 Try to create a socket, using an exponential backoff with
495 a max retry time. Thanks to Robert Olson for the original patch
496 (SF #815911) which has been slightly refactored.
497 """
498 now = time.time()
499 # Either retryTime is None, in which case this
500 # is the first time back after a disconnect, or
501 # we've waited long enough.
502 if self.retryTime is None:
Vinay Sajipde19e082011-04-30 21:52:26 +0100503 attempt = True
Vinay Sajip48cfe382004-02-20 13:17:27 +0000504 else:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000505 attempt = (now >= self.retryTime)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000506 if attempt:
507 try:
508 self.sock = self.makeSocket()
509 self.retryTime = None # next time, no delay before trying
Thomas Wouters902d6eb2007-01-09 23:18:33 +0000510 except socket.error:
Vinay Sajip48cfe382004-02-20 13:17:27 +0000511 #Creation failed, so set the retry time and return.
512 if self.retryTime is None:
513 self.retryPeriod = self.retryStart
514 else:
515 self.retryPeriod = self.retryPeriod * self.retryFactor
516 if self.retryPeriod > self.retryMax:
517 self.retryPeriod = self.retryMax
518 self.retryTime = now + self.retryPeriod
519
Guido van Rossum57102f82002-11-13 16:15:58 +0000520 def send(self, s):
521 """
522 Send a pickled string to the socket.
523
524 This function allows for partial sends which can happen when the
525 network is busy.
526 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000527 if self.sock is None:
528 self.createSocket()
529 #self.sock can be None either because we haven't reached the retry
530 #time yet, or because we have reached the retry time and retried,
531 #but are still unable to connect.
532 if self.sock:
533 try:
534 if hasattr(self.sock, "sendall"):
535 self.sock.sendall(s)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100536 else: #pragma: no cover
Vinay Sajip48cfe382004-02-20 13:17:27 +0000537 sentsofar = 0
538 left = len(s)
539 while left > 0:
540 sent = self.sock.send(s[sentsofar:])
541 sentsofar = sentsofar + sent
542 left = left - sent
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100543 except socket.error: #pragma: no cover
Vinay Sajip48cfe382004-02-20 13:17:27 +0000544 self.sock.close()
545 self.sock = None # so we can call createSocket next time
Guido van Rossum57102f82002-11-13 16:15:58 +0000546
547 def makePickle(self, record):
548 """
549 Pickles the record in binary format with a length prefix, and
550 returns it ready for transmission across the socket.
551 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000552 ei = record.exc_info
553 if ei:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000554 dummy = self.format(record) # just to get traceback text into record.exc_text
555 record.exc_info = None # to avoid Unpickleable error
Guido van Rossumba205d62006-08-17 08:57:26 +0000556 s = pickle.dumps(record.__dict__, 1)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000557 if ei:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000558 record.exc_info = ei # for next handler
Guido van Rossum57102f82002-11-13 16:15:58 +0000559 slen = struct.pack(">L", len(s))
560 return slen + s
561
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000562 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000563 """
564 Handle an error during logging.
565
566 An error has occurred during logging. Most likely cause -
567 connection lost. Close the socket so that we can retry on the
568 next event.
569 """
570 if self.closeOnError and self.sock:
571 self.sock.close()
572 self.sock = None #try to reconnect next time
573 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000574 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000575
576 def emit(self, record):
577 """
578 Emit a record.
579
580 Pickles the record and writes it to the socket in binary format.
581 If there is an error with the socket, silently drop the packet.
582 If there was a problem with the socket, re-establishes the
583 socket.
584 """
585 try:
586 s = self.makePickle(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000587 self.send(s)
Vinay Sajip985ef872011-04-26 19:34:04 +0100588 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip85c19092005-10-31 13:14:19 +0000589 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000590 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000591 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000592
593 def close(self):
594 """
595 Closes the socket.
596 """
597 if self.sock:
598 self.sock.close()
599 self.sock = None
Vinay Sajip48cfe382004-02-20 13:17:27 +0000600 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000601
602class DatagramHandler(SocketHandler):
603 """
604 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000605 a datagram socket. The pickle which is sent is that of the LogRecord's
606 attribute dictionary (__dict__), so that the receiver does not need to
607 have the logging module installed in order to process the logging event.
608
609 To unpickle the record at the receiving end into a LogRecord, use the
610 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000611
612 """
613 def __init__(self, host, port):
614 """
615 Initializes the handler with a specific host address and port.
616 """
617 SocketHandler.__init__(self, host, port)
Vinay Sajipde19e082011-04-30 21:52:26 +0100618 self.closeOnError = False
Guido van Rossum57102f82002-11-13 16:15:58 +0000619
620 def makeSocket(self):
621 """
622 The factory method of SocketHandler is here overridden to create
623 a UDP socket (SOCK_DGRAM).
624 """
625 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
626 return s
627
628 def send(self, s):
629 """
630 Send a pickled string to a socket.
631
632 This function no longer allows for partial sends which can happen
633 when the network is busy - UDP does not guarantee delivery and
634 can deliver packets out of sequence.
635 """
Vinay Sajipfb154172004-08-24 09:36:23 +0000636 if self.sock is None:
637 self.createSocket()
Guido van Rossum57102f82002-11-13 16:15:58 +0000638 self.sock.sendto(s, (self.host, self.port))
639
640class SysLogHandler(logging.Handler):
641 """
642 A handler class which sends formatted logging records to a syslog
643 server. Based on Sam Rushing's syslog module:
644 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
645 Contributed by Nicolas Untz (after which minor refactoring changes
646 have been made).
647 """
648
649 # from <linux/sys/syslog.h>:
650 # ======================================================================
651 # priorities/facilities are encoded into a single 32-bit quantity, where
652 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
653 # facility (0-big number). Both the priorities and the facilities map
654 # roughly one-to-one to strings in the syslogd(8) source code. This
655 # mapping is included in this file.
656 #
657 # priorities (these are ordered)
658
659 LOG_EMERG = 0 # system is unusable
660 LOG_ALERT = 1 # action must be taken immediately
661 LOG_CRIT = 2 # critical conditions
662 LOG_ERR = 3 # error conditions
663 LOG_WARNING = 4 # warning conditions
664 LOG_NOTICE = 5 # normal but significant condition
665 LOG_INFO = 6 # informational
666 LOG_DEBUG = 7 # debug-level messages
667
668 # facility codes
669 LOG_KERN = 0 # kernel messages
670 LOG_USER = 1 # random user-level messages
671 LOG_MAIL = 2 # mail system
672 LOG_DAEMON = 3 # system daemons
673 LOG_AUTH = 4 # security/authorization messages
674 LOG_SYSLOG = 5 # messages generated internally by syslogd
675 LOG_LPR = 6 # line printer subsystem
676 LOG_NEWS = 7 # network news subsystem
677 LOG_UUCP = 8 # UUCP subsystem
678 LOG_CRON = 9 # clock daemon
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000679 LOG_AUTHPRIV = 10 # security/authorization messages (private)
680 LOG_FTP = 11 # FTP daemon
Guido van Rossum57102f82002-11-13 16:15:58 +0000681
682 # other codes through 15 reserved for system use
683 LOG_LOCAL0 = 16 # reserved for local use
684 LOG_LOCAL1 = 17 # reserved for local use
685 LOG_LOCAL2 = 18 # reserved for local use
686 LOG_LOCAL3 = 19 # reserved for local use
687 LOG_LOCAL4 = 20 # reserved for local use
688 LOG_LOCAL5 = 21 # reserved for local use
689 LOG_LOCAL6 = 22 # reserved for local use
690 LOG_LOCAL7 = 23 # reserved for local use
691
692 priority_names = {
693 "alert": LOG_ALERT,
694 "crit": LOG_CRIT,
695 "critical": LOG_CRIT,
696 "debug": LOG_DEBUG,
697 "emerg": LOG_EMERG,
698 "err": LOG_ERR,
699 "error": LOG_ERR, # DEPRECATED
700 "info": LOG_INFO,
701 "notice": LOG_NOTICE,
702 "panic": LOG_EMERG, # DEPRECATED
703 "warn": LOG_WARNING, # DEPRECATED
704 "warning": LOG_WARNING,
705 }
706
707 facility_names = {
708 "auth": LOG_AUTH,
709 "authpriv": LOG_AUTHPRIV,
710 "cron": LOG_CRON,
711 "daemon": LOG_DAEMON,
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000712 "ftp": LOG_FTP,
Guido van Rossum57102f82002-11-13 16:15:58 +0000713 "kern": LOG_KERN,
714 "lpr": LOG_LPR,
715 "mail": LOG_MAIL,
716 "news": LOG_NEWS,
717 "security": LOG_AUTH, # DEPRECATED
718 "syslog": LOG_SYSLOG,
719 "user": LOG_USER,
720 "uucp": LOG_UUCP,
721 "local0": LOG_LOCAL0,
722 "local1": LOG_LOCAL1,
723 "local2": LOG_LOCAL2,
724 "local3": LOG_LOCAL3,
725 "local4": LOG_LOCAL4,
726 "local5": LOG_LOCAL5,
727 "local6": LOG_LOCAL6,
728 "local7": LOG_LOCAL7,
729 }
730
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000731 #The map below appears to be trivially lowercasing the key. However,
732 #there's more to it than meets the eye - in some locales, lowercasing
733 #gives unexpected results. See SF #1524081: in the Turkish locale,
734 #"INFO".lower() != "info"
735 priority_map = {
736 "DEBUG" : "debug",
737 "INFO" : "info",
738 "WARNING" : "warning",
739 "ERROR" : "error",
740 "CRITICAL" : "critical"
741 }
742
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000743 def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
744 facility=LOG_USER, socktype=socket.SOCK_DGRAM):
Guido van Rossum57102f82002-11-13 16:15:58 +0000745 """
746 Initialize a handler.
747
Guido van Rossume7ba4952007-06-06 23:52:48 +0000748 If address is specified as a string, a UNIX socket is used. To log to a
749 local syslogd, "SysLogHandler(address="/dev/log")" can be used.
Guido van Rossum57102f82002-11-13 16:15:58 +0000750 If facility is not specified, LOG_USER is used.
751 """
752 logging.Handler.__init__(self)
753
754 self.address = address
755 self.facility = facility
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000756 self.socktype = socktype
757
Guido van Rossum13257902007-06-07 23:15:56 +0000758 if isinstance(address, str):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100759 self.unixsocket = True
Thomas Wouters89f507f2006-12-13 04:49:30 +0000760 self._connect_unixsocket(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000761 else:
Vinay Sajip5a35b062011-04-27 11:31:14 +0100762 self.unixsocket = False
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000763 self.socket = socket.socket(socket.AF_INET, socktype)
764 if socktype == socket.SOCK_STREAM:
765 self.socket.connect(address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000766 self.formatter = None
767
Vinay Sajipa1974c12005-01-13 08:23:56 +0000768 def _connect_unixsocket(self, address):
769 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
770 # syslog may require either DGRAM or STREAM sockets
771 try:
772 self.socket.connect(address)
773 except socket.error:
774 self.socket.close()
775 self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Vinay Sajip8b6b53f2005-11-09 13:55:13 +0000776 self.socket.connect(address)
Vinay Sajipa1974c12005-01-13 08:23:56 +0000777
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000778 def encodePriority(self, facility, priority):
Guido van Rossum57102f82002-11-13 16:15:58 +0000779 """
780 Encode the facility and priority. You can pass in strings or
781 integers - if strings are passed, the facility_names and
782 priority_names mapping dictionaries are used to convert them to
783 integers.
784 """
Guido van Rossum13257902007-06-07 23:15:56 +0000785 if isinstance(facility, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000786 facility = self.facility_names[facility]
Guido van Rossum13257902007-06-07 23:15:56 +0000787 if isinstance(priority, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000788 priority = self.priority_names[priority]
789 return (facility << 3) | priority
790
791 def close (self):
792 """
793 Closes the socket.
794 """
Vinay Sajipfd285022011-05-07 17:01:22 +0100795 self.socket.close()
Vinay Sajip48cfe382004-02-20 13:17:27 +0000796 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +0000797
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000798 def mapPriority(self, levelName):
799 """
800 Map a logging level name to a key in the priority_names map.
801 This is useful in two scenarios: when custom levels are being
802 used, and in the case where you can't do a straightforward
803 mapping by lowercasing the logging level name because of locale-
804 specific issues (see SF #1524081).
805 """
806 return self.priority_map.get(levelName, "warning")
807
Vinay Sajip2353e352011-06-27 15:40:06 +0100808 ident = '' # prepended to all messages
Vinay Sajip8168d102011-06-09 16:50:49 +0100809 append_nul = True # some old syslog daemons expect a NUL terminator
810
Guido van Rossum57102f82002-11-13 16:15:58 +0000811 def emit(self, record):
812 """
813 Emit a record.
814
815 The record is formatted, and then sent to the syslog server. If
816 exception information is present, it is NOT sent to the server.
817 """
Vinay Sajip8168d102011-06-09 16:50:49 +0100818 msg = self.format(record)
Vinay Sajip2353e352011-06-27 15:40:06 +0100819 if self.ident:
820 msg = self.ident + msg
Vinay Sajip8168d102011-06-09 16:50:49 +0100821 if self.append_nul:
822 msg += '\000'
Guido van Rossum57102f82002-11-13 16:15:58 +0000823 """
824 We need to convert record level to lowercase, maybe this will
825 change in the future.
826 """
Vinay Sajip467d12f2010-08-19 22:20:22 +0000827 prio = '<%d>' % self.encodePriority(self.facility,
828 self.mapPriority(record.levelname))
829 prio = prio.encode('utf-8')
Vinay Sajip609364a2010-08-30 18:31:13 +0000830 # Message is a string. Convert to bytes as required by RFC 5424
Vinay Sajip42ead482009-10-21 20:22:14 +0000831 msg = msg.encode('utf-8')
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100832 msg = prio + BOM_UTF8 + msg
Guido van Rossum57102f82002-11-13 16:15:58 +0000833 try:
834 if self.unixsocket:
Vinay Sajipa1974c12005-01-13 08:23:56 +0000835 try:
836 self.socket.send(msg)
837 except socket.error:
838 self._connect_unixsocket(self.address)
839 self.socket.send(msg)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000840 elif self.socktype == socket.SOCK_DGRAM:
Guido van Rossum57102f82002-11-13 16:15:58 +0000841 self.socket.sendto(msg, self.address)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000842 else:
843 self.socket.sendall(msg)
Vinay Sajip985ef872011-04-26 19:34:04 +0100844 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip85c19092005-10-31 13:14:19 +0000845 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000846 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000847 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000848
849class SMTPHandler(logging.Handler):
850 """
851 A handler class which sends an SMTP email for each logging event.
852 """
Vinay Sajip540f2152009-12-06 17:57:11 +0000853 def __init__(self, mailhost, fromaddr, toaddrs, subject,
Vinay Sajip25fcd222009-12-06 18:05:04 +0000854 credentials=None, secure=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000855 """
856 Initialize the handler.
857
858 Initialize the instance with the from and to addresses and subject
859 line of the email. To specify a non-standard SMTP port, use the
Guido van Rossum360e4b82007-05-14 22:51:27 +0000860 (host, port) tuple format for the mailhost argument. To specify
861 authentication credentials, supply a (username, password) tuple
Vinay Sajip25fcd222009-12-06 18:05:04 +0000862 for the credentials argument. To specify the use of a secure
863 protocol (TLS), pass in a tuple for the secure argument. This will
864 only be used when authentication credentials are supplied. The tuple
865 will be either an empty tuple, or a single-value tuple with the name
866 of a keyfile, or a 2-value tuple with the names of the keyfile and
867 certificate file. (This tuple is passed to the `starttls` method).
Guido van Rossum57102f82002-11-13 16:15:58 +0000868 """
869 logging.Handler.__init__(self)
Guido van Rossum13257902007-06-07 23:15:56 +0000870 if isinstance(mailhost, tuple):
Guido van Rossum360e4b82007-05-14 22:51:27 +0000871 self.mailhost, self.mailport = mailhost
Guido van Rossum57102f82002-11-13 16:15:58 +0000872 else:
Guido van Rossum360e4b82007-05-14 22:51:27 +0000873 self.mailhost, self.mailport = mailhost, None
Guido van Rossum13257902007-06-07 23:15:56 +0000874 if isinstance(credentials, tuple):
Guido van Rossum360e4b82007-05-14 22:51:27 +0000875 self.username, self.password = credentials
876 else:
877 self.username = None
Guido van Rossum57102f82002-11-13 16:15:58 +0000878 self.fromaddr = fromaddr
Guido van Rossum13257902007-06-07 23:15:56 +0000879 if isinstance(toaddrs, str):
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000880 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +0000881 self.toaddrs = toaddrs
882 self.subject = subject
Vinay Sajip540f2152009-12-06 17:57:11 +0000883 self.secure = secure
Guido van Rossum57102f82002-11-13 16:15:58 +0000884
885 def getSubject(self, record):
886 """
887 Determine the subject for the email.
888
889 If you want to specify a subject line which is record-dependent,
890 override this method.
891 """
892 return self.subject
893
894 def emit(self, record):
895 """
896 Emit a record.
897
898 Format the record and send it to the specified addressees.
899 """
900 try:
901 import smtplib
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000902 from email.utils import formatdate
Guido van Rossum57102f82002-11-13 16:15:58 +0000903 port = self.mailport
904 if not port:
905 port = smtplib.SMTP_PORT
906 smtp = smtplib.SMTP(self.mailhost, port)
907 msg = self.format(record)
Neal Norwitzf297bd12003-04-23 03:49:43 +0000908 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 +0000909 self.fromaddr,
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000910 ",".join(self.toaddrs),
Neal Norwitzf297bd12003-04-23 03:49:43 +0000911 self.getSubject(record),
Martin v. Löwis318a12e2004-08-18 12:27:40 +0000912 formatdate(), msg)
Guido van Rossum360e4b82007-05-14 22:51:27 +0000913 if self.username:
Vinay Sajip25fcd222009-12-06 18:05:04 +0000914 if self.secure is not None:
Vinay Sajip540f2152009-12-06 17:57:11 +0000915 smtp.ehlo()
Vinay Sajip25fcd222009-12-06 18:05:04 +0000916 smtp.starttls(*self.secure)
Vinay Sajip540f2152009-12-06 17:57:11 +0000917 smtp.ehlo()
Guido van Rossum360e4b82007-05-14 22:51:27 +0000918 smtp.login(self.username, self.password)
Guido van Rossum57102f82002-11-13 16:15:58 +0000919 smtp.sendmail(self.fromaddr, self.toaddrs, msg)
920 smtp.quit()
Vinay Sajip985ef872011-04-26 19:34:04 +0100921 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip245a5ab2005-10-31 14:27:01 +0000922 raise
Guido van Rossum57102f82002-11-13 16:15:58 +0000923 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000924 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000925
926class NTEventLogHandler(logging.Handler):
927 """
928 A handler class which sends events to the NT Event Log. Adds a
929 registry entry for the specified application name. If no dllname is
930 provided, win32service.pyd (which contains some basic message
931 placeholders) is used. Note that use of these placeholders will make
932 your event logs big, as the entire message source is held in the log.
933 If you want slimmer logs, you have to pass in the name of your own DLL
934 which contains the message definitions you want to use in the event log.
935 """
936 def __init__(self, appname, dllname=None, logtype="Application"):
937 logging.Handler.__init__(self)
938 try:
939 import win32evtlogutil, win32evtlog
940 self.appname = appname
941 self._welu = win32evtlogutil
942 if not dllname:
943 dllname = os.path.split(self._welu.__file__)
944 dllname = os.path.split(dllname[0])
945 dllname = os.path.join(dllname[0], r'win32service.pyd')
946 self.dllname = dllname
947 self.logtype = logtype
948 self._welu.AddSourceToRegistry(appname, dllname, logtype)
949 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
950 self.typemap = {
951 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
952 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000953 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +0000954 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
955 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
956 }
957 except ImportError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000958 print("The Python Win32 extensions for NT (service, event "\
959 "logging) appear not to be available.")
Guido van Rossum57102f82002-11-13 16:15:58 +0000960 self._welu = None
961
962 def getMessageID(self, record):
963 """
964 Return the message ID for the event record. If you are using your
965 own messages, you could do this by having the msg passed to the
966 logger being an ID rather than a formatting string. Then, in here,
967 you could use a dictionary lookup to get the message ID. This
968 version returns 1, which is the base message ID in win32service.pyd.
969 """
970 return 1
971
972 def getEventCategory(self, record):
973 """
974 Return the event category for the record.
975
976 Override this if you want to specify your own categories. This version
977 returns 0.
978 """
979 return 0
980
981 def getEventType(self, record):
982 """
983 Return the event type for the record.
984
985 Override this if you want to specify your own types. This version does
986 a mapping using the handler's typemap attribute, which is set up in
987 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000988 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +0000989 either need to override this method or place a suitable dictionary in
990 the handler's typemap attribute.
991 """
992 return self.typemap.get(record.levelno, self.deftype)
993
994 def emit(self, record):
995 """
996 Emit a record.
997
998 Determine the message ID, event category and event type. Then
999 log the message in the NT event log.
1000 """
1001 if self._welu:
1002 try:
1003 id = self.getMessageID(record)
1004 cat = self.getEventCategory(record)
1005 type = self.getEventType(record)
1006 msg = self.format(record)
1007 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
Vinay Sajip985ef872011-04-26 19:34:04 +01001008 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip245a5ab2005-10-31 14:27:01 +00001009 raise
Guido van Rossum57102f82002-11-13 16:15:58 +00001010 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001011 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001012
1013 def close(self):
1014 """
1015 Clean up this handler.
1016
1017 You can remove the application name from the registry as a
1018 source of event log entries. However, if you do this, you will
1019 not be able to see the events as you intended in the Event Log
1020 Viewer - it needs to be able to access the registry to get the
1021 DLL name.
1022 """
1023 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
Vinay Sajip48cfe382004-02-20 13:17:27 +00001024 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +00001025
1026class HTTPHandler(logging.Handler):
1027 """
1028 A class which sends records to a Web server, using either GET or
1029 POST semantics.
1030 """
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001031 def __init__(self, host, url, method="GET", secure=False, credentials=None):
Guido van Rossum57102f82002-11-13 16:15:58 +00001032 """
1033 Initialize the instance with the host, the request URL, and the method
1034 ("GET" or "POST")
1035 """
1036 logging.Handler.__init__(self)
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001037 method = method.upper()
Guido van Rossum57102f82002-11-13 16:15:58 +00001038 if method not in ["GET", "POST"]:
Collin Winterce36ad82007-08-30 01:19:48 +00001039 raise ValueError("method must be GET or POST")
Guido van Rossum57102f82002-11-13 16:15:58 +00001040 self.host = host
1041 self.url = url
1042 self.method = method
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001043 self.secure = secure
1044 self.credentials = credentials
Guido van Rossum57102f82002-11-13 16:15:58 +00001045
Neal Norwitzf297bd12003-04-23 03:49:43 +00001046 def mapLogRecord(self, record):
1047 """
1048 Default implementation of mapping the log record into a dict
Vinay Sajip48cfe382004-02-20 13:17:27 +00001049 that is sent as the CGI data. Overwrite in your class.
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001050 Contributed by Franz Glasner.
Neal Norwitzf297bd12003-04-23 03:49:43 +00001051 """
1052 return record.__dict__
1053
Guido van Rossum57102f82002-11-13 16:15:58 +00001054 def emit(self, record):
1055 """
1056 Emit a record.
1057
Senthil Kumaran30e86a42010-08-09 20:01:35 +00001058 Send the record to the Web server as a percent-encoded dictionary
Guido van Rossum57102f82002-11-13 16:15:58 +00001059 """
1060 try:
Georg Brandl029986a2008-06-23 11:44:14 +00001061 import http.client, urllib.parse
Vinay Sajipb7935062005-10-11 13:15:31 +00001062 host = self.host
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001063 if self.secure:
1064 h = http.client.HTTPSConnection(host)
1065 else:
1066 h = http.client.HTTPConnection(host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001067 url = self.url
Georg Brandl029986a2008-06-23 11:44:14 +00001068 data = urllib.parse.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +00001069 if self.method == "GET":
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001070 if (url.find('?') >= 0):
Guido van Rossum57102f82002-11-13 16:15:58 +00001071 sep = '&'
1072 else:
1073 sep = '?'
1074 url = url + "%c%s" % (sep, data)
1075 h.putrequest(self.method, url)
Vinay Sajipb7935062005-10-11 13:15:31 +00001076 # support multiple hosts on one IP address...
1077 # need to strip optional :port from host, if present
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001078 i = host.find(":")
Vinay Sajipb7935062005-10-11 13:15:31 +00001079 if i >= 0:
1080 host = host[:i]
1081 h.putheader("Host", host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001082 if self.method == "POST":
Vinay Sajipb7935062005-10-11 13:15:31 +00001083 h.putheader("Content-type",
1084 "application/x-www-form-urlencoded")
Guido van Rossum57102f82002-11-13 16:15:58 +00001085 h.putheader("Content-length", str(len(data)))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001086 if self.credentials:
1087 import base64
1088 s = ('u%s:%s' % self.credentials).encode('utf-8')
1089 s = 'Basic ' + base64.b64encode(s).strip()
1090 h.putheader('Authorization', s)
Vinay Sajip0372e102011-05-05 12:59:14 +01001091 h.endheaders()
1092 if self.method == "POST":
1093 h.send(data.encode('utf-8'))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001094 h.getresponse() #can't do anything with the result
Vinay Sajip985ef872011-04-26 19:34:04 +01001095 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip245a5ab2005-10-31 14:27:01 +00001096 raise
Guido van Rossum57102f82002-11-13 16:15:58 +00001097 except:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001098 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001099
1100class BufferingHandler(logging.Handler):
1101 """
1102 A handler class which buffers logging records in memory. Whenever each
1103 record is added to the buffer, a check is made to see if the buffer should
1104 be flushed. If it should, then flush() is expected to do what's needed.
1105 """
1106 def __init__(self, capacity):
1107 """
1108 Initialize the handler with the buffer size.
1109 """
1110 logging.Handler.__init__(self)
1111 self.capacity = capacity
1112 self.buffer = []
1113
1114 def shouldFlush(self, record):
1115 """
1116 Should the handler flush its buffer?
1117
1118 Returns true if the buffer is up to capacity. This method can be
1119 overridden to implement custom flushing strategies.
1120 """
1121 return (len(self.buffer) >= self.capacity)
1122
1123 def emit(self, record):
1124 """
1125 Emit a record.
1126
1127 Append the record. If shouldFlush() tells us to, call flush() to process
1128 the buffer.
1129 """
1130 self.buffer.append(record)
1131 if self.shouldFlush(record):
1132 self.flush()
1133
1134 def flush(self):
1135 """
1136 Override to implement custom flushing behaviour.
1137
1138 This version just zaps the buffer to empty.
1139 """
1140 self.buffer = []
1141
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001142 def close(self):
1143 """
1144 Close the handler.
1145
1146 This version just flushes and chains to the parent class' close().
1147 """
1148 self.flush()
1149 logging.Handler.close(self)
1150
Guido van Rossum57102f82002-11-13 16:15:58 +00001151class MemoryHandler(BufferingHandler):
1152 """
1153 A handler class which buffers logging records in memory, periodically
1154 flushing them to a target handler. Flushing occurs whenever the buffer
1155 is full, or when an event of a certain severity or greater is seen.
1156 """
1157 def __init__(self, capacity, flushLevel=logging.ERROR, target=None):
1158 """
1159 Initialize the handler with the buffer size, the level at which
1160 flushing should occur and an optional target.
1161
1162 Note that without a target being set either here or via setTarget(),
1163 a MemoryHandler is no use to anyone!
1164 """
1165 BufferingHandler.__init__(self, capacity)
1166 self.flushLevel = flushLevel
1167 self.target = target
1168
1169 def shouldFlush(self, record):
1170 """
1171 Check for buffer full or a record at the flushLevel or higher.
1172 """
1173 return (len(self.buffer) >= self.capacity) or \
1174 (record.levelno >= self.flushLevel)
1175
1176 def setTarget(self, target):
1177 """
1178 Set the target handler for this handler.
1179 """
1180 self.target = target
1181
1182 def flush(self):
1183 """
1184 For a MemoryHandler, flushing means just sending the buffered
1185 records to the target, if there is one. Override if you want
1186 different behaviour.
Vinay Sajipc84f0162010-09-21 11:25:39 +00001187
1188 The record buffer is also cleared by this operation.
Guido van Rossum57102f82002-11-13 16:15:58 +00001189 """
1190 if self.target:
1191 for record in self.buffer:
1192 self.target.handle(record)
1193 self.buffer = []
1194
1195 def close(self):
1196 """
1197 Flush, set the target to None and lose the buffer.
1198 """
1199 self.flush()
1200 self.target = None
Vinay Sajip48cfe382004-02-20 13:17:27 +00001201 BufferingHandler.close(self)
Vinay Sajip121a1c42010-09-08 10:46:15 +00001202
1203
1204class QueueHandler(logging.Handler):
1205 """
1206 This handler sends events to a queue. Typically, it would be used together
1207 with a multiprocessing Queue to centralise logging to file in one process
1208 (in a multi-process application), so as to avoid file write contention
1209 between processes.
1210
1211 This code is new in Python 3.2, but this class can be copy pasted into
1212 user code for use with earlier Python versions.
1213 """
1214
1215 def __init__(self, queue):
1216 """
1217 Initialise an instance, using the passed queue.
1218 """
1219 logging.Handler.__init__(self)
1220 self.queue = queue
1221
1222 def enqueue(self, record):
1223 """
1224 Enqueue a record.
1225
1226 The base implementation uses put_nowait. You may want to override
1227 this method if you want to use blocking, timeouts or custom queue
1228 implementations.
1229 """
1230 self.queue.put_nowait(record)
1231
Vinay Sajip0258ce82010-09-22 20:34:53 +00001232 def prepare(self, record):
1233 """
Vinay Sajip0637d492010-09-23 08:15:54 +00001234 Prepares a record for queuing. The object returned by this method is
1235 enqueued.
Vinay Sajip0258ce82010-09-22 20:34:53 +00001236
1237 The base implementation formats the record to merge the message
1238 and arguments, and removes unpickleable items from the record
1239 in-place.
1240
1241 You might want to override this method if you want to convert
1242 the record to a dict or JSON string, or send a modified copy
1243 of the record while leaving the original intact.
1244 """
1245 # The format operation gets traceback text into record.exc_text
1246 # (if there's exception data), and also puts the message into
1247 # record.message. We can then use this to replace the original
1248 # msg + args, as these might be unpickleable. We also zap the
1249 # exc_info attribute, as it's no longer needed and, if not None,
1250 # will typically not be pickleable.
1251 self.format(record)
1252 record.msg = record.message
1253 record.args = None
1254 record.exc_info = None
1255 return record
1256
Vinay Sajip121a1c42010-09-08 10:46:15 +00001257 def emit(self, record):
1258 """
1259 Emit a record.
1260
Vinay Sajip0637d492010-09-23 08:15:54 +00001261 Writes the LogRecord to the queue, preparing it for pickling first.
Vinay Sajip121a1c42010-09-08 10:46:15 +00001262 """
1263 try:
Vinay Sajip0258ce82010-09-22 20:34:53 +00001264 self.enqueue(self.prepare(record))
Vinay Sajip985ef872011-04-26 19:34:04 +01001265 except (KeyboardInterrupt, SystemExit): #pragma: no cover
Vinay Sajip121a1c42010-09-08 10:46:15 +00001266 raise
1267 except:
1268 self.handleError(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001269
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001270if threading:
1271 class QueueListener(object):
1272 """
1273 This class implements an internal threaded listener which watches for
1274 LogRecords being added to a queue, removes them and passes them to a
1275 list of handlers for processing.
1276 """
1277 _sentinel = None
Vinay Sajip0637d492010-09-23 08:15:54 +00001278
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001279 def __init__(self, queue, *handlers):
1280 """
1281 Initialise an instance with the specified queue and
1282 handlers.
1283 """
1284 self.queue = queue
1285 self.handlers = handlers
1286 self._stop = threading.Event()
1287 self._thread = None
Vinay Sajip0637d492010-09-23 08:15:54 +00001288
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001289 def dequeue(self, block):
1290 """
1291 Dequeue a record and return it, optionally blocking.
Vinay Sajip0637d492010-09-23 08:15:54 +00001292
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001293 The base implementation uses get. You may want to override this method
1294 if you want to use timeouts or work with custom queue implementations.
1295 """
1296 return self.queue.get(block)
Vinay Sajip0637d492010-09-23 08:15:54 +00001297
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001298 def start(self):
1299 """
1300 Start the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001301
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001302 This starts up a background thread to monitor the queue for
1303 LogRecords to process.
1304 """
1305 self._thread = t = threading.Thread(target=self._monitor)
1306 t.setDaemon(True)
1307 t.start()
Vinay Sajip0637d492010-09-23 08:15:54 +00001308
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001309 def prepare(self , record):
1310 """
1311 Prepare a record for handling.
Vinay Sajip0637d492010-09-23 08:15:54 +00001312
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001313 This method just returns the passed-in record. You may want to
1314 override this method if you need to do any custom marshalling or
1315 manipulation of the record before passing it to the handlers.
1316 """
1317 return record
Vinay Sajip0637d492010-09-23 08:15:54 +00001318
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001319 def handle(self, record):
1320 """
1321 Handle a record.
Vinay Sajip0637d492010-09-23 08:15:54 +00001322
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001323 This just loops through the handlers offering them the record
1324 to handle.
1325 """
1326 record = self.prepare(record)
1327 for handler in self.handlers:
1328 handler.handle(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001329
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001330 def _monitor(self):
1331 """
1332 Monitor the queue for records, and ask the handler
1333 to deal with them.
Vinay Sajip0637d492010-09-23 08:15:54 +00001334
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001335 This method runs on a separate, internal thread.
1336 The thread will terminate if it sees a sentinel object in the queue.
1337 """
1338 q = self.queue
1339 has_task_done = hasattr(q, 'task_done')
1340 while not self._stop.isSet():
1341 try:
1342 record = self.dequeue(True)
1343 if record is self._sentinel:
1344 break
1345 self.handle(record)
1346 if has_task_done:
1347 q.task_done()
1348 except queue.Empty:
1349 pass
1350 # There might still be records in the queue.
1351 while True:
1352 try:
1353 record = self.dequeue(False)
1354 if record is self._sentinel:
1355 break
1356 self.handle(record)
1357 if has_task_done:
1358 q.task_done()
1359 except queue.Empty:
Vinay Sajip0637d492010-09-23 08:15:54 +00001360 break
Vinay Sajip0637d492010-09-23 08:15:54 +00001361
Victor Stinner59bec362011-05-02 16:14:16 +02001362 def enqueue_sentinel(self):
1363 """
1364 This is used to enqueue the sentinel record.
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001365
Victor Stinner59bec362011-05-02 16:14:16 +02001366 The base implementation uses put_nowait. You may want to override this
1367 method if you want to use timeouts or work with custom queue
1368 implementations.
1369 """
1370 self.queue.put_nowait(self._sentinel)
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001371
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001372 def stop(self):
1373 """
1374 Stop the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001375
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001376 This asks the thread to terminate, and then waits for it to do so.
1377 Note that if you don't call this before your application exits, there
1378 may be some records still left on the queue, which won't be processed.
1379 """
1380 self._stop.set()
Victor Stinner59bec362011-05-02 16:14:16 +02001381 self.enqueue_sentinel()
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001382 self._thread.join()
1383 self._thread = None