blob: 4dcbe4530fcfcccb9ccf9b9ea6af580bea7b89b4 [file] [log] [blame]
Vinay Sajipcccf6062016-07-22 16:27:31 +01001# Copyright 2001-2016 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
Vinay Sajip0abf61d2012-02-23 19:45:52 +000019based on PEP 282 and comments thereto in comp.lang.python.
Guido van Rossum57102f82002-11-13 16:15:58 +000020
Vinay Sajipcccf6062016-07-22 16:27:31 +010021Copyright (C) 2001-2016 Vinay Sajip. All Rights Reserved.
Guido van Rossum57102f82002-11-13 16:15:58 +000022
Vinay Sajip6268cbc2009-01-21 00:19:28 +000023To use, simply 'import logging.handlers' and log away!
Guido van Rossum57102f82002-11-13 16:15:58 +000024"""
25
Inada Naokifb786922021-04-06 11:18:41 +090026import io, logging, socket, os, pickle, struct, time, re
Benjamin Peterson9451a1c2010-03-13 22:30:34 +000027from stat import ST_DEV, ST_INO, ST_MTIME
Vinay Sajipe723e962011-04-15 22:27:17 +010028import queue
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020029import threading
Manjusakada6424e2019-01-23 15:08:38 +080030import copy
Guido van Rossum57102f82002-11-13 16:15:58 +000031
Guido van Rossum57102f82002-11-13 16:15:58 +000032#
33# Some constants...
34#
35
36DEFAULT_TCP_LOGGING_PORT = 9020
37DEFAULT_UDP_LOGGING_PORT = 9021
38DEFAULT_HTTP_LOGGING_PORT = 9022
39DEFAULT_SOAP_LOGGING_PORT = 9023
40SYSLOG_UDP_PORT = 514
Vinay Sajipcbabd7e2009-10-10 20:32:36 +000041SYSLOG_TCP_PORT = 514
Guido van Rossum57102f82002-11-13 16:15:58 +000042
Thomas Wouters477c8d52006-05-27 19:21:47 +000043_MIDNIGHT = 24 * 60 * 60 # number of seconds in a day
44
Vinay Sajip17c52d82004-07-03 11:48:34 +000045class BaseRotatingHandler(logging.FileHandler):
46 """
47 Base class for handlers that rotate log files at a certain point.
48 Not meant to be instantiated directly. Instead, use RotatingFileHandler
49 or TimedRotatingFileHandler.
50 """
l0rb519cb872019-11-06 22:21:40 +010051 namer = None
52 rotator = None
53
Vinay Sajipca7b5042019-06-17 17:40:52 +010054 def __init__(self, filename, mode, encoding=None, delay=False, errors=None):
Vinay Sajip17c52d82004-07-03 11:48:34 +000055 """
56 Use the specified filename for streamed logging
57 """
Vinay Sajipca7b5042019-06-17 17:40:52 +010058 logging.FileHandler.__init__(self, filename, mode=mode,
59 encoding=encoding, delay=delay,
60 errors=errors)
Vinay Sajip4600f112005-03-13 09:56:36 +000061 self.mode = mode
62 self.encoding = encoding
Vinay Sajipca7b5042019-06-17 17:40:52 +010063 self.errors = errors
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 Sajip8cf4eb12012-10-09 08:06:13 +010076 except Exception:
Vinay Sajip3970c112004-07-08 10:24:04 +000077 self.handleError(record)
Vinay Sajip17c52d82004-07-03 11:48:34 +000078
Vinay Sajip23b94d02012-01-04 12:02:26 +000079 def rotation_filename(self, default_name):
80 """
81 Modify the filename of a log file when rotating.
82
83 This is provided so that a custom filename can be provided.
84
85 The default implementation calls the 'namer' attribute of the
86 handler, if it's callable, passing the default name to
87 it. If the attribute isn't callable (the default is None), the name
88 is returned unchanged.
89
90 :param default_name: The default name for the log file.
91 """
92 if not callable(self.namer):
93 result = default_name
94 else:
95 result = self.namer(default_name)
96 return result
97
98 def rotate(self, source, dest):
99 """
100 When rotating, rotate the current log.
101
102 The default implementation calls the 'rotator' attribute of the
103 handler, if it's callable, passing the source and dest arguments to
104 it. If the attribute isn't callable (the default is None), the source
105 is simply renamed to the destination.
106
107 :param source: The source filename. This is normally the base
108 filename, e.g. 'test.log'
109 :param dest: The destination filename. This is normally
110 what the source is rotated to, e.g. 'test.log.1'.
111 """
112 if not callable(self.rotator):
Vinay Sajipd8599262013-09-06 10:10:22 +0100113 # Issue 18940: A file may not have been created if delay is True.
114 if os.path.exists(source):
115 os.rename(source, dest)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000116 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 Sajipca7b5042019-06-17 17:40:52 +0100124 def __init__(self, filename, mode='a', maxBytes=0, backupCount=0,
125 encoding=None, delay=False, errors=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000126 """
127 Open the specified file and use it as the stream for logging.
128
129 By default, the file grows indefinitely. You can specify particular
130 values of maxBytes and backupCount to allow the file to rollover at
131 a predetermined size.
132
133 Rollover occurs whenever the current log file is nearly maxBytes in
134 length. If backupCount is >= 1, the system will successively create
135 new files with the same pathname as the base file, but with extensions
136 ".1", ".2" etc. appended to it. For example, with a backupCount of 5
137 and a base file name of "app.log", you would get "app.log",
138 "app.log.1", "app.log.2", ... through to "app.log.5". The file being
139 written to is always "app.log" - when it gets filled up, it is closed
140 and renamed to "app.log.1", and if files "app.log.1", "app.log.2" etc.
141 exist, then they are renamed to "app.log.2", "app.log.3" etc.
142 respectively.
143
144 If maxBytes is zero, rollover never occurs.
145 """
Vinay Sajip89994b22010-08-22 18:11:02 +0000146 # If rotation/rollover is wanted, it doesn't make sense to use another
147 # mode. If for example 'w' were specified, then if there were multiple
148 # runs of the calling application, the logs from previous runs would be
149 # lost if the 'w' is respected, because the log file would be truncated
150 # on each run.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000151 if maxBytes > 0:
Vinay Sajip89994b22010-08-22 18:11:02 +0000152 mode = 'a'
Inada Naokifb786922021-04-06 11:18:41 +0900153 if "b" not in mode:
154 encoding = io.text_encoding(encoding)
Vinay Sajipca7b5042019-06-17 17:40:52 +0100155 BaseRotatingHandler.__init__(self, filename, mode, encoding=encoding,
156 delay=delay, errors=errors)
Guido van Rossum57102f82002-11-13 16:15:58 +0000157 self.maxBytes = maxBytes
158 self.backupCount = backupCount
Guido van Rossum57102f82002-11-13 16:15:58 +0000159
160 def doRollover(self):
161 """
162 Do a rollover, as described in __init__().
163 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000164 if self.stream:
165 self.stream.close()
Vinay Sajip01241d62011-01-21 23:35:57 +0000166 self.stream = None
Guido van Rossum57102f82002-11-13 16:15:58 +0000167 if self.backupCount > 0:
168 for i in range(self.backupCount - 1, 0, -1):
Vinay Sajip23b94d02012-01-04 12:02:26 +0000169 sfn = self.rotation_filename("%s.%d" % (self.baseFilename, i))
170 dfn = self.rotation_filename("%s.%d" % (self.baseFilename,
171 i + 1))
Guido van Rossum57102f82002-11-13 16:15:58 +0000172 if os.path.exists(sfn):
Guido van Rossum57102f82002-11-13 16:15:58 +0000173 if os.path.exists(dfn):
174 os.remove(dfn)
175 os.rename(sfn, dfn)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000176 dfn = self.rotation_filename(self.baseFilename + ".1")
Guido van Rossum57102f82002-11-13 16:15:58 +0000177 if os.path.exists(dfn):
178 os.remove(dfn)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000179 self.rotate(self.baseFilename, dfn)
Vinay Sajip43c6ef12013-09-06 10:25:31 +0100180 if not self.delay:
181 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000182
Vinay Sajip17c52d82004-07-03 11:48:34 +0000183 def shouldRollover(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000184 """
Vinay Sajip17c52d82004-07-03 11:48:34 +0000185 Determine if rollover should occur.
Guido van Rossum57102f82002-11-13 16:15:58 +0000186
Vinay Sajip17c52d82004-07-03 11:48:34 +0000187 Basically, see if the supplied record would cause the file to exceed
188 the size limit we have.
Guido van Rossum57102f82002-11-13 16:15:58 +0000189 """
Miss Islington (bot)5aca34f2021-10-11 03:01:12 -0700190 # See bpo-45401: Never rollover anything other than regular files
191 if os.path.exists(self.baseFilename) and not os.path.isfile(self.baseFilename):
192 return False
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000193 if self.stream is None: # delay was set...
194 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000195 if self.maxBytes > 0: # are we rolling over?
196 msg = "%s\n" % self.format(record)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000197 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
Guido van Rossum57102f82002-11-13 16:15:58 +0000198 if self.stream.tell() + len(msg) >= self.maxBytes:
Miss Islington (bot)5aca34f2021-10-11 03:01:12 -0700199 return True
200 return False
Guido van Rossum57102f82002-11-13 16:15:58 +0000201
Vinay Sajip17c52d82004-07-03 11:48:34 +0000202class TimedRotatingFileHandler(BaseRotatingHandler):
203 """
204 Handler for logging to a file, rotating the log file at certain timed
205 intervals.
206
207 If backupCount is > 0, when rollover is done, no more than backupCount
208 files are kept - the oldest ones are deleted.
209 """
Vinay Sajipca7b5042019-06-17 17:40:52 +0100210 def __init__(self, filename, when='h', interval=1, backupCount=0,
211 encoding=None, delay=False, utc=False, atTime=None,
212 errors=None):
Inada Naokifb786922021-04-06 11:18:41 +0900213 encoding = io.text_encoding(encoding)
Vinay Sajipca7b5042019-06-17 17:40:52 +0100214 BaseRotatingHandler.__init__(self, filename, 'a', encoding=encoding,
215 delay=delay, errors=errors)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000216 self.when = when.upper()
Vinay Sajip17c52d82004-07-03 11:48:34 +0000217 self.backupCount = backupCount
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000218 self.utc = utc
Vinay Sajipa7130792013-04-12 17:04:23 +0100219 self.atTime = atTime
Vinay Sajip17c52d82004-07-03 11:48:34 +0000220 # Calculate the real rollover interval, which is just the number of
221 # seconds between rollovers. Also set the filename suffix used when
222 # a rollover occurs. Current 'when' events supported:
223 # S - Seconds
224 # M - Minutes
225 # H - Hours
226 # D - Days
227 # midnight - roll over at midnight
228 # W{0-6} - roll over on a certain day; 0 - Monday
229 #
230 # Case of the 'when' specifier is not important; lower or upper case
231 # will work.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000232 if self.when == 'S':
233 self.interval = 1 # one second
234 self.suffix = "%Y-%m-%d_%H-%M-%S"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000235 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000236 elif self.when == 'M':
237 self.interval = 60 # one minute
238 self.suffix = "%Y-%m-%d_%H-%M"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000239 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000240 elif self.when == 'H':
241 self.interval = 60 * 60 # one hour
242 self.suffix = "%Y-%m-%d_%H"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000243 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000244 elif self.when == 'D' or self.when == 'MIDNIGHT':
245 self.interval = 60 * 60 * 24 # one day
246 self.suffix = "%Y-%m-%d"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000247 self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000248 elif self.when.startswith('W'):
249 self.interval = 60 * 60 * 24 * 7 # one week
250 if len(self.when) != 2:
251 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
252 if self.when[1] < '0' or self.when[1] > '6':
253 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
254 self.dayOfWeek = int(self.when[1])
255 self.suffix = "%Y-%m-%d"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000256 self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000257 else:
258 raise ValueError("Invalid rollover interval specified: %s" % self.when)
259
Antoine Pitroufd036452008-08-19 17:56:33 +0000260 self.extMatch = re.compile(self.extMatch, re.ASCII)
Vinay Sajipe7d40662004-10-03 19:12:07 +0000261 self.interval = self.interval * interval # multiply by units requested
Vinay Sajip638e6222016-07-22 18:23:04 +0100262 # The following line added because the filename passed in could be a
263 # path object (see Issue #27493), but self.baseFilename will be a string
264 filename = self.baseFilename
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000265 if os.path.exists(filename):
266 t = os.stat(filename)[ST_MTIME]
267 else:
268 t = int(time.time())
269 self.rolloverAt = self.computeRollover(t)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000270
Vinay Sajipaffbd872009-06-11 10:11:47 +0000271 def computeRollover(self, currentTime):
272 """
273 Work out the rollover time based on the specified time.
274 """
275 result = currentTime + self.interval
Vinay Sajip17c52d82004-07-03 11:48:34 +0000276 # If we are rolling over at midnight or weekly, then the interval is already known.
277 # What we need to figure out is WHEN the next interval is. In other words,
278 # if you are rolling over at midnight, then your base interval is 1 day,
279 # but you want to start that one day clock at midnight, not now. So, we
280 # have to fudge the rolloverAt value in order to trigger the first rollover
281 # at the right time. After that, the regular interval will take care of
282 # the rest. Note that this code doesn't care about leap seconds. :)
283 if self.when == 'MIDNIGHT' or self.when.startswith('W'):
284 # This could be done with less code, but I wanted it to be clear
Vinay Sajipaffbd872009-06-11 10:11:47 +0000285 if self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000286 t = time.gmtime(currentTime)
287 else:
288 t = time.localtime(currentTime)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000289 currentHour = t[3]
290 currentMinute = t[4]
291 currentSecond = t[5]
Vinay Sajipa7130792013-04-12 17:04:23 +0100292 currentDay = t[6]
293 # r is the number of seconds left between now and the next rotation
294 if self.atTime is None:
295 rotate_ts = _MIDNIGHT
296 else:
297 rotate_ts = ((self.atTime.hour * 60 + self.atTime.minute)*60 +
298 self.atTime.second)
299
300 r = rotate_ts - ((currentHour * 60 + currentMinute) * 60 +
301 currentSecond)
302 if r < 0:
303 # Rotate time is before the current time (for example when
304 # self.rotateAt is 13:45 and it now 14:15), rotation is
305 # tomorrow.
306 r += _MIDNIGHT
307 currentDay = (currentDay + 1) % 7
Vinay Sajipaffbd872009-06-11 10:11:47 +0000308 result = currentTime + r
Vinay Sajip17c52d82004-07-03 11:48:34 +0000309 # If we are rolling over on a certain day, add in the number of days until
310 # the next rollover, but offset by 1 since we just calculated the time
311 # until the next day starts. There are three cases:
312 # Case 1) The day to rollover is today; in this case, do nothing
313 # Case 2) The day to rollover is further in the interval (i.e., today is
314 # day 2 (Wednesday) and rollover is on day 6 (Sunday). Days to
315 # next rollover is simply 6 - 2 - 1, or 3.
316 # Case 3) The day to rollover is behind us in the interval (i.e., today
317 # is day 5 (Saturday) and rollover is on day 3 (Thursday).
318 # Days to rollover is 6 - 5 + 3, or 4. In this case, it's the
319 # number of days left in the current week (1) plus the number
320 # of days in the next week until the rollover day (3).
Georg Brandl86def6c2008-01-21 20:36:10 +0000321 # The calculations described in 2) and 3) above need to have a day added.
322 # This is because the above time calculation takes us to midnight on this
323 # day, i.e. the start of the next day.
Vinay Sajipaffbd872009-06-11 10:11:47 +0000324 if self.when.startswith('W'):
Vinay Sajipa7130792013-04-12 17:04:23 +0100325 day = currentDay # 0 is Monday
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000326 if day != self.dayOfWeek:
327 if day < self.dayOfWeek:
Georg Brandl86def6c2008-01-21 20:36:10 +0000328 daysToWait = self.dayOfWeek - day
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000329 else:
Georg Brandl86def6c2008-01-21 20:36:10 +0000330 daysToWait = 6 - day + self.dayOfWeek + 1
Vinay Sajipaffbd872009-06-11 10:11:47 +0000331 newRolloverAt = result + (daysToWait * (60 * 60 * 24))
332 if not self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000333 dstNow = t[-1]
334 dstAtRollover = time.localtime(newRolloverAt)[-1]
335 if dstNow != dstAtRollover:
336 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000337 addend = -3600
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000338 else: # DST bows out before next rollover, so we need to add an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000339 addend = 3600
340 newRolloverAt += addend
Vinay Sajipaffbd872009-06-11 10:11:47 +0000341 result = newRolloverAt
342 return result
Vinay Sajip17c52d82004-07-03 11:48:34 +0000343
344 def shouldRollover(self, record):
345 """
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000346 Determine if rollover should occur.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000347
348 record is not used, as we are just comparing times, but it is needed so
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000349 the method signatures are the same
Vinay Sajip17c52d82004-07-03 11:48:34 +0000350 """
Miss Islington (bot)5aca34f2021-10-11 03:01:12 -0700351 # See bpo-45401: Never rollover anything other than regular files
352 if os.path.exists(self.baseFilename) and not os.path.isfile(self.baseFilename):
353 return False
Vinay Sajip17c52d82004-07-03 11:48:34 +0000354 t = int(time.time())
355 if t >= self.rolloverAt:
Miss Islington (bot)5aca34f2021-10-11 03:01:12 -0700356 return True
357 return False
Vinay Sajip17c52d82004-07-03 11:48:34 +0000358
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000359 def getFilesToDelete(self):
360 """
361 Determine the files to delete when rolling over.
362
363 More specific than the earlier method, which just used glob.glob().
364 """
365 dirName, baseName = os.path.split(self.baseFilename)
366 fileNames = os.listdir(dirName)
367 result = []
Miss Islington (bot)6e6dc252021-07-30 09:20:59 -0700368 # See bpo-44753: Don't use the extension when computing the prefix.
369 prefix = os.path.splitext(baseName)[0] + "."
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000370 plen = len(prefix)
371 for fileName in fileNames:
372 if fileName[:plen] == prefix:
373 suffix = fileName[plen:]
374 if self.extMatch.match(suffix):
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000375 result.append(os.path.join(dirName, fileName))
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000376 if len(result) < self.backupCount:
377 result = []
378 else:
Lovesh Harchandaniafad1472017-10-27 09:04:33 +0200379 result.sort()
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000380 result = result[:len(result) - self.backupCount]
381 return result
382
Vinay Sajip17c52d82004-07-03 11:48:34 +0000383 def doRollover(self):
384 """
385 do a rollover; in this case, a date/time stamp is appended to the filename
386 when the rollover happens. However, you want the file to be named for the
387 start of the interval, not the current time. If there is a backup count,
388 then we have to get a list of matching filenames, sort them and remove
389 the one with the oldest suffix.
390 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000391 if self.stream:
392 self.stream.close()
Vinay Sajip01241d62011-01-21 23:35:57 +0000393 self.stream = None
Vinay Sajip17c52d82004-07-03 11:48:34 +0000394 # get the time that this sequence started at and make it a TimeTuple
Vinay Sajip27f48972012-03-13 12:10:33 +0000395 currentTime = int(time.time())
396 dstNow = time.localtime(currentTime)[-1]
Vinay Sajip17c52d82004-07-03 11:48:34 +0000397 t = self.rolloverAt - self.interval
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000398 if self.utc:
399 timeTuple = time.gmtime(t)
400 else:
401 timeTuple = time.localtime(t)
Vinay Sajip27f48972012-03-13 12:10:33 +0000402 dstThen = timeTuple[-1]
403 if dstNow != dstThen:
404 if dstNow:
405 addend = 3600
406 else:
407 addend = -3600
408 timeTuple = time.localtime(t + addend)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000409 dfn = self.rotation_filename(self.baseFilename + "." +
410 time.strftime(self.suffix, timeTuple))
Vinay Sajip17c52d82004-07-03 11:48:34 +0000411 if os.path.exists(dfn):
412 os.remove(dfn)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000413 self.rotate(self.baseFilename, dfn)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000414 if self.backupCount > 0:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000415 for s in self.getFilesToDelete():
416 os.remove(s)
Vinay Sajip43c6ef12013-09-06 10:25:31 +0100417 if not self.delay:
418 self.stream = self._open()
Vinay Sajipaffbd872009-06-11 10:11:47 +0000419 newRolloverAt = self.computeRollover(currentTime)
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000420 while newRolloverAt <= currentTime:
421 newRolloverAt = newRolloverAt + self.interval
422 #If DST changes and midnight or weekly rollover, adjust for this.
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000423 if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000424 dstAtRollover = time.localtime(newRolloverAt)[-1]
425 if dstNow != dstAtRollover:
426 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000427 addend = -3600
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000428 else: # DST bows out before next rollover, so we need to add an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000429 addend = 3600
430 newRolloverAt += addend
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000431 self.rolloverAt = newRolloverAt
Guido van Rossum57102f82002-11-13 16:15:58 +0000432
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000433class WatchedFileHandler(logging.FileHandler):
434 """
435 A handler for logging to a file, which watches the file
436 to see if it has changed while in use. This can happen because of
437 usage of programs such as newsyslog and logrotate which perform
438 log file rotation. This handler, intended for use under Unix,
439 watches the file to see if it has changed since the last emit.
440 (A file has changed if its device or inode have changed.)
441 If it has changed, the old file stream is closed, and the file
442 opened to get a new stream.
443
444 This handler is not appropriate for use under Windows, because
445 under Windows open files cannot be moved or renamed - logging
446 opens the files with exclusive locks - and so there is no need
447 for such a handler. Furthermore, ST_INO is not supported under
448 Windows; stat always returns zero for this value.
449
450 This handler is based on a suggestion and patch by Chad J.
451 Schroeder.
452 """
Vinay Sajipca7b5042019-06-17 17:40:52 +0100453 def __init__(self, filename, mode='a', encoding=None, delay=False,
454 errors=None):
Inada Naokifb786922021-04-06 11:18:41 +0900455 if "b" not in mode:
456 encoding = io.text_encoding(encoding)
Vinay Sajipca7b5042019-06-17 17:40:52 +0100457 logging.FileHandler.__init__(self, filename, mode=mode,
458 encoding=encoding, delay=delay,
459 errors=errors)
Vinay Sajip66b8b082012-04-24 23:25:30 +0100460 self.dev, self.ino = -1, -1
461 self._statstream()
462
463 def _statstream(self):
464 if self.stream:
465 sres = os.fstat(self.stream.fileno())
466 self.dev, self.ino = sres[ST_DEV], sres[ST_INO]
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000467
Vinay Sajip29a14452015-10-01 20:54:41 +0100468 def reopenIfNeeded(self):
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000469 """
Vinay Sajip29a14452015-10-01 20:54:41 +0100470 Reopen log file if needed.
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000471
Vinay Sajip29a14452015-10-01 20:54:41 +0100472 Checks if the underlying file has changed, and if it
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000473 has, close the old stream and reopen the file to get the
474 current stream.
475 """
Vinay Sajip66b8b082012-04-24 23:25:30 +0100476 # Reduce the chance of race conditions by stat'ing by path only
477 # once and then fstat'ing our new fd if we opened a new log stream.
478 # See issue #14632: Thanks to John Mulligan for the problem report
479 # and patch.
480 try:
481 # stat the file by path, checking for existence
482 sres = os.stat(self.baseFilename)
Giampaolo Rodola'0166a282013-02-12 15:14:17 +0100483 except FileNotFoundError:
484 sres = None
Vinay Sajip66b8b082012-04-24 23:25:30 +0100485 # compare file system stat with that of our stream file handle
486 if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
487 if self.stream is not None:
488 # we have an open file handle, clean it up
489 self.stream.flush()
490 self.stream.close()
Vinay Sajip02200482014-06-14 10:22:05 +0100491 self.stream = None # See Issue #21742: _open () might fail.
Vinay Sajip66b8b082012-04-24 23:25:30 +0100492 # open a new file handle and get new stat info from that fd
493 self.stream = self._open()
494 self._statstream()
Vinay Sajip29a14452015-10-01 20:54:41 +0100495
496 def emit(self, record):
497 """
498 Emit a record.
499
500 If underlying file has changed, reopen the file before emitting the
501 record to it.
502 """
503 self.reopenIfNeeded()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000504 logging.FileHandler.emit(self, record)
505
Vinay Sajip66b8b082012-04-24 23:25:30 +0100506
Guido van Rossum57102f82002-11-13 16:15:58 +0000507class SocketHandler(logging.Handler):
508 """
509 A handler class which writes logging records, in pickle format, to
510 a streaming socket. The socket is kept open across logging calls.
511 If the peer resets it, an attempt is made to reconnect on the next call.
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000512 The pickle which is sent is that of the LogRecord's attribute dictionary
513 (__dict__), so that the receiver does not need to have the logging module
514 installed in order to process the logging event.
515
516 To unpickle the record at the receiving end into a LogRecord, use the
517 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000518 """
519
520 def __init__(self, host, port):
521 """
522 Initializes the handler with a specific host address and port.
523
Vinay Sajipde19e082011-04-30 21:52:26 +0100524 When the attribute *closeOnError* is set to True - if a socket error
525 occurs, the socket is silently closed and then reopened on the next
526 logging call.
Guido van Rossum57102f82002-11-13 16:15:58 +0000527 """
528 logging.Handler.__init__(self)
529 self.host = host
530 self.port = port
Vinay Sajip5421f352013-09-27 18:18:28 +0100531 if port is None:
532 self.address = host
533 else:
534 self.address = (host, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000535 self.sock = None
Vinay Sajipde19e082011-04-30 21:52:26 +0100536 self.closeOnError = False
Vinay Sajip48cfe382004-02-20 13:17:27 +0000537 self.retryTime = None
538 #
539 # Exponential backoff parameters.
540 #
541 self.retryStart = 1.0
542 self.retryMax = 30.0
543 self.retryFactor = 2.0
Guido van Rossum57102f82002-11-13 16:15:58 +0000544
Guido van Rossumd8faa362007-04-27 19:54:29 +0000545 def makeSocket(self, timeout=1):
Guido van Rossum57102f82002-11-13 16:15:58 +0000546 """
547 A factory method which allows subclasses to define the precise
548 type of socket they want.
549 """
Vinay Sajip5421f352013-09-27 18:18:28 +0100550 if self.port is not None:
551 result = socket.create_connection(self.address, timeout=timeout)
552 else:
553 result = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
554 result.settimeout(timeout)
Vinay Sajip38c741c2013-10-06 18:36:00 +0100555 try:
556 result.connect(self.address)
557 except OSError:
558 result.close() # Issue 19182
559 raise
Vinay Sajip5421f352013-09-27 18:18:28 +0100560 return result
Guido van Rossum57102f82002-11-13 16:15:58 +0000561
Vinay Sajip48cfe382004-02-20 13:17:27 +0000562 def createSocket(self):
563 """
564 Try to create a socket, using an exponential backoff with
565 a max retry time. Thanks to Robert Olson for the original patch
566 (SF #815911) which has been slightly refactored.
567 """
568 now = time.time()
569 # Either retryTime is None, in which case this
570 # is the first time back after a disconnect, or
571 # we've waited long enough.
572 if self.retryTime is None:
Vinay Sajipde19e082011-04-30 21:52:26 +0100573 attempt = True
Vinay Sajip48cfe382004-02-20 13:17:27 +0000574 else:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000575 attempt = (now >= self.retryTime)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000576 if attempt:
577 try:
578 self.sock = self.makeSocket()
579 self.retryTime = None # next time, no delay before trying
Andrew Svetlov0832af62012-12-18 23:10:48 +0200580 except OSError:
Vinay Sajip48cfe382004-02-20 13:17:27 +0000581 #Creation failed, so set the retry time and return.
582 if self.retryTime is None:
583 self.retryPeriod = self.retryStart
584 else:
585 self.retryPeriod = self.retryPeriod * self.retryFactor
586 if self.retryPeriod > self.retryMax:
587 self.retryPeriod = self.retryMax
588 self.retryTime = now + self.retryPeriod
589
Guido van Rossum57102f82002-11-13 16:15:58 +0000590 def send(self, s):
591 """
592 Send a pickled string to the socket.
593
594 This function allows for partial sends which can happen when the
595 network is busy.
596 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000597 if self.sock is None:
598 self.createSocket()
599 #self.sock can be None either because we haven't reached the retry
600 #time yet, or because we have reached the retry time and retried,
601 #but are still unable to connect.
602 if self.sock:
603 try:
Giampaolo Rodola'f9de90a2012-10-26 18:32:24 +0200604 self.sock.sendall(s)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200605 except OSError: #pragma: no cover
Vinay Sajip48cfe382004-02-20 13:17:27 +0000606 self.sock.close()
607 self.sock = None # so we can call createSocket next time
Guido van Rossum57102f82002-11-13 16:15:58 +0000608
609 def makePickle(self, record):
610 """
611 Pickles the record in binary format with a length prefix, and
612 returns it ready for transmission across the socket.
613 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000614 ei = record.exc_info
615 if ei:
Vinay Sajip6f5e54e2012-03-29 20:17:18 +0100616 # just to get traceback text into record.exc_text ...
617 dummy = self.format(record)
618 # See issue #14436: If msg or args are objects, they may not be
619 # available on the receiving end. So we convert the msg % args
620 # to a string, save it as msg and zap the args.
621 d = dict(record.__dict__)
622 d['msg'] = record.getMessage()
623 d['args'] = None
624 d['exc_info'] = None
Vinay Sajip9cdfd182015-12-26 12:48:44 +0000625 # Issue #25685: delete 'message' if present: redundant with 'msg'
626 d.pop('message', None)
Vinay Sajip6f5e54e2012-03-29 20:17:18 +0100627 s = pickle.dumps(d, 1)
Guido van Rossum57102f82002-11-13 16:15:58 +0000628 slen = struct.pack(">L", len(s))
629 return slen + s
630
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000631 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000632 """
633 Handle an error during logging.
634
635 An error has occurred during logging. Most likely cause -
636 connection lost. Close the socket so that we can retry on the
637 next event.
638 """
639 if self.closeOnError and self.sock:
640 self.sock.close()
641 self.sock = None #try to reconnect next time
642 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000643 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000644
645 def emit(self, record):
646 """
647 Emit a record.
648
649 Pickles the record and writes it to the socket in binary format.
650 If there is an error with the socket, silently drop the packet.
651 If there was a problem with the socket, re-establishes the
652 socket.
653 """
654 try:
655 s = self.makePickle(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000656 self.send(s)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100657 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000658 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000659
660 def close(self):
661 """
662 Closes the socket.
663 """
Vinay Sajipf0509032012-02-23 20:49:08 +0000664 self.acquire()
665 try:
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300666 sock = self.sock
667 if sock:
Vinay Sajip0abf61d2012-02-23 19:45:52 +0000668 self.sock = None
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300669 sock.close()
Vinay Sajip0abf61d2012-02-23 19:45:52 +0000670 logging.Handler.close(self)
Vinay Sajipf0509032012-02-23 20:49:08 +0000671 finally:
672 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +0000673
674class DatagramHandler(SocketHandler):
675 """
676 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000677 a datagram socket. The pickle which is sent is that of the LogRecord's
678 attribute dictionary (__dict__), so that the receiver does not need to
679 have the logging module installed in order to process the logging event.
680
681 To unpickle the record at the receiving end into a LogRecord, use the
682 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000683
684 """
685 def __init__(self, host, port):
686 """
687 Initializes the handler with a specific host address and port.
688 """
689 SocketHandler.__init__(self, host, port)
Vinay Sajipde19e082011-04-30 21:52:26 +0100690 self.closeOnError = False
Guido van Rossum57102f82002-11-13 16:15:58 +0000691
692 def makeSocket(self):
693 """
694 The factory method of SocketHandler is here overridden to create
695 a UDP socket (SOCK_DGRAM).
696 """
Vinay Sajip5421f352013-09-27 18:18:28 +0100697 if self.port is None:
698 family = socket.AF_UNIX
699 else:
700 family = socket.AF_INET
701 s = socket.socket(family, socket.SOCK_DGRAM)
Guido van Rossum57102f82002-11-13 16:15:58 +0000702 return s
703
704 def send(self, s):
705 """
706 Send a pickled string to a socket.
707
708 This function no longer allows for partial sends which can happen
709 when the network is busy - UDP does not guarantee delivery and
710 can deliver packets out of sequence.
711 """
Vinay Sajipfb154172004-08-24 09:36:23 +0000712 if self.sock is None:
713 self.createSocket()
Vinay Sajip5421f352013-09-27 18:18:28 +0100714 self.sock.sendto(s, self.address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000715
716class SysLogHandler(logging.Handler):
717 """
718 A handler class which sends formatted logging records to a syslog
719 server. Based on Sam Rushing's syslog module:
720 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
721 Contributed by Nicolas Untz (after which minor refactoring changes
722 have been made).
723 """
724
725 # from <linux/sys/syslog.h>:
726 # ======================================================================
727 # priorities/facilities are encoded into a single 32-bit quantity, where
728 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
729 # facility (0-big number). Both the priorities and the facilities map
730 # roughly one-to-one to strings in the syslogd(8) source code. This
731 # mapping is included in this file.
732 #
733 # priorities (these are ordered)
734
735 LOG_EMERG = 0 # system is unusable
736 LOG_ALERT = 1 # action must be taken immediately
737 LOG_CRIT = 2 # critical conditions
738 LOG_ERR = 3 # error conditions
739 LOG_WARNING = 4 # warning conditions
740 LOG_NOTICE = 5 # normal but significant condition
741 LOG_INFO = 6 # informational
742 LOG_DEBUG = 7 # debug-level messages
743
744 # facility codes
745 LOG_KERN = 0 # kernel messages
746 LOG_USER = 1 # random user-level messages
747 LOG_MAIL = 2 # mail system
748 LOG_DAEMON = 3 # system daemons
749 LOG_AUTH = 4 # security/authorization messages
750 LOG_SYSLOG = 5 # messages generated internally by syslogd
751 LOG_LPR = 6 # line printer subsystem
752 LOG_NEWS = 7 # network news subsystem
753 LOG_UUCP = 8 # UUCP subsystem
754 LOG_CRON = 9 # clock daemon
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000755 LOG_AUTHPRIV = 10 # security/authorization messages (private)
756 LOG_FTP = 11 # FTP daemon
Vinay Sajipce545192020-01-10 19:37:48 +0000757 LOG_NTP = 12 # NTP subsystem
758 LOG_SECURITY = 13 # Log audit
759 LOG_CONSOLE = 14 # Log alert
760 LOG_SOLCRON = 15 # Scheduling daemon (Solaris)
Guido van Rossum57102f82002-11-13 16:15:58 +0000761
762 # other codes through 15 reserved for system use
763 LOG_LOCAL0 = 16 # reserved for local use
764 LOG_LOCAL1 = 17 # reserved for local use
765 LOG_LOCAL2 = 18 # reserved for local use
766 LOG_LOCAL3 = 19 # reserved for local use
767 LOG_LOCAL4 = 20 # reserved for local use
768 LOG_LOCAL5 = 21 # reserved for local use
769 LOG_LOCAL6 = 22 # reserved for local use
770 LOG_LOCAL7 = 23 # reserved for local use
771
772 priority_names = {
773 "alert": LOG_ALERT,
774 "crit": LOG_CRIT,
775 "critical": LOG_CRIT,
776 "debug": LOG_DEBUG,
777 "emerg": LOG_EMERG,
778 "err": LOG_ERR,
779 "error": LOG_ERR, # DEPRECATED
780 "info": LOG_INFO,
781 "notice": LOG_NOTICE,
782 "panic": LOG_EMERG, # DEPRECATED
783 "warn": LOG_WARNING, # DEPRECATED
784 "warning": LOG_WARNING,
785 }
786
787 facility_names = {
Vinay Sajipce545192020-01-10 19:37:48 +0000788 "auth": LOG_AUTH,
789 "authpriv": LOG_AUTHPRIV,
790 "console": LOG_CONSOLE,
791 "cron": LOG_CRON,
792 "daemon": LOG_DAEMON,
793 "ftp": LOG_FTP,
794 "kern": LOG_KERN,
795 "lpr": LOG_LPR,
796 "mail": LOG_MAIL,
797 "news": LOG_NEWS,
798 "ntp": LOG_NTP,
799 "security": LOG_SECURITY,
800 "solaris-cron": LOG_SOLCRON,
801 "syslog": LOG_SYSLOG,
802 "user": LOG_USER,
803 "uucp": LOG_UUCP,
804 "local0": LOG_LOCAL0,
805 "local1": LOG_LOCAL1,
806 "local2": LOG_LOCAL2,
807 "local3": LOG_LOCAL3,
808 "local4": LOG_LOCAL4,
809 "local5": LOG_LOCAL5,
810 "local6": LOG_LOCAL6,
811 "local7": LOG_LOCAL7,
Guido van Rossum57102f82002-11-13 16:15:58 +0000812 }
813
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000814 #The map below appears to be trivially lowercasing the key. However,
815 #there's more to it than meets the eye - in some locales, lowercasing
816 #gives unexpected results. See SF #1524081: in the Turkish locale,
817 #"INFO".lower() != "info"
818 priority_map = {
819 "DEBUG" : "debug",
820 "INFO" : "info",
821 "WARNING" : "warning",
822 "ERROR" : "error",
823 "CRITICAL" : "critical"
824 }
825
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000826 def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
Vinay Sajipe9170522013-04-22 10:07:40 +0100827 facility=LOG_USER, socktype=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000828 """
829 Initialize a handler.
830
Guido van Rossume7ba4952007-06-06 23:52:48 +0000831 If address is specified as a string, a UNIX socket is used. To log to a
832 local syslogd, "SysLogHandler(address="/dev/log")" can be used.
Vinay Sajip40589f42013-04-22 10:14:12 +0100833 If facility is not specified, LOG_USER is used. If socktype is
834 specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
835 socket type will be used. For Unix sockets, you can also specify a
836 socktype of None, in which case socket.SOCK_DGRAM will be used, falling
837 back to socket.SOCK_STREAM.
Guido van Rossum57102f82002-11-13 16:15:58 +0000838 """
839 logging.Handler.__init__(self)
840
841 self.address = address
842 self.facility = facility
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000843 self.socktype = socktype
844
Guido van Rossum13257902007-06-07 23:15:56 +0000845 if isinstance(address, str):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100846 self.unixsocket = True
Коренберг Марк1b038e02017-03-17 20:25:05 +0500847 # Syslog server may be unavailable during handler initialisation.
848 # C's openlog() function also ignores connection errors.
849 # Moreover, we ignore these errors while logging, so it not worse
850 # to ignore it also here.
851 try:
852 self._connect_unixsocket(address)
853 except OSError:
854 pass
Guido van Rossum57102f82002-11-13 16:15:58 +0000855 else:
Vinay Sajip5a35b062011-04-27 11:31:14 +0100856 self.unixsocket = False
Vinay Sajipe9170522013-04-22 10:07:40 +0100857 if socktype is None:
858 socktype = socket.SOCK_DGRAM
Xiang Zhang0b4b57d2017-06-01 21:11:56 +0800859 host, port = address
860 ress = socket.getaddrinfo(host, port, 0, socktype)
861 if not ress:
862 raise OSError("getaddrinfo returns an empty list")
863 for res in ress:
864 af, socktype, proto, _, sa = res
865 err = sock = None
866 try:
867 sock = socket.socket(af, socktype, proto)
868 if socktype == socket.SOCK_STREAM:
869 sock.connect(sa)
870 break
871 except OSError as exc:
872 err = exc
873 if sock is not None:
874 sock.close()
875 if err is not None:
876 raise err
877 self.socket = sock
Vinay Sajipe9170522013-04-22 10:07:40 +0100878 self.socktype = socktype
Guido van Rossum57102f82002-11-13 16:15:58 +0000879
Vinay Sajipa1974c12005-01-13 08:23:56 +0000880 def _connect_unixsocket(self, address):
Vinay Sajipe9170522013-04-22 10:07:40 +0100881 use_socktype = self.socktype
882 if use_socktype is None:
883 use_socktype = socket.SOCK_DGRAM
884 self.socket = socket.socket(socket.AF_UNIX, use_socktype)
Vinay Sajipa1974c12005-01-13 08:23:56 +0000885 try:
886 self.socket.connect(address)
Vinay Sajipe9170522013-04-22 10:07:40 +0100887 # it worked, so set self.socktype to the used type
888 self.socktype = use_socktype
Andrew Svetlov0832af62012-12-18 23:10:48 +0200889 except OSError:
Vinay Sajipa1974c12005-01-13 08:23:56 +0000890 self.socket.close()
Vinay Sajipe9170522013-04-22 10:07:40 +0100891 if self.socktype is not None:
892 # user didn't specify falling back, so fail
893 raise
894 use_socktype = socket.SOCK_STREAM
895 self.socket = socket.socket(socket.AF_UNIX, use_socktype)
896 try:
897 self.socket.connect(address)
898 # it worked, so set self.socktype to the used type
899 self.socktype = use_socktype
Vinay Sajip40589f42013-04-22 10:14:12 +0100900 except OSError:
Vinay Sajipe9170522013-04-22 10:07:40 +0100901 self.socket.close()
902 raise
Vinay Sajipa1974c12005-01-13 08:23:56 +0000903
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000904 def encodePriority(self, facility, priority):
Guido van Rossum57102f82002-11-13 16:15:58 +0000905 """
906 Encode the facility and priority. You can pass in strings or
907 integers - if strings are passed, the facility_names and
908 priority_names mapping dictionaries are used to convert them to
909 integers.
910 """
Guido van Rossum13257902007-06-07 23:15:56 +0000911 if isinstance(facility, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000912 facility = self.facility_names[facility]
Guido van Rossum13257902007-06-07 23:15:56 +0000913 if isinstance(priority, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000914 priority = self.priority_names[priority]
915 return (facility << 3) | priority
916
Xiang Zhang0b4b57d2017-06-01 21:11:56 +0800917 def close(self):
Guido van Rossum57102f82002-11-13 16:15:58 +0000918 """
919 Closes the socket.
920 """
Vinay Sajipf0509032012-02-23 20:49:08 +0000921 self.acquire()
922 try:
Vinay Sajip57c22372012-02-23 20:03:49 +0000923 self.socket.close()
Vinay Sajip0abf61d2012-02-23 19:45:52 +0000924 logging.Handler.close(self)
Vinay Sajipf0509032012-02-23 20:49:08 +0000925 finally:
926 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +0000927
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000928 def mapPriority(self, levelName):
929 """
930 Map a logging level name to a key in the priority_names map.
931 This is useful in two scenarios: when custom levels are being
932 used, and in the case where you can't do a straightforward
933 mapping by lowercasing the logging level name because of locale-
934 specific issues (see SF #1524081).
935 """
936 return self.priority_map.get(levelName, "warning")
937
Vinay Sajip2353e352011-06-27 15:40:06 +0100938 ident = '' # prepended to all messages
Vinay Sajip8168d102011-06-09 16:50:49 +0100939 append_nul = True # some old syslog daemons expect a NUL terminator
940
Guido van Rossum57102f82002-11-13 16:15:58 +0000941 def emit(self, record):
942 """
943 Emit a record.
944
945 The record is formatted, and then sent to the syslog server. If
946 exception information is present, it is NOT sent to the server.
947 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000948 try:
Vinay Sajipc33a0cc2014-11-01 19:58:47 +0000949 msg = self.format(record)
950 if self.ident:
951 msg = self.ident + msg
952 if self.append_nul:
953 msg += '\000'
954
955 # We need to convert record level to lowercase, maybe this will
956 # change in the future.
957 prio = '<%d>' % self.encodePriority(self.facility,
958 self.mapPriority(record.levelname))
959 prio = prio.encode('utf-8')
960 # Message is a string. Convert to bytes as required by RFC 5424
961 msg = msg.encode('utf-8')
962 msg = prio + msg
Guido van Rossum57102f82002-11-13 16:15:58 +0000963 if self.unixsocket:
Vinay Sajipa1974c12005-01-13 08:23:56 +0000964 try:
965 self.socket.send(msg)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200966 except OSError:
Vinay Sajip862b15e2013-05-16 22:57:02 +0100967 self.socket.close()
Vinay Sajipa1974c12005-01-13 08:23:56 +0000968 self._connect_unixsocket(self.address)
969 self.socket.send(msg)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000970 elif self.socktype == socket.SOCK_DGRAM:
Guido van Rossum57102f82002-11-13 16:15:58 +0000971 self.socket.sendto(msg, self.address)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000972 else:
973 self.socket.sendall(msg)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100974 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000975 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000976
977class SMTPHandler(logging.Handler):
978 """
979 A handler class which sends an SMTP email for each logging event.
980 """
Vinay Sajip540f2152009-12-06 17:57:11 +0000981 def __init__(self, mailhost, fromaddr, toaddrs, subject,
Vinay Sajip26308da2012-04-22 18:16:14 +0100982 credentials=None, secure=None, timeout=5.0):
Guido van Rossum57102f82002-11-13 16:15:58 +0000983 """
984 Initialize the handler.
985
986 Initialize the instance with the from and to addresses and subject
987 line of the email. To specify a non-standard SMTP port, use the
Guido van Rossum360e4b82007-05-14 22:51:27 +0000988 (host, port) tuple format for the mailhost argument. To specify
989 authentication credentials, supply a (username, password) tuple
Vinay Sajip25fcd222009-12-06 18:05:04 +0000990 for the credentials argument. To specify the use of a secure
991 protocol (TLS), pass in a tuple for the secure argument. This will
992 only be used when authentication credentials are supplied. The tuple
993 will be either an empty tuple, or a single-value tuple with the name
994 of a keyfile, or a 2-value tuple with the names of the keyfile and
995 certificate file. (This tuple is passed to the `starttls` method).
Vinay Sajip17160fd2012-03-15 12:02:08 +0000996 A timeout in seconds can be specified for the SMTP connection (the
997 default is one second).
Guido van Rossum57102f82002-11-13 16:15:58 +0000998 """
999 logging.Handler.__init__(self)
Vinay Sajip16c41ab2014-10-17 08:49:38 +01001000 if isinstance(mailhost, (list, tuple)):
Guido van Rossum360e4b82007-05-14 22:51:27 +00001001 self.mailhost, self.mailport = mailhost
Guido van Rossum57102f82002-11-13 16:15:58 +00001002 else:
Guido van Rossum360e4b82007-05-14 22:51:27 +00001003 self.mailhost, self.mailport = mailhost, None
Vinay Sajip16c41ab2014-10-17 08:49:38 +01001004 if isinstance(credentials, (list, tuple)):
Guido van Rossum360e4b82007-05-14 22:51:27 +00001005 self.username, self.password = credentials
1006 else:
1007 self.username = None
Guido van Rossum57102f82002-11-13 16:15:58 +00001008 self.fromaddr = fromaddr
Guido van Rossum13257902007-06-07 23:15:56 +00001009 if isinstance(toaddrs, str):
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001010 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +00001011 self.toaddrs = toaddrs
1012 self.subject = subject
Vinay Sajip540f2152009-12-06 17:57:11 +00001013 self.secure = secure
Vinay Sajip17160fd2012-03-15 12:02:08 +00001014 self.timeout = timeout
Guido van Rossum57102f82002-11-13 16:15:58 +00001015
1016 def getSubject(self, record):
1017 """
1018 Determine the subject for the email.
1019
1020 If you want to specify a subject line which is record-dependent,
1021 override this method.
1022 """
1023 return self.subject
1024
1025 def emit(self, record):
1026 """
1027 Emit a record.
1028
1029 Format the record and send it to the specified addressees.
1030 """
1031 try:
1032 import smtplib
Vinay Sajip277640a2015-10-17 16:13:10 +01001033 from email.message import EmailMessage
1034 import email.utils
1035
Guido van Rossum57102f82002-11-13 16:15:58 +00001036 port = self.mailport
1037 if not port:
1038 port = smtplib.SMTP_PORT
Vinay Sajip17160fd2012-03-15 12:02:08 +00001039 smtp = smtplib.SMTP(self.mailhost, port, timeout=self.timeout)
Vinay Sajip277640a2015-10-17 16:13:10 +01001040 msg = EmailMessage()
1041 msg['From'] = self.fromaddr
1042 msg['To'] = ','.join(self.toaddrs)
1043 msg['Subject'] = self.getSubject(record)
1044 msg['Date'] = email.utils.localtime()
1045 msg.set_content(self.format(record))
Guido van Rossum360e4b82007-05-14 22:51:27 +00001046 if self.username:
Vinay Sajip25fcd222009-12-06 18:05:04 +00001047 if self.secure is not None:
Vinay Sajip540f2152009-12-06 17:57:11 +00001048 smtp.ehlo()
Vinay Sajip25fcd222009-12-06 18:05:04 +00001049 smtp.starttls(*self.secure)
Vinay Sajip540f2152009-12-06 17:57:11 +00001050 smtp.ehlo()
Guido van Rossum360e4b82007-05-14 22:51:27 +00001051 smtp.login(self.username, self.password)
Vinay Sajip277640a2015-10-17 16:13:10 +01001052 smtp.send_message(msg)
Guido van Rossum57102f82002-11-13 16:15:58 +00001053 smtp.quit()
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001054 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001055 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001056
1057class NTEventLogHandler(logging.Handler):
1058 """
1059 A handler class which sends events to the NT Event Log. Adds a
1060 registry entry for the specified application name. If no dllname is
1061 provided, win32service.pyd (which contains some basic message
1062 placeholders) is used. Note that use of these placeholders will make
1063 your event logs big, as the entire message source is held in the log.
1064 If you want slimmer logs, you have to pass in the name of your own DLL
1065 which contains the message definitions you want to use in the event log.
1066 """
1067 def __init__(self, appname, dllname=None, logtype="Application"):
1068 logging.Handler.__init__(self)
1069 try:
1070 import win32evtlogutil, win32evtlog
1071 self.appname = appname
1072 self._welu = win32evtlogutil
1073 if not dllname:
1074 dllname = os.path.split(self._welu.__file__)
1075 dllname = os.path.split(dllname[0])
1076 dllname = os.path.join(dllname[0], r'win32service.pyd')
1077 self.dllname = dllname
1078 self.logtype = logtype
1079 self._welu.AddSourceToRegistry(appname, dllname, logtype)
1080 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
1081 self.typemap = {
1082 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
1083 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001084 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +00001085 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
1086 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
1087 }
Brett Cannoncd171c82013-07-04 17:43:24 -04001088 except ImportError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001089 print("The Python Win32 extensions for NT (service, event "\
1090 "logging) appear not to be available.")
Guido van Rossum57102f82002-11-13 16:15:58 +00001091 self._welu = None
1092
1093 def getMessageID(self, record):
1094 """
1095 Return the message ID for the event record. If you are using your
1096 own messages, you could do this by having the msg passed to the
1097 logger being an ID rather than a formatting string. Then, in here,
1098 you could use a dictionary lookup to get the message ID. This
1099 version returns 1, which is the base message ID in win32service.pyd.
1100 """
1101 return 1
1102
1103 def getEventCategory(self, record):
1104 """
1105 Return the event category for the record.
1106
1107 Override this if you want to specify your own categories. This version
1108 returns 0.
1109 """
1110 return 0
1111
1112 def getEventType(self, record):
1113 """
1114 Return the event type for the record.
1115
1116 Override this if you want to specify your own types. This version does
1117 a mapping using the handler's typemap attribute, which is set up in
1118 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001119 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +00001120 either need to override this method or place a suitable dictionary in
1121 the handler's typemap attribute.
1122 """
1123 return self.typemap.get(record.levelno, self.deftype)
1124
1125 def emit(self, record):
1126 """
1127 Emit a record.
1128
1129 Determine the message ID, event category and event type. Then
1130 log the message in the NT event log.
1131 """
1132 if self._welu:
1133 try:
1134 id = self.getMessageID(record)
1135 cat = self.getEventCategory(record)
1136 type = self.getEventType(record)
1137 msg = self.format(record)
1138 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001139 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001140 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001141
1142 def close(self):
1143 """
1144 Clean up this handler.
1145
1146 You can remove the application name from the registry as a
1147 source of event log entries. However, if you do this, you will
1148 not be able to see the events as you intended in the Event Log
1149 Viewer - it needs to be able to access the registry to get the
1150 DLL name.
1151 """
1152 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
Vinay Sajip48cfe382004-02-20 13:17:27 +00001153 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +00001154
1155class HTTPHandler(logging.Handler):
1156 """
Miss Islington (bot)6fc1efa2021-07-26 15:34:32 -07001157 A class which sends records to a web server, using either GET or
Guido van Rossum57102f82002-11-13 16:15:58 +00001158 POST semantics.
1159 """
Benjamin Peterson43052a12014-11-23 20:36:44 -06001160 def __init__(self, host, url, method="GET", secure=False, credentials=None,
1161 context=None):
Guido van Rossum57102f82002-11-13 16:15:58 +00001162 """
1163 Initialize the instance with the host, the request URL, and the method
1164 ("GET" or "POST")
1165 """
1166 logging.Handler.__init__(self)
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001167 method = method.upper()
Guido van Rossum57102f82002-11-13 16:15:58 +00001168 if method not in ["GET", "POST"]:
Collin Winterce36ad82007-08-30 01:19:48 +00001169 raise ValueError("method must be GET or POST")
Benjamin Peterson43052a12014-11-23 20:36:44 -06001170 if not secure and context is not None:
1171 raise ValueError("context parameter only makes sense "
1172 "with secure=True")
Guido van Rossum57102f82002-11-13 16:15:58 +00001173 self.host = host
1174 self.url = url
1175 self.method = method
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001176 self.secure = secure
1177 self.credentials = credentials
Benjamin Peterson43052a12014-11-23 20:36:44 -06001178 self.context = context
Guido van Rossum57102f82002-11-13 16:15:58 +00001179
Neal Norwitzf297bd12003-04-23 03:49:43 +00001180 def mapLogRecord(self, record):
1181 """
1182 Default implementation of mapping the log record into a dict
Vinay Sajip48cfe382004-02-20 13:17:27 +00001183 that is sent as the CGI data. Overwrite in your class.
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001184 Contributed by Franz Glasner.
Neal Norwitzf297bd12003-04-23 03:49:43 +00001185 """
1186 return record.__dict__
1187
l0rb22a9a542020-03-04 11:49:51 +01001188 def getConnection(self, host, secure):
1189 """
1190 get a HTTP[S]Connection.
1191
1192 Override when a custom connection is required, for example if
1193 there is a proxy.
1194 """
1195 import http.client
1196 if secure:
1197 connection = http.client.HTTPSConnection(host, context=self.context)
1198 else:
1199 connection = http.client.HTTPConnection(host)
1200 return connection
1201
Guido van Rossum57102f82002-11-13 16:15:58 +00001202 def emit(self, record):
1203 """
1204 Emit a record.
1205
Miss Islington (bot)6fc1efa2021-07-26 15:34:32 -07001206 Send the record to the web server as a percent-encoded dictionary
Guido van Rossum57102f82002-11-13 16:15:58 +00001207 """
1208 try:
l0rb22a9a542020-03-04 11:49:51 +01001209 import urllib.parse
Vinay Sajipb7935062005-10-11 13:15:31 +00001210 host = self.host
l0rb22a9a542020-03-04 11:49:51 +01001211 h = self.getConnection(host, self.secure)
Guido van Rossum57102f82002-11-13 16:15:58 +00001212 url = self.url
Georg Brandl029986a2008-06-23 11:44:14 +00001213 data = urllib.parse.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +00001214 if self.method == "GET":
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001215 if (url.find('?') >= 0):
Guido van Rossum57102f82002-11-13 16:15:58 +00001216 sep = '&'
1217 else:
1218 sep = '?'
1219 url = url + "%c%s" % (sep, data)
1220 h.putrequest(self.method, url)
Vinay Sajipb7935062005-10-11 13:15:31 +00001221 # support multiple hosts on one IP address...
1222 # need to strip optional :port from host, if present
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001223 i = host.find(":")
Vinay Sajipb7935062005-10-11 13:15:31 +00001224 if i >= 0:
1225 host = host[:i]
Vinay Sajipe96ba182017-11-19 18:36:17 +00001226 # See issue #30904: putrequest call above already adds this header
1227 # on Python 3.x.
1228 # h.putheader("Host", host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001229 if self.method == "POST":
Vinay Sajipb7935062005-10-11 13:15:31 +00001230 h.putheader("Content-type",
1231 "application/x-www-form-urlencoded")
Guido van Rossum57102f82002-11-13 16:15:58 +00001232 h.putheader("Content-length", str(len(data)))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001233 if self.credentials:
1234 import base64
Vinay Sajip1bf197e2016-06-07 21:19:55 +01001235 s = ('%s:%s' % self.credentials).encode('utf-8')
1236 s = 'Basic ' + base64.b64encode(s).strip().decode('ascii')
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001237 h.putheader('Authorization', s)
Vinay Sajip0372e102011-05-05 12:59:14 +01001238 h.endheaders()
1239 if self.method == "POST":
1240 h.send(data.encode('utf-8'))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001241 h.getresponse() #can't do anything with the result
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001242 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001243 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001244
1245class BufferingHandler(logging.Handler):
1246 """
1247 A handler class which buffers logging records in memory. Whenever each
1248 record is added to the buffer, a check is made to see if the buffer should
1249 be flushed. If it should, then flush() is expected to do what's needed.
1250 """
1251 def __init__(self, capacity):
1252 """
1253 Initialize the handler with the buffer size.
1254 """
1255 logging.Handler.__init__(self)
1256 self.capacity = capacity
1257 self.buffer = []
1258
1259 def shouldFlush(self, record):
1260 """
1261 Should the handler flush its buffer?
1262
1263 Returns true if the buffer is up to capacity. This method can be
1264 overridden to implement custom flushing strategies.
1265 """
1266 return (len(self.buffer) >= self.capacity)
1267
1268 def emit(self, record):
1269 """
1270 Emit a record.
1271
1272 Append the record. If shouldFlush() tells us to, call flush() to process
1273 the buffer.
1274 """
1275 self.buffer.append(record)
1276 if self.shouldFlush(record):
1277 self.flush()
1278
1279 def flush(self):
1280 """
1281 Override to implement custom flushing behaviour.
1282
1283 This version just zaps the buffer to empty.
1284 """
Vinay Sajipf0509032012-02-23 20:49:08 +00001285 self.acquire()
1286 try:
Daniel Anderssond89cea12019-11-13 10:03:45 +01001287 self.buffer.clear()
Vinay Sajipf0509032012-02-23 20:49:08 +00001288 finally:
1289 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +00001290
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001291 def close(self):
1292 """
1293 Close the handler.
1294
1295 This version just flushes and chains to the parent class' close().
1296 """
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +03001297 try:
1298 self.flush()
1299 finally:
1300 logging.Handler.close(self)
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001301
Guido van Rossum57102f82002-11-13 16:15:58 +00001302class MemoryHandler(BufferingHandler):
1303 """
1304 A handler class which buffers logging records in memory, periodically
1305 flushing them to a target handler. Flushing occurs whenever the buffer
1306 is full, or when an event of a certain severity or greater is seen.
1307 """
Vinay Sajipcccf6062016-07-22 16:27:31 +01001308 def __init__(self, capacity, flushLevel=logging.ERROR, target=None,
1309 flushOnClose=True):
Guido van Rossum57102f82002-11-13 16:15:58 +00001310 """
1311 Initialize the handler with the buffer size, the level at which
1312 flushing should occur and an optional target.
1313
1314 Note that without a target being set either here or via setTarget(),
1315 a MemoryHandler is no use to anyone!
Vinay Sajipcccf6062016-07-22 16:27:31 +01001316
1317 The ``flushOnClose`` argument is ``True`` for backward compatibility
1318 reasons - the old behaviour is that when the handler is closed, the
1319 buffer is flushed, even if the flush level hasn't been exceeded nor the
1320 capacity exceeded. To prevent this, set ``flushOnClose`` to ``False``.
Guido van Rossum57102f82002-11-13 16:15:58 +00001321 """
1322 BufferingHandler.__init__(self, capacity)
1323 self.flushLevel = flushLevel
1324 self.target = target
Vinay Sajipcccf6062016-07-22 16:27:31 +01001325 # See Issue #26559 for why this has been added
1326 self.flushOnClose = flushOnClose
Guido van Rossum57102f82002-11-13 16:15:58 +00001327
1328 def shouldFlush(self, record):
1329 """
1330 Check for buffer full or a record at the flushLevel or higher.
1331 """
1332 return (len(self.buffer) >= self.capacity) or \
1333 (record.levelno >= self.flushLevel)
1334
1335 def setTarget(self, target):
1336 """
1337 Set the target handler for this handler.
1338 """
Irit Katriel2353d772020-08-16 16:10:13 +01001339 self.acquire()
1340 try:
1341 self.target = target
1342 finally:
1343 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +00001344
1345 def flush(self):
1346 """
1347 For a MemoryHandler, flushing means just sending the buffered
1348 records to the target, if there is one. Override if you want
1349 different behaviour.
Vinay Sajipc84f0162010-09-21 11:25:39 +00001350
1351 The record buffer is also cleared by this operation.
Guido van Rossum57102f82002-11-13 16:15:58 +00001352 """
Vinay Sajipf0509032012-02-23 20:49:08 +00001353 self.acquire()
1354 try:
Vinay Sajip0abf61d2012-02-23 19:45:52 +00001355 if self.target:
1356 for record in self.buffer:
1357 self.target.handle(record)
Daniel Anderssond89cea12019-11-13 10:03:45 +01001358 self.buffer.clear()
Vinay Sajipf0509032012-02-23 20:49:08 +00001359 finally:
1360 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +00001361
1362 def close(self):
1363 """
Vinay Sajipcccf6062016-07-22 16:27:31 +01001364 Flush, if appropriately configured, set the target to None and lose the
1365 buffer.
Guido van Rossum57102f82002-11-13 16:15:58 +00001366 """
Vinay Sajipf0509032012-02-23 20:49:08 +00001367 try:
Vinay Sajipcccf6062016-07-22 16:27:31 +01001368 if self.flushOnClose:
1369 self.flush()
Vinay Sajipf0509032012-02-23 20:49:08 +00001370 finally:
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +03001371 self.acquire()
1372 try:
1373 self.target = None
1374 BufferingHandler.close(self)
1375 finally:
1376 self.release()
Vinay Sajip121a1c42010-09-08 10:46:15 +00001377
1378
1379class QueueHandler(logging.Handler):
1380 """
1381 This handler sends events to a queue. Typically, it would be used together
1382 with a multiprocessing Queue to centralise logging to file in one process
1383 (in a multi-process application), so as to avoid file write contention
1384 between processes.
1385
1386 This code is new in Python 3.2, but this class can be copy pasted into
1387 user code for use with earlier Python versions.
1388 """
1389
1390 def __init__(self, queue):
1391 """
1392 Initialise an instance, using the passed queue.
1393 """
1394 logging.Handler.__init__(self)
1395 self.queue = queue
1396
1397 def enqueue(self, record):
1398 """
1399 Enqueue a record.
1400
1401 The base implementation uses put_nowait. You may want to override
1402 this method if you want to use blocking, timeouts or custom queue
1403 implementations.
1404 """
1405 self.queue.put_nowait(record)
1406
Vinay Sajip0258ce82010-09-22 20:34:53 +00001407 def prepare(self, record):
1408 """
Vinay Sajip0637d492010-09-23 08:15:54 +00001409 Prepares a record for queuing. The object returned by this method is
1410 enqueued.
Vinay Sajip0258ce82010-09-22 20:34:53 +00001411
1412 The base implementation formats the record to merge the message
1413 and arguments, and removes unpickleable items from the record
1414 in-place.
1415
1416 You might want to override this method if you want to convert
1417 the record to a dict or JSON string, or send a modified copy
1418 of the record while leaving the original intact.
1419 """
1420 # The format operation gets traceback text into record.exc_text
favlladfe3442017-08-01 20:12:26 +02001421 # (if there's exception data), and also returns the formatted
1422 # message. We can then use this to replace the original
Vinay Sajip0258ce82010-09-22 20:34:53 +00001423 # msg + args, as these might be unpickleable. We also zap the
Cheryl Sabellad345bb42018-09-25 19:00:08 -04001424 # exc_info and exc_text attributes, as they are no longer
1425 # needed and, if not None, will typically not be pickleable.
favlladfe3442017-08-01 20:12:26 +02001426 msg = self.format(record)
Manjusakada6424e2019-01-23 15:08:38 +08001427 # bpo-35726: make copy of record to avoid affecting other handlers in the chain.
1428 record = copy.copy(record)
favlladfe3442017-08-01 20:12:26 +02001429 record.message = msg
1430 record.msg = msg
Vinay Sajip0258ce82010-09-22 20:34:53 +00001431 record.args = None
1432 record.exc_info = None
Cheryl Sabellad345bb42018-09-25 19:00:08 -04001433 record.exc_text = None
Vinay Sajip0258ce82010-09-22 20:34:53 +00001434 return record
1435
Vinay Sajip121a1c42010-09-08 10:46:15 +00001436 def emit(self, record):
1437 """
1438 Emit a record.
1439
Vinay Sajip0637d492010-09-23 08:15:54 +00001440 Writes the LogRecord to the queue, preparing it for pickling first.
Vinay Sajip121a1c42010-09-08 10:46:15 +00001441 """
1442 try:
Vinay Sajip0258ce82010-09-22 20:34:53 +00001443 self.enqueue(self.prepare(record))
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001444 except Exception:
Vinay Sajip121a1c42010-09-08 10:46:15 +00001445 self.handleError(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001446
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001447
1448class QueueListener(object):
1449 """
1450 This class implements an internal threaded listener which watches for
1451 LogRecords being added to a queue, removes them and passes them to a
1452 list of handlers for processing.
1453 """
1454 _sentinel = None
1455
1456 def __init__(self, queue, *handlers, respect_handler_level=False):
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001457 """
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001458 Initialise an instance with the specified queue and
1459 handlers.
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001460 """
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001461 self.queue = queue
1462 self.handlers = handlers
1463 self._thread = None
1464 self.respect_handler_level = respect_handler_level
Vinay Sajip0637d492010-09-23 08:15:54 +00001465
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001466 def dequeue(self, block):
1467 """
1468 Dequeue a record and return it, optionally blocking.
Vinay Sajip0637d492010-09-23 08:15:54 +00001469
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001470 The base implementation uses get. You may want to override this method
1471 if you want to use timeouts or work with custom queue implementations.
1472 """
1473 return self.queue.get(block)
Vinay Sajip0637d492010-09-23 08:15:54 +00001474
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001475 def start(self):
1476 """
1477 Start the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001478
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001479 This starts up a background thread to monitor the queue for
1480 LogRecords to process.
1481 """
1482 self._thread = t = threading.Thread(target=self._monitor)
1483 t.daemon = True
1484 t.start()
Vinay Sajip0637d492010-09-23 08:15:54 +00001485
Boris Feld800d7862019-06-04 17:20:18 +02001486 def prepare(self, record):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001487 """
1488 Prepare a record for handling.
Vinay Sajip0637d492010-09-23 08:15:54 +00001489
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001490 This method just returns the passed-in record. You may want to
1491 override this method if you need to do any custom marshalling or
1492 manipulation of the record before passing it to the handlers.
1493 """
1494 return record
Vinay Sajip0637d492010-09-23 08:15:54 +00001495
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001496 def handle(self, record):
1497 """
1498 Handle a record.
Vinay Sajip0637d492010-09-23 08:15:54 +00001499
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001500 This just loops through the handlers offering them the record
1501 to handle.
1502 """
1503 record = self.prepare(record)
1504 for handler in self.handlers:
1505 if not self.respect_handler_level:
1506 process = True
1507 else:
1508 process = record.levelno >= handler.level
1509 if process:
1510 handler.handle(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001511
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001512 def _monitor(self):
1513 """
1514 Monitor the queue for records, and ask the handler
1515 to deal with them.
Vinay Sajip0637d492010-09-23 08:15:54 +00001516
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001517 This method runs on a separate, internal thread.
1518 The thread will terminate if it sees a sentinel object in the queue.
1519 """
1520 q = self.queue
1521 has_task_done = hasattr(q, 'task_done')
1522 while True:
1523 try:
1524 record = self.dequeue(True)
1525 if record is self._sentinel:
Bar Harel6b282e12019-06-01 12:19:09 +03001526 if has_task_done:
1527 q.task_done()
Vinay Sajip0637d492010-09-23 08:15:54 +00001528 break
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001529 self.handle(record)
1530 if has_task_done:
1531 q.task_done()
1532 except queue.Empty:
1533 break
Vinay Sajip0637d492010-09-23 08:15:54 +00001534
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001535 def enqueue_sentinel(self):
1536 """
1537 This is used to enqueue the sentinel record.
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001538
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001539 The base implementation uses put_nowait. You may want to override this
1540 method if you want to use timeouts or work with custom queue
1541 implementations.
1542 """
1543 self.queue.put_nowait(self._sentinel)
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001544
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001545 def stop(self):
1546 """
1547 Stop the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001548
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001549 This asks the thread to terminate, and then waits for it to do so.
1550 Note that if you don't call this before your application exits, there
1551 may be some records still left on the queue, which won't be processed.
1552 """
1553 self.enqueue_sentinel()
1554 self._thread.join()
1555 self._thread = None