blob: b0d5885989ea426b2870dea71ee9a08df2282480 [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 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000190 if self.stream is None: # delay was set...
191 self.stream = self._open()
Guido van Rossum57102f82002-11-13 16:15:58 +0000192 if self.maxBytes > 0: # are we rolling over?
193 msg = "%s\n" % self.format(record)
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000194 self.stream.seek(0, 2) #due to non-posix-compliant Windows feature
Guido van Rossum57102f82002-11-13 16:15:58 +0000195 if self.stream.tell() + len(msg) >= self.maxBytes:
Vinay Sajip17c52d82004-07-03 11:48:34 +0000196 return 1
197 return 0
Guido van Rossum57102f82002-11-13 16:15:58 +0000198
Vinay Sajip17c52d82004-07-03 11:48:34 +0000199class TimedRotatingFileHandler(BaseRotatingHandler):
200 """
201 Handler for logging to a file, rotating the log file at certain timed
202 intervals.
203
204 If backupCount is > 0, when rollover is done, no more than backupCount
205 files are kept - the oldest ones are deleted.
206 """
Vinay Sajipca7b5042019-06-17 17:40:52 +0100207 def __init__(self, filename, when='h', interval=1, backupCount=0,
208 encoding=None, delay=False, utc=False, atTime=None,
209 errors=None):
Inada Naokifb786922021-04-06 11:18:41 +0900210 encoding = io.text_encoding(encoding)
Vinay Sajipca7b5042019-06-17 17:40:52 +0100211 BaseRotatingHandler.__init__(self, filename, 'a', encoding=encoding,
212 delay=delay, errors=errors)
Neal Norwitz9d72bb42007-04-17 08:48:32 +0000213 self.when = when.upper()
Vinay Sajip17c52d82004-07-03 11:48:34 +0000214 self.backupCount = backupCount
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000215 self.utc = utc
Vinay Sajipa7130792013-04-12 17:04:23 +0100216 self.atTime = atTime
Vinay Sajip17c52d82004-07-03 11:48:34 +0000217 # Calculate the real rollover interval, which is just the number of
218 # seconds between rollovers. Also set the filename suffix used when
219 # a rollover occurs. Current 'when' events supported:
220 # S - Seconds
221 # M - Minutes
222 # H - Hours
223 # D - Days
224 # midnight - roll over at midnight
225 # W{0-6} - roll over on a certain day; 0 - Monday
226 #
227 # Case of the 'when' specifier is not important; lower or upper case
228 # will work.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000229 if self.when == 'S':
230 self.interval = 1 # one second
231 self.suffix = "%Y-%m-%d_%H-%M-%S"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000232 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000233 elif self.when == 'M':
234 self.interval = 60 # one minute
235 self.suffix = "%Y-%m-%d_%H-%M"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000236 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000237 elif self.when == 'H':
238 self.interval = 60 * 60 # one hour
239 self.suffix = "%Y-%m-%d_%H"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000240 self.extMatch = r"^\d{4}-\d{2}-\d{2}_\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000241 elif self.when == 'D' or self.when == 'MIDNIGHT':
242 self.interval = 60 * 60 * 24 # one day
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 elif self.when.startswith('W'):
246 self.interval = 60 * 60 * 24 * 7 # one week
247 if len(self.when) != 2:
248 raise ValueError("You must specify a day for weekly rollover from 0 to 6 (0 is Monday): %s" % self.when)
249 if self.when[1] < '0' or self.when[1] > '6':
250 raise ValueError("Invalid day specified for weekly rollover: %s" % self.when)
251 self.dayOfWeek = int(self.when[1])
252 self.suffix = "%Y-%m-%d"
Vinay Sajip23b94d02012-01-04 12:02:26 +0000253 self.extMatch = r"^\d{4}-\d{2}-\d{2}(\.\w+)?$"
Vinay Sajip17c52d82004-07-03 11:48:34 +0000254 else:
255 raise ValueError("Invalid rollover interval specified: %s" % self.when)
256
Antoine Pitroufd036452008-08-19 17:56:33 +0000257 self.extMatch = re.compile(self.extMatch, re.ASCII)
Vinay Sajipe7d40662004-10-03 19:12:07 +0000258 self.interval = self.interval * interval # multiply by units requested
Vinay Sajip638e6222016-07-22 18:23:04 +0100259 # The following line added because the filename passed in could be a
260 # path object (see Issue #27493), but self.baseFilename will be a string
261 filename = self.baseFilename
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000262 if os.path.exists(filename):
263 t = os.stat(filename)[ST_MTIME]
264 else:
265 t = int(time.time())
266 self.rolloverAt = self.computeRollover(t)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000267
Vinay Sajipaffbd872009-06-11 10:11:47 +0000268 def computeRollover(self, currentTime):
269 """
270 Work out the rollover time based on the specified time.
271 """
272 result = currentTime + self.interval
Vinay Sajip17c52d82004-07-03 11:48:34 +0000273 # If we are rolling over at midnight or weekly, then the interval is already known.
274 # What we need to figure out is WHEN the next interval is. In other words,
275 # if you are rolling over at midnight, then your base interval is 1 day,
276 # but you want to start that one day clock at midnight, not now. So, we
277 # have to fudge the rolloverAt value in order to trigger the first rollover
278 # at the right time. After that, the regular interval will take care of
279 # the rest. Note that this code doesn't care about leap seconds. :)
280 if self.when == 'MIDNIGHT' or self.when.startswith('W'):
281 # This could be done with less code, but I wanted it to be clear
Vinay Sajipaffbd872009-06-11 10:11:47 +0000282 if self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000283 t = time.gmtime(currentTime)
284 else:
285 t = time.localtime(currentTime)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000286 currentHour = t[3]
287 currentMinute = t[4]
288 currentSecond = t[5]
Vinay Sajipa7130792013-04-12 17:04:23 +0100289 currentDay = t[6]
290 # r is the number of seconds left between now and the next rotation
291 if self.atTime is None:
292 rotate_ts = _MIDNIGHT
293 else:
294 rotate_ts = ((self.atTime.hour * 60 + self.atTime.minute)*60 +
295 self.atTime.second)
296
297 r = rotate_ts - ((currentHour * 60 + currentMinute) * 60 +
298 currentSecond)
299 if r < 0:
300 # Rotate time is before the current time (for example when
301 # self.rotateAt is 13:45 and it now 14:15), rotation is
302 # tomorrow.
303 r += _MIDNIGHT
304 currentDay = (currentDay + 1) % 7
Vinay Sajipaffbd872009-06-11 10:11:47 +0000305 result = currentTime + r
Vinay Sajip17c52d82004-07-03 11:48:34 +0000306 # If we are rolling over on a certain day, add in the number of days until
307 # the next rollover, but offset by 1 since we just calculated the time
308 # until the next day starts. There are three cases:
309 # Case 1) The day to rollover is today; in this case, do nothing
310 # Case 2) The day to rollover is further in the interval (i.e., today is
311 # day 2 (Wednesday) and rollover is on day 6 (Sunday). Days to
312 # next rollover is simply 6 - 2 - 1, or 3.
313 # Case 3) The day to rollover is behind us in the interval (i.e., today
314 # is day 5 (Saturday) and rollover is on day 3 (Thursday).
315 # Days to rollover is 6 - 5 + 3, or 4. In this case, it's the
316 # number of days left in the current week (1) plus the number
317 # of days in the next week until the rollover day (3).
Georg Brandl86def6c2008-01-21 20:36:10 +0000318 # The calculations described in 2) and 3) above need to have a day added.
319 # This is because the above time calculation takes us to midnight on this
320 # day, i.e. the start of the next day.
Vinay Sajipaffbd872009-06-11 10:11:47 +0000321 if self.when.startswith('W'):
Vinay Sajipa7130792013-04-12 17:04:23 +0100322 day = currentDay # 0 is Monday
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000323 if day != self.dayOfWeek:
324 if day < self.dayOfWeek:
Georg Brandl86def6c2008-01-21 20:36:10 +0000325 daysToWait = self.dayOfWeek - day
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000326 else:
Georg Brandl86def6c2008-01-21 20:36:10 +0000327 daysToWait = 6 - day + self.dayOfWeek + 1
Vinay Sajipaffbd872009-06-11 10:11:47 +0000328 newRolloverAt = result + (daysToWait * (60 * 60 * 24))
329 if not self.utc:
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000330 dstNow = t[-1]
331 dstAtRollover = time.localtime(newRolloverAt)[-1]
332 if dstNow != dstAtRollover:
333 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000334 addend = -3600
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000335 else: # DST bows out before next rollover, so we need to add an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000336 addend = 3600
337 newRolloverAt += addend
Vinay Sajipaffbd872009-06-11 10:11:47 +0000338 result = newRolloverAt
339 return result
Vinay Sajip17c52d82004-07-03 11:48:34 +0000340
341 def shouldRollover(self, record):
342 """
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000343 Determine if rollover should occur.
Vinay Sajip17c52d82004-07-03 11:48:34 +0000344
345 record is not used, as we are just comparing times, but it is needed so
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000346 the method signatures are the same
Vinay Sajip17c52d82004-07-03 11:48:34 +0000347 """
348 t = int(time.time())
349 if t >= self.rolloverAt:
350 return 1
Vinay Sajip17c52d82004-07-03 11:48:34 +0000351 return 0
352
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000353 def getFilesToDelete(self):
354 """
355 Determine the files to delete when rolling over.
356
357 More specific than the earlier method, which just used glob.glob().
358 """
359 dirName, baseName = os.path.split(self.baseFilename)
360 fileNames = os.listdir(dirName)
361 result = []
362 prefix = baseName + "."
363 plen = len(prefix)
364 for fileName in fileNames:
365 if fileName[:plen] == prefix:
366 suffix = fileName[plen:]
367 if self.extMatch.match(suffix):
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000368 result.append(os.path.join(dirName, fileName))
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000369 if len(result) < self.backupCount:
370 result = []
371 else:
Lovesh Harchandaniafad1472017-10-27 09:04:33 +0200372 result.sort()
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000373 result = result[:len(result) - self.backupCount]
374 return result
375
Vinay Sajip17c52d82004-07-03 11:48:34 +0000376 def doRollover(self):
377 """
378 do a rollover; in this case, a date/time stamp is appended to the filename
379 when the rollover happens. However, you want the file to be named for the
380 start of the interval, not the current time. If there is a backup count,
381 then we have to get a list of matching filenames, sort them and remove
382 the one with the oldest suffix.
383 """
Vinay Sajip6268cbc2009-01-21 00:19:28 +0000384 if self.stream:
385 self.stream.close()
Vinay Sajip01241d62011-01-21 23:35:57 +0000386 self.stream = None
Vinay Sajip17c52d82004-07-03 11:48:34 +0000387 # get the time that this sequence started at and make it a TimeTuple
Vinay Sajip27f48972012-03-13 12:10:33 +0000388 currentTime = int(time.time())
389 dstNow = time.localtime(currentTime)[-1]
Vinay Sajip17c52d82004-07-03 11:48:34 +0000390 t = self.rolloverAt - self.interval
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000391 if self.utc:
392 timeTuple = time.gmtime(t)
393 else:
394 timeTuple = time.localtime(t)
Vinay Sajip27f48972012-03-13 12:10:33 +0000395 dstThen = timeTuple[-1]
396 if dstNow != dstThen:
397 if dstNow:
398 addend = 3600
399 else:
400 addend = -3600
401 timeTuple = time.localtime(t + addend)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000402 dfn = self.rotation_filename(self.baseFilename + "." +
403 time.strftime(self.suffix, timeTuple))
Vinay Sajip17c52d82004-07-03 11:48:34 +0000404 if os.path.exists(dfn):
405 os.remove(dfn)
Vinay Sajip23b94d02012-01-04 12:02:26 +0000406 self.rotate(self.baseFilename, dfn)
Vinay Sajip17c52d82004-07-03 11:48:34 +0000407 if self.backupCount > 0:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000408 for s in self.getFilesToDelete():
409 os.remove(s)
Vinay Sajip43c6ef12013-09-06 10:25:31 +0100410 if not self.delay:
411 self.stream = self._open()
Vinay Sajipaffbd872009-06-11 10:11:47 +0000412 newRolloverAt = self.computeRollover(currentTime)
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000413 while newRolloverAt <= currentTime:
414 newRolloverAt = newRolloverAt + self.interval
415 #If DST changes and midnight or weekly rollover, adjust for this.
Benjamin Petersona37cfc62008-05-26 13:48:34 +0000416 if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000417 dstAtRollover = time.localtime(newRolloverAt)[-1]
418 if dstNow != dstAtRollover:
419 if not dstNow: # DST kicks in before next rollover, so we need to deduct an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000420 addend = -3600
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000421 else: # DST bows out before next rollover, so we need to add an hour
Vinay Sajip27f48972012-03-13 12:10:33 +0000422 addend = 3600
423 newRolloverAt += addend
Benjamin Petersonad9d48d2008-04-02 21:49:44 +0000424 self.rolloverAt = newRolloverAt
Guido van Rossum57102f82002-11-13 16:15:58 +0000425
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000426class WatchedFileHandler(logging.FileHandler):
427 """
428 A handler for logging to a file, which watches the file
429 to see if it has changed while in use. This can happen because of
430 usage of programs such as newsyslog and logrotate which perform
431 log file rotation. This handler, intended for use under Unix,
432 watches the file to see if it has changed since the last emit.
433 (A file has changed if its device or inode have changed.)
434 If it has changed, the old file stream is closed, and the file
435 opened to get a new stream.
436
437 This handler is not appropriate for use under Windows, because
438 under Windows open files cannot be moved or renamed - logging
439 opens the files with exclusive locks - and so there is no need
440 for such a handler. Furthermore, ST_INO is not supported under
441 Windows; stat always returns zero for this value.
442
443 This handler is based on a suggestion and patch by Chad J.
444 Schroeder.
445 """
Vinay Sajipca7b5042019-06-17 17:40:52 +0100446 def __init__(self, filename, mode='a', encoding=None, delay=False,
447 errors=None):
Inada Naokifb786922021-04-06 11:18:41 +0900448 if "b" not in mode:
449 encoding = io.text_encoding(encoding)
Vinay Sajipca7b5042019-06-17 17:40:52 +0100450 logging.FileHandler.__init__(self, filename, mode=mode,
451 encoding=encoding, delay=delay,
452 errors=errors)
Vinay Sajip66b8b082012-04-24 23:25:30 +0100453 self.dev, self.ino = -1, -1
454 self._statstream()
455
456 def _statstream(self):
457 if self.stream:
458 sres = os.fstat(self.stream.fileno())
459 self.dev, self.ino = sres[ST_DEV], sres[ST_INO]
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000460
Vinay Sajip29a14452015-10-01 20:54:41 +0100461 def reopenIfNeeded(self):
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000462 """
Vinay Sajip29a14452015-10-01 20:54:41 +0100463 Reopen log file if needed.
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000464
Vinay Sajip29a14452015-10-01 20:54:41 +0100465 Checks if the underlying file has changed, and if it
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000466 has, close the old stream and reopen the file to get the
467 current stream.
468 """
Vinay Sajip66b8b082012-04-24 23:25:30 +0100469 # Reduce the chance of race conditions by stat'ing by path only
470 # once and then fstat'ing our new fd if we opened a new log stream.
471 # See issue #14632: Thanks to John Mulligan for the problem report
472 # and patch.
473 try:
474 # stat the file by path, checking for existence
475 sres = os.stat(self.baseFilename)
Giampaolo Rodola'0166a282013-02-12 15:14:17 +0100476 except FileNotFoundError:
477 sres = None
Vinay Sajip66b8b082012-04-24 23:25:30 +0100478 # compare file system stat with that of our stream file handle
479 if not sres or sres[ST_DEV] != self.dev or sres[ST_INO] != self.ino:
480 if self.stream is not None:
481 # we have an open file handle, clean it up
482 self.stream.flush()
483 self.stream.close()
Vinay Sajip02200482014-06-14 10:22:05 +0100484 self.stream = None # See Issue #21742: _open () might fail.
Vinay Sajip66b8b082012-04-24 23:25:30 +0100485 # open a new file handle and get new stat info from that fd
486 self.stream = self._open()
487 self._statstream()
Vinay Sajip29a14452015-10-01 20:54:41 +0100488
489 def emit(self, record):
490 """
491 Emit a record.
492
493 If underlying file has changed, reopen the file before emitting the
494 record to it.
495 """
496 self.reopenIfNeeded()
Thomas Woutersfc7bb8c2007-01-15 15:49:28 +0000497 logging.FileHandler.emit(self, record)
498
Vinay Sajip66b8b082012-04-24 23:25:30 +0100499
Guido van Rossum57102f82002-11-13 16:15:58 +0000500class SocketHandler(logging.Handler):
501 """
502 A handler class which writes logging records, in pickle format, to
503 a streaming socket. The socket is kept open across logging calls.
504 If the peer resets it, an attempt is made to reconnect on the next call.
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000505 The pickle which is sent is that of the LogRecord's attribute dictionary
506 (__dict__), so that the receiver does not need to have the logging module
507 installed in order to process the logging event.
508
509 To unpickle the record at the receiving end into a LogRecord, use the
510 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000511 """
512
513 def __init__(self, host, port):
514 """
515 Initializes the handler with a specific host address and port.
516
Vinay Sajipde19e082011-04-30 21:52:26 +0100517 When the attribute *closeOnError* is set to True - if a socket error
518 occurs, the socket is silently closed and then reopened on the next
519 logging call.
Guido van Rossum57102f82002-11-13 16:15:58 +0000520 """
521 logging.Handler.__init__(self)
522 self.host = host
523 self.port = port
Vinay Sajip5421f352013-09-27 18:18:28 +0100524 if port is None:
525 self.address = host
526 else:
527 self.address = (host, port)
Guido van Rossum57102f82002-11-13 16:15:58 +0000528 self.sock = None
Vinay Sajipde19e082011-04-30 21:52:26 +0100529 self.closeOnError = False
Vinay Sajip48cfe382004-02-20 13:17:27 +0000530 self.retryTime = None
531 #
532 # Exponential backoff parameters.
533 #
534 self.retryStart = 1.0
535 self.retryMax = 30.0
536 self.retryFactor = 2.0
Guido van Rossum57102f82002-11-13 16:15:58 +0000537
Guido van Rossumd8faa362007-04-27 19:54:29 +0000538 def makeSocket(self, timeout=1):
Guido van Rossum57102f82002-11-13 16:15:58 +0000539 """
540 A factory method which allows subclasses to define the precise
541 type of socket they want.
542 """
Vinay Sajip5421f352013-09-27 18:18:28 +0100543 if self.port is not None:
544 result = socket.create_connection(self.address, timeout=timeout)
545 else:
546 result = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
547 result.settimeout(timeout)
Vinay Sajip38c741c2013-10-06 18:36:00 +0100548 try:
549 result.connect(self.address)
550 except OSError:
551 result.close() # Issue 19182
552 raise
Vinay Sajip5421f352013-09-27 18:18:28 +0100553 return result
Guido van Rossum57102f82002-11-13 16:15:58 +0000554
Vinay Sajip48cfe382004-02-20 13:17:27 +0000555 def createSocket(self):
556 """
557 Try to create a socket, using an exponential backoff with
558 a max retry time. Thanks to Robert Olson for the original patch
559 (SF #815911) which has been slightly refactored.
560 """
561 now = time.time()
562 # Either retryTime is None, in which case this
563 # is the first time back after a disconnect, or
564 # we've waited long enough.
565 if self.retryTime is None:
Vinay Sajipde19e082011-04-30 21:52:26 +0100566 attempt = True
Vinay Sajip48cfe382004-02-20 13:17:27 +0000567 else:
Tim Peters4e0e1b62004-07-07 20:54:48 +0000568 attempt = (now >= self.retryTime)
Vinay Sajip48cfe382004-02-20 13:17:27 +0000569 if attempt:
570 try:
571 self.sock = self.makeSocket()
572 self.retryTime = None # next time, no delay before trying
Andrew Svetlov0832af62012-12-18 23:10:48 +0200573 except OSError:
Vinay Sajip48cfe382004-02-20 13:17:27 +0000574 #Creation failed, so set the retry time and return.
575 if self.retryTime is None:
576 self.retryPeriod = self.retryStart
577 else:
578 self.retryPeriod = self.retryPeriod * self.retryFactor
579 if self.retryPeriod > self.retryMax:
580 self.retryPeriod = self.retryMax
581 self.retryTime = now + self.retryPeriod
582
Guido van Rossum57102f82002-11-13 16:15:58 +0000583 def send(self, s):
584 """
585 Send a pickled string to the socket.
586
587 This function allows for partial sends which can happen when the
588 network is busy.
589 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000590 if self.sock is None:
591 self.createSocket()
592 #self.sock can be None either because we haven't reached the retry
593 #time yet, or because we have reached the retry time and retried,
594 #but are still unable to connect.
595 if self.sock:
596 try:
Giampaolo Rodola'f9de90a2012-10-26 18:32:24 +0200597 self.sock.sendall(s)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200598 except OSError: #pragma: no cover
Vinay Sajip48cfe382004-02-20 13:17:27 +0000599 self.sock.close()
600 self.sock = None # so we can call createSocket next time
Guido van Rossum57102f82002-11-13 16:15:58 +0000601
602 def makePickle(self, record):
603 """
604 Pickles the record in binary format with a length prefix, and
605 returns it ready for transmission across the socket.
606 """
Vinay Sajip48cfe382004-02-20 13:17:27 +0000607 ei = record.exc_info
608 if ei:
Vinay Sajip6f5e54e2012-03-29 20:17:18 +0100609 # just to get traceback text into record.exc_text ...
610 dummy = self.format(record)
611 # See issue #14436: If msg or args are objects, they may not be
612 # available on the receiving end. So we convert the msg % args
613 # to a string, save it as msg and zap the args.
614 d = dict(record.__dict__)
615 d['msg'] = record.getMessage()
616 d['args'] = None
617 d['exc_info'] = None
Vinay Sajip9cdfd182015-12-26 12:48:44 +0000618 # Issue #25685: delete 'message' if present: redundant with 'msg'
619 d.pop('message', None)
Vinay Sajip6f5e54e2012-03-29 20:17:18 +0100620 s = pickle.dumps(d, 1)
Guido van Rossum57102f82002-11-13 16:15:58 +0000621 slen = struct.pack(">L", len(s))
622 return slen + s
623
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000624 def handleError(self, record):
Guido van Rossum57102f82002-11-13 16:15:58 +0000625 """
626 Handle an error during logging.
627
628 An error has occurred during logging. Most likely cause -
629 connection lost. Close the socket so that we can retry on the
630 next event.
631 """
632 if self.closeOnError and self.sock:
633 self.sock.close()
634 self.sock = None #try to reconnect next time
635 else:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000636 logging.Handler.handleError(self, record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000637
638 def emit(self, record):
639 """
640 Emit a record.
641
642 Pickles the record and writes it to the socket in binary format.
643 If there is an error with the socket, silently drop the packet.
644 If there was a problem with the socket, re-establishes the
645 socket.
646 """
647 try:
648 s = self.makePickle(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000649 self.send(s)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100650 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000651 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000652
653 def close(self):
654 """
655 Closes the socket.
656 """
Vinay Sajipf0509032012-02-23 20:49:08 +0000657 self.acquire()
658 try:
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300659 sock = self.sock
660 if sock:
Vinay Sajip0abf61d2012-02-23 19:45:52 +0000661 self.sock = None
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +0300662 sock.close()
Vinay Sajip0abf61d2012-02-23 19:45:52 +0000663 logging.Handler.close(self)
Vinay Sajipf0509032012-02-23 20:49:08 +0000664 finally:
665 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +0000666
667class DatagramHandler(SocketHandler):
668 """
669 A handler class which writes logging records, in pickle format, to
Raymond Hettinger6f3eaa62003-06-27 21:43:39 +0000670 a datagram socket. The pickle which is sent is that of the LogRecord's
671 attribute dictionary (__dict__), so that the receiver does not need to
672 have the logging module installed in order to process the logging event.
673
674 To unpickle the record at the receiving end into a LogRecord, use the
675 makeLogRecord function.
Guido van Rossum57102f82002-11-13 16:15:58 +0000676
677 """
678 def __init__(self, host, port):
679 """
680 Initializes the handler with a specific host address and port.
681 """
682 SocketHandler.__init__(self, host, port)
Vinay Sajipde19e082011-04-30 21:52:26 +0100683 self.closeOnError = False
Guido van Rossum57102f82002-11-13 16:15:58 +0000684
685 def makeSocket(self):
686 """
687 The factory method of SocketHandler is here overridden to create
688 a UDP socket (SOCK_DGRAM).
689 """
Vinay Sajip5421f352013-09-27 18:18:28 +0100690 if self.port is None:
691 family = socket.AF_UNIX
692 else:
693 family = socket.AF_INET
694 s = socket.socket(family, socket.SOCK_DGRAM)
Guido van Rossum57102f82002-11-13 16:15:58 +0000695 return s
696
697 def send(self, s):
698 """
699 Send a pickled string to a socket.
700
701 This function no longer allows for partial sends which can happen
702 when the network is busy - UDP does not guarantee delivery and
703 can deliver packets out of sequence.
704 """
Vinay Sajipfb154172004-08-24 09:36:23 +0000705 if self.sock is None:
706 self.createSocket()
Vinay Sajip5421f352013-09-27 18:18:28 +0100707 self.sock.sendto(s, self.address)
Guido van Rossum57102f82002-11-13 16:15:58 +0000708
709class SysLogHandler(logging.Handler):
710 """
711 A handler class which sends formatted logging records to a syslog
712 server. Based on Sam Rushing's syslog module:
713 http://www.nightmare.com/squirl/python-ext/misc/syslog.py
714 Contributed by Nicolas Untz (after which minor refactoring changes
715 have been made).
716 """
717
718 # from <linux/sys/syslog.h>:
719 # ======================================================================
720 # priorities/facilities are encoded into a single 32-bit quantity, where
721 # the bottom 3 bits are the priority (0-7) and the top 28 bits are the
722 # facility (0-big number). Both the priorities and the facilities map
723 # roughly one-to-one to strings in the syslogd(8) source code. This
724 # mapping is included in this file.
725 #
726 # priorities (these are ordered)
727
728 LOG_EMERG = 0 # system is unusable
729 LOG_ALERT = 1 # action must be taken immediately
730 LOG_CRIT = 2 # critical conditions
731 LOG_ERR = 3 # error conditions
732 LOG_WARNING = 4 # warning conditions
733 LOG_NOTICE = 5 # normal but significant condition
734 LOG_INFO = 6 # informational
735 LOG_DEBUG = 7 # debug-level messages
736
737 # facility codes
738 LOG_KERN = 0 # kernel messages
739 LOG_USER = 1 # random user-level messages
740 LOG_MAIL = 2 # mail system
741 LOG_DAEMON = 3 # system daemons
742 LOG_AUTH = 4 # security/authorization messages
743 LOG_SYSLOG = 5 # messages generated internally by syslogd
744 LOG_LPR = 6 # line printer subsystem
745 LOG_NEWS = 7 # network news subsystem
746 LOG_UUCP = 8 # UUCP subsystem
747 LOG_CRON = 9 # clock daemon
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000748 LOG_AUTHPRIV = 10 # security/authorization messages (private)
749 LOG_FTP = 11 # FTP daemon
Vinay Sajipce545192020-01-10 19:37:48 +0000750 LOG_NTP = 12 # NTP subsystem
751 LOG_SECURITY = 13 # Log audit
752 LOG_CONSOLE = 14 # Log alert
753 LOG_SOLCRON = 15 # Scheduling daemon (Solaris)
Guido van Rossum57102f82002-11-13 16:15:58 +0000754
755 # other codes through 15 reserved for system use
756 LOG_LOCAL0 = 16 # reserved for local use
757 LOG_LOCAL1 = 17 # reserved for local use
758 LOG_LOCAL2 = 18 # reserved for local use
759 LOG_LOCAL3 = 19 # reserved for local use
760 LOG_LOCAL4 = 20 # reserved for local use
761 LOG_LOCAL5 = 21 # reserved for local use
762 LOG_LOCAL6 = 22 # reserved for local use
763 LOG_LOCAL7 = 23 # reserved for local use
764
765 priority_names = {
766 "alert": LOG_ALERT,
767 "crit": LOG_CRIT,
768 "critical": LOG_CRIT,
769 "debug": LOG_DEBUG,
770 "emerg": LOG_EMERG,
771 "err": LOG_ERR,
772 "error": LOG_ERR, # DEPRECATED
773 "info": LOG_INFO,
774 "notice": LOG_NOTICE,
775 "panic": LOG_EMERG, # DEPRECATED
776 "warn": LOG_WARNING, # DEPRECATED
777 "warning": LOG_WARNING,
778 }
779
780 facility_names = {
Vinay Sajipce545192020-01-10 19:37:48 +0000781 "auth": LOG_AUTH,
782 "authpriv": LOG_AUTHPRIV,
783 "console": LOG_CONSOLE,
784 "cron": LOG_CRON,
785 "daemon": LOG_DAEMON,
786 "ftp": LOG_FTP,
787 "kern": LOG_KERN,
788 "lpr": LOG_LPR,
789 "mail": LOG_MAIL,
790 "news": LOG_NEWS,
791 "ntp": LOG_NTP,
792 "security": LOG_SECURITY,
793 "solaris-cron": LOG_SOLCRON,
794 "syslog": LOG_SYSLOG,
795 "user": LOG_USER,
796 "uucp": LOG_UUCP,
797 "local0": LOG_LOCAL0,
798 "local1": LOG_LOCAL1,
799 "local2": LOG_LOCAL2,
800 "local3": LOG_LOCAL3,
801 "local4": LOG_LOCAL4,
802 "local5": LOG_LOCAL5,
803 "local6": LOG_LOCAL6,
804 "local7": LOG_LOCAL7,
Guido van Rossum57102f82002-11-13 16:15:58 +0000805 }
806
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000807 #The map below appears to be trivially lowercasing the key. However,
808 #there's more to it than meets the eye - in some locales, lowercasing
809 #gives unexpected results. See SF #1524081: in the Turkish locale,
810 #"INFO".lower() != "info"
811 priority_map = {
812 "DEBUG" : "debug",
813 "INFO" : "info",
814 "WARNING" : "warning",
815 "ERROR" : "error",
816 "CRITICAL" : "critical"
817 }
818
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000819 def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
Vinay Sajipe9170522013-04-22 10:07:40 +0100820 facility=LOG_USER, socktype=None):
Guido van Rossum57102f82002-11-13 16:15:58 +0000821 """
822 Initialize a handler.
823
Guido van Rossume7ba4952007-06-06 23:52:48 +0000824 If address is specified as a string, a UNIX socket is used. To log to a
825 local syslogd, "SysLogHandler(address="/dev/log")" can be used.
Vinay Sajip40589f42013-04-22 10:14:12 +0100826 If facility is not specified, LOG_USER is used. If socktype is
827 specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
828 socket type will be used. For Unix sockets, you can also specify a
829 socktype of None, in which case socket.SOCK_DGRAM will be used, falling
830 back to socket.SOCK_STREAM.
Guido van Rossum57102f82002-11-13 16:15:58 +0000831 """
832 logging.Handler.__init__(self)
833
834 self.address = address
835 self.facility = facility
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000836 self.socktype = socktype
837
Guido van Rossum13257902007-06-07 23:15:56 +0000838 if isinstance(address, str):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100839 self.unixsocket = True
Коренберг Марк1b038e02017-03-17 20:25:05 +0500840 # Syslog server may be unavailable during handler initialisation.
841 # C's openlog() function also ignores connection errors.
842 # Moreover, we ignore these errors while logging, so it not worse
843 # to ignore it also here.
844 try:
845 self._connect_unixsocket(address)
846 except OSError:
847 pass
Guido van Rossum57102f82002-11-13 16:15:58 +0000848 else:
Vinay Sajip5a35b062011-04-27 11:31:14 +0100849 self.unixsocket = False
Vinay Sajipe9170522013-04-22 10:07:40 +0100850 if socktype is None:
851 socktype = socket.SOCK_DGRAM
Xiang Zhang0b4b57d2017-06-01 21:11:56 +0800852 host, port = address
853 ress = socket.getaddrinfo(host, port, 0, socktype)
854 if not ress:
855 raise OSError("getaddrinfo returns an empty list")
856 for res in ress:
857 af, socktype, proto, _, sa = res
858 err = sock = None
859 try:
860 sock = socket.socket(af, socktype, proto)
861 if socktype == socket.SOCK_STREAM:
862 sock.connect(sa)
863 break
864 except OSError as exc:
865 err = exc
866 if sock is not None:
867 sock.close()
868 if err is not None:
869 raise err
870 self.socket = sock
Vinay Sajipe9170522013-04-22 10:07:40 +0100871 self.socktype = socktype
Guido van Rossum57102f82002-11-13 16:15:58 +0000872
Vinay Sajipa1974c12005-01-13 08:23:56 +0000873 def _connect_unixsocket(self, address):
Vinay Sajipe9170522013-04-22 10:07:40 +0100874 use_socktype = self.socktype
875 if use_socktype is None:
876 use_socktype = socket.SOCK_DGRAM
877 self.socket = socket.socket(socket.AF_UNIX, use_socktype)
Vinay Sajipa1974c12005-01-13 08:23:56 +0000878 try:
879 self.socket.connect(address)
Vinay Sajipe9170522013-04-22 10:07:40 +0100880 # it worked, so set self.socktype to the used type
881 self.socktype = use_socktype
Andrew Svetlov0832af62012-12-18 23:10:48 +0200882 except OSError:
Vinay Sajipa1974c12005-01-13 08:23:56 +0000883 self.socket.close()
Vinay Sajipe9170522013-04-22 10:07:40 +0100884 if self.socktype is not None:
885 # user didn't specify falling back, so fail
886 raise
887 use_socktype = socket.SOCK_STREAM
888 self.socket = socket.socket(socket.AF_UNIX, use_socktype)
889 try:
890 self.socket.connect(address)
891 # it worked, so set self.socktype to the used type
892 self.socktype = use_socktype
Vinay Sajip40589f42013-04-22 10:14:12 +0100893 except OSError:
Vinay Sajipe9170522013-04-22 10:07:40 +0100894 self.socket.close()
895 raise
Vinay Sajipa1974c12005-01-13 08:23:56 +0000896
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000897 def encodePriority(self, facility, priority):
Guido van Rossum57102f82002-11-13 16:15:58 +0000898 """
899 Encode the facility and priority. You can pass in strings or
900 integers - if strings are passed, the facility_names and
901 priority_names mapping dictionaries are used to convert them to
902 integers.
903 """
Guido van Rossum13257902007-06-07 23:15:56 +0000904 if isinstance(facility, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000905 facility = self.facility_names[facility]
Guido van Rossum13257902007-06-07 23:15:56 +0000906 if isinstance(priority, str):
Guido van Rossum57102f82002-11-13 16:15:58 +0000907 priority = self.priority_names[priority]
908 return (facility << 3) | priority
909
Xiang Zhang0b4b57d2017-06-01 21:11:56 +0800910 def close(self):
Guido van Rossum57102f82002-11-13 16:15:58 +0000911 """
912 Closes the socket.
913 """
Vinay Sajipf0509032012-02-23 20:49:08 +0000914 self.acquire()
915 try:
Vinay Sajip57c22372012-02-23 20:03:49 +0000916 self.socket.close()
Vinay Sajip0abf61d2012-02-23 19:45:52 +0000917 logging.Handler.close(self)
Vinay Sajipf0509032012-02-23 20:49:08 +0000918 finally:
919 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +0000920
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000921 def mapPriority(self, levelName):
922 """
923 Map a logging level name to a key in the priority_names map.
924 This is useful in two scenarios: when custom levels are being
925 used, and in the case where you can't do a straightforward
926 mapping by lowercasing the logging level name because of locale-
927 specific issues (see SF #1524081).
928 """
929 return self.priority_map.get(levelName, "warning")
930
Vinay Sajip2353e352011-06-27 15:40:06 +0100931 ident = '' # prepended to all messages
Vinay Sajip8168d102011-06-09 16:50:49 +0100932 append_nul = True # some old syslog daemons expect a NUL terminator
933
Guido van Rossum57102f82002-11-13 16:15:58 +0000934 def emit(self, record):
935 """
936 Emit a record.
937
938 The record is formatted, and then sent to the syslog server. If
939 exception information is present, it is NOT sent to the server.
940 """
Guido van Rossum57102f82002-11-13 16:15:58 +0000941 try:
Vinay Sajipc33a0cc2014-11-01 19:58:47 +0000942 msg = self.format(record)
943 if self.ident:
944 msg = self.ident + msg
945 if self.append_nul:
946 msg += '\000'
947
948 # We need to convert record level to lowercase, maybe this will
949 # change in the future.
950 prio = '<%d>' % self.encodePriority(self.facility,
951 self.mapPriority(record.levelname))
952 prio = prio.encode('utf-8')
953 # Message is a string. Convert to bytes as required by RFC 5424
954 msg = msg.encode('utf-8')
955 msg = prio + msg
Guido van Rossum57102f82002-11-13 16:15:58 +0000956 if self.unixsocket:
Vinay Sajipa1974c12005-01-13 08:23:56 +0000957 try:
958 self.socket.send(msg)
Andrew Svetlov0832af62012-12-18 23:10:48 +0200959 except OSError:
Vinay Sajip862b15e2013-05-16 22:57:02 +0100960 self.socket.close()
Vinay Sajipa1974c12005-01-13 08:23:56 +0000961 self._connect_unixsocket(self.address)
962 self.socket.send(msg)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000963 elif self.socktype == socket.SOCK_DGRAM:
Guido van Rossum57102f82002-11-13 16:15:58 +0000964 self.socket.sendto(msg, self.address)
Vinay Sajipcbabd7e2009-10-10 20:32:36 +0000965 else:
966 self.socket.sendall(msg)
Vinay Sajip8cf4eb12012-10-09 08:06:13 +0100967 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +0000968 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +0000969
970class SMTPHandler(logging.Handler):
971 """
972 A handler class which sends an SMTP email for each logging event.
973 """
Vinay Sajip540f2152009-12-06 17:57:11 +0000974 def __init__(self, mailhost, fromaddr, toaddrs, subject,
Vinay Sajip26308da2012-04-22 18:16:14 +0100975 credentials=None, secure=None, timeout=5.0):
Guido van Rossum57102f82002-11-13 16:15:58 +0000976 """
977 Initialize the handler.
978
979 Initialize the instance with the from and to addresses and subject
980 line of the email. To specify a non-standard SMTP port, use the
Guido van Rossum360e4b82007-05-14 22:51:27 +0000981 (host, port) tuple format for the mailhost argument. To specify
982 authentication credentials, supply a (username, password) tuple
Vinay Sajip25fcd222009-12-06 18:05:04 +0000983 for the credentials argument. To specify the use of a secure
984 protocol (TLS), pass in a tuple for the secure argument. This will
985 only be used when authentication credentials are supplied. The tuple
986 will be either an empty tuple, or a single-value tuple with the name
987 of a keyfile, or a 2-value tuple with the names of the keyfile and
988 certificate file. (This tuple is passed to the `starttls` method).
Vinay Sajip17160fd2012-03-15 12:02:08 +0000989 A timeout in seconds can be specified for the SMTP connection (the
990 default is one second).
Guido van Rossum57102f82002-11-13 16:15:58 +0000991 """
992 logging.Handler.__init__(self)
Vinay Sajip16c41ab2014-10-17 08:49:38 +0100993 if isinstance(mailhost, (list, tuple)):
Guido van Rossum360e4b82007-05-14 22:51:27 +0000994 self.mailhost, self.mailport = mailhost
Guido van Rossum57102f82002-11-13 16:15:58 +0000995 else:
Guido van Rossum360e4b82007-05-14 22:51:27 +0000996 self.mailhost, self.mailport = mailhost, None
Vinay Sajip16c41ab2014-10-17 08:49:38 +0100997 if isinstance(credentials, (list, tuple)):
Guido van Rossum360e4b82007-05-14 22:51:27 +0000998 self.username, self.password = credentials
999 else:
1000 self.username = None
Guido van Rossum57102f82002-11-13 16:15:58 +00001001 self.fromaddr = fromaddr
Guido van Rossum13257902007-06-07 23:15:56 +00001002 if isinstance(toaddrs, str):
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001003 toaddrs = [toaddrs]
Guido van Rossum57102f82002-11-13 16:15:58 +00001004 self.toaddrs = toaddrs
1005 self.subject = subject
Vinay Sajip540f2152009-12-06 17:57:11 +00001006 self.secure = secure
Vinay Sajip17160fd2012-03-15 12:02:08 +00001007 self.timeout = timeout
Guido van Rossum57102f82002-11-13 16:15:58 +00001008
1009 def getSubject(self, record):
1010 """
1011 Determine the subject for the email.
1012
1013 If you want to specify a subject line which is record-dependent,
1014 override this method.
1015 """
1016 return self.subject
1017
1018 def emit(self, record):
1019 """
1020 Emit a record.
1021
1022 Format the record and send it to the specified addressees.
1023 """
1024 try:
1025 import smtplib
Vinay Sajip277640a2015-10-17 16:13:10 +01001026 from email.message import EmailMessage
1027 import email.utils
1028
Guido van Rossum57102f82002-11-13 16:15:58 +00001029 port = self.mailport
1030 if not port:
1031 port = smtplib.SMTP_PORT
Vinay Sajip17160fd2012-03-15 12:02:08 +00001032 smtp = smtplib.SMTP(self.mailhost, port, timeout=self.timeout)
Vinay Sajip277640a2015-10-17 16:13:10 +01001033 msg = EmailMessage()
1034 msg['From'] = self.fromaddr
1035 msg['To'] = ','.join(self.toaddrs)
1036 msg['Subject'] = self.getSubject(record)
1037 msg['Date'] = email.utils.localtime()
1038 msg.set_content(self.format(record))
Guido van Rossum360e4b82007-05-14 22:51:27 +00001039 if self.username:
Vinay Sajip25fcd222009-12-06 18:05:04 +00001040 if self.secure is not None:
Vinay Sajip540f2152009-12-06 17:57:11 +00001041 smtp.ehlo()
Vinay Sajip25fcd222009-12-06 18:05:04 +00001042 smtp.starttls(*self.secure)
Vinay Sajip540f2152009-12-06 17:57:11 +00001043 smtp.ehlo()
Guido van Rossum360e4b82007-05-14 22:51:27 +00001044 smtp.login(self.username, self.password)
Vinay Sajip277640a2015-10-17 16:13:10 +01001045 smtp.send_message(msg)
Guido van Rossum57102f82002-11-13 16:15:58 +00001046 smtp.quit()
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001047 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001048 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001049
1050class NTEventLogHandler(logging.Handler):
1051 """
1052 A handler class which sends events to the NT Event Log. Adds a
1053 registry entry for the specified application name. If no dllname is
1054 provided, win32service.pyd (which contains some basic message
1055 placeholders) is used. Note that use of these placeholders will make
1056 your event logs big, as the entire message source is held in the log.
1057 If you want slimmer logs, you have to pass in the name of your own DLL
1058 which contains the message definitions you want to use in the event log.
1059 """
1060 def __init__(self, appname, dllname=None, logtype="Application"):
1061 logging.Handler.__init__(self)
1062 try:
1063 import win32evtlogutil, win32evtlog
1064 self.appname = appname
1065 self._welu = win32evtlogutil
1066 if not dllname:
1067 dllname = os.path.split(self._welu.__file__)
1068 dllname = os.path.split(dllname[0])
1069 dllname = os.path.join(dllname[0], r'win32service.pyd')
1070 self.dllname = dllname
1071 self.logtype = logtype
1072 self._welu.AddSourceToRegistry(appname, dllname, logtype)
1073 self.deftype = win32evtlog.EVENTLOG_ERROR_TYPE
1074 self.typemap = {
1075 logging.DEBUG : win32evtlog.EVENTLOG_INFORMATION_TYPE,
1076 logging.INFO : win32evtlog.EVENTLOG_INFORMATION_TYPE,
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001077 logging.WARNING : win32evtlog.EVENTLOG_WARNING_TYPE,
Guido van Rossum57102f82002-11-13 16:15:58 +00001078 logging.ERROR : win32evtlog.EVENTLOG_ERROR_TYPE,
1079 logging.CRITICAL: win32evtlog.EVENTLOG_ERROR_TYPE,
1080 }
Brett Cannoncd171c82013-07-04 17:43:24 -04001081 except ImportError:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001082 print("The Python Win32 extensions for NT (service, event "\
1083 "logging) appear not to be available.")
Guido van Rossum57102f82002-11-13 16:15:58 +00001084 self._welu = None
1085
1086 def getMessageID(self, record):
1087 """
1088 Return the message ID for the event record. If you are using your
1089 own messages, you could do this by having the msg passed to the
1090 logger being an ID rather than a formatting string. Then, in here,
1091 you could use a dictionary lookup to get the message ID. This
1092 version returns 1, which is the base message ID in win32service.pyd.
1093 """
1094 return 1
1095
1096 def getEventCategory(self, record):
1097 """
1098 Return the event category for the record.
1099
1100 Override this if you want to specify your own categories. This version
1101 returns 0.
1102 """
1103 return 0
1104
1105 def getEventType(self, record):
1106 """
1107 Return the event type for the record.
1108
1109 Override this if you want to specify your own types. This version does
1110 a mapping using the handler's typemap attribute, which is set up in
1111 __init__() to a dictionary which contains mappings for DEBUG, INFO,
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001112 WARNING, ERROR and CRITICAL. If you are using your own levels you will
Guido van Rossum57102f82002-11-13 16:15:58 +00001113 either need to override this method or place a suitable dictionary in
1114 the handler's typemap attribute.
1115 """
1116 return self.typemap.get(record.levelno, self.deftype)
1117
1118 def emit(self, record):
1119 """
1120 Emit a record.
1121
1122 Determine the message ID, event category and event type. Then
1123 log the message in the NT event log.
1124 """
1125 if self._welu:
1126 try:
1127 id = self.getMessageID(record)
1128 cat = self.getEventCategory(record)
1129 type = self.getEventType(record)
1130 msg = self.format(record)
1131 self._welu.ReportEvent(self.appname, id, cat, type, [msg])
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001132 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001133 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001134
1135 def close(self):
1136 """
1137 Clean up this handler.
1138
1139 You can remove the application name from the registry as a
1140 source of event log entries. However, if you do this, you will
1141 not be able to see the events as you intended in the Event Log
1142 Viewer - it needs to be able to access the registry to get the
1143 DLL name.
1144 """
1145 #self._welu.RemoveSourceFromRegistry(self.appname, self.logtype)
Vinay Sajip48cfe382004-02-20 13:17:27 +00001146 logging.Handler.close(self)
Guido van Rossum57102f82002-11-13 16:15:58 +00001147
1148class HTTPHandler(logging.Handler):
1149 """
1150 A class which sends records to a Web server, using either GET or
1151 POST semantics.
1152 """
Benjamin Peterson43052a12014-11-23 20:36:44 -06001153 def __init__(self, host, url, method="GET", secure=False, credentials=None,
1154 context=None):
Guido van Rossum57102f82002-11-13 16:15:58 +00001155 """
1156 Initialize the instance with the host, the request URL, and the method
1157 ("GET" or "POST")
1158 """
1159 logging.Handler.__init__(self)
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001160 method = method.upper()
Guido van Rossum57102f82002-11-13 16:15:58 +00001161 if method not in ["GET", "POST"]:
Collin Winterce36ad82007-08-30 01:19:48 +00001162 raise ValueError("method must be GET or POST")
Benjamin Peterson43052a12014-11-23 20:36:44 -06001163 if not secure and context is not None:
1164 raise ValueError("context parameter only makes sense "
1165 "with secure=True")
Guido van Rossum57102f82002-11-13 16:15:58 +00001166 self.host = host
1167 self.url = url
1168 self.method = method
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001169 self.secure = secure
1170 self.credentials = credentials
Benjamin Peterson43052a12014-11-23 20:36:44 -06001171 self.context = context
Guido van Rossum57102f82002-11-13 16:15:58 +00001172
Neal Norwitzf297bd12003-04-23 03:49:43 +00001173 def mapLogRecord(self, record):
1174 """
1175 Default implementation of mapping the log record into a dict
Vinay Sajip48cfe382004-02-20 13:17:27 +00001176 that is sent as the CGI data. Overwrite in your class.
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001177 Contributed by Franz Glasner.
Neal Norwitzf297bd12003-04-23 03:49:43 +00001178 """
1179 return record.__dict__
1180
l0rb22a9a542020-03-04 11:49:51 +01001181 def getConnection(self, host, secure):
1182 """
1183 get a HTTP[S]Connection.
1184
1185 Override when a custom connection is required, for example if
1186 there is a proxy.
1187 """
1188 import http.client
1189 if secure:
1190 connection = http.client.HTTPSConnection(host, context=self.context)
1191 else:
1192 connection = http.client.HTTPConnection(host)
1193 return connection
1194
Guido van Rossum57102f82002-11-13 16:15:58 +00001195 def emit(self, record):
1196 """
1197 Emit a record.
1198
Senthil Kumaran30e86a42010-08-09 20:01:35 +00001199 Send the record to the Web server as a percent-encoded dictionary
Guido van Rossum57102f82002-11-13 16:15:58 +00001200 """
1201 try:
l0rb22a9a542020-03-04 11:49:51 +01001202 import urllib.parse
Vinay Sajipb7935062005-10-11 13:15:31 +00001203 host = self.host
l0rb22a9a542020-03-04 11:49:51 +01001204 h = self.getConnection(host, self.secure)
Guido van Rossum57102f82002-11-13 16:15:58 +00001205 url = self.url
Georg Brandl029986a2008-06-23 11:44:14 +00001206 data = urllib.parse.urlencode(self.mapLogRecord(record))
Guido van Rossum57102f82002-11-13 16:15:58 +00001207 if self.method == "GET":
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001208 if (url.find('?') >= 0):
Guido van Rossum57102f82002-11-13 16:15:58 +00001209 sep = '&'
1210 else:
1211 sep = '?'
1212 url = url + "%c%s" % (sep, data)
1213 h.putrequest(self.method, url)
Vinay Sajipb7935062005-10-11 13:15:31 +00001214 # support multiple hosts on one IP address...
1215 # need to strip optional :port from host, if present
Neal Norwitz9d72bb42007-04-17 08:48:32 +00001216 i = host.find(":")
Vinay Sajipb7935062005-10-11 13:15:31 +00001217 if i >= 0:
1218 host = host[:i]
Vinay Sajipe96ba182017-11-19 18:36:17 +00001219 # See issue #30904: putrequest call above already adds this header
1220 # on Python 3.x.
1221 # h.putheader("Host", host)
Guido van Rossum57102f82002-11-13 16:15:58 +00001222 if self.method == "POST":
Vinay Sajipb7935062005-10-11 13:15:31 +00001223 h.putheader("Content-type",
1224 "application/x-www-form-urlencoded")
Guido van Rossum57102f82002-11-13 16:15:58 +00001225 h.putheader("Content-length", str(len(data)))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001226 if self.credentials:
1227 import base64
Vinay Sajip1bf197e2016-06-07 21:19:55 +01001228 s = ('%s:%s' % self.credentials).encode('utf-8')
1229 s = 'Basic ' + base64.b64encode(s).strip().decode('ascii')
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001230 h.putheader('Authorization', s)
Vinay Sajip0372e102011-05-05 12:59:14 +01001231 h.endheaders()
1232 if self.method == "POST":
1233 h.send(data.encode('utf-8'))
Vinay Sajipaf9d10a2010-09-13 20:40:30 +00001234 h.getresponse() #can't do anything with the result
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001235 except Exception:
Neal Norwitz6fa635d2003-02-18 14:20:07 +00001236 self.handleError(record)
Guido van Rossum57102f82002-11-13 16:15:58 +00001237
1238class BufferingHandler(logging.Handler):
1239 """
1240 A handler class which buffers logging records in memory. Whenever each
1241 record is added to the buffer, a check is made to see if the buffer should
1242 be flushed. If it should, then flush() is expected to do what's needed.
1243 """
1244 def __init__(self, capacity):
1245 """
1246 Initialize the handler with the buffer size.
1247 """
1248 logging.Handler.__init__(self)
1249 self.capacity = capacity
1250 self.buffer = []
1251
1252 def shouldFlush(self, record):
1253 """
1254 Should the handler flush its buffer?
1255
1256 Returns true if the buffer is up to capacity. This method can be
1257 overridden to implement custom flushing strategies.
1258 """
1259 return (len(self.buffer) >= self.capacity)
1260
1261 def emit(self, record):
1262 """
1263 Emit a record.
1264
1265 Append the record. If shouldFlush() tells us to, call flush() to process
1266 the buffer.
1267 """
1268 self.buffer.append(record)
1269 if self.shouldFlush(record):
1270 self.flush()
1271
1272 def flush(self):
1273 """
1274 Override to implement custom flushing behaviour.
1275
1276 This version just zaps the buffer to empty.
1277 """
Vinay Sajipf0509032012-02-23 20:49:08 +00001278 self.acquire()
1279 try:
Daniel Anderssond89cea12019-11-13 10:03:45 +01001280 self.buffer.clear()
Vinay Sajipf0509032012-02-23 20:49:08 +00001281 finally:
1282 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +00001283
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001284 def close(self):
1285 """
1286 Close the handler.
1287
1288 This version just flushes and chains to the parent class' close().
1289 """
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +03001290 try:
1291 self.flush()
1292 finally:
1293 logging.Handler.close(self)
Vinay Sajipf42d95e2004-02-21 22:14:34 +00001294
Guido van Rossum57102f82002-11-13 16:15:58 +00001295class MemoryHandler(BufferingHandler):
1296 """
1297 A handler class which buffers logging records in memory, periodically
1298 flushing them to a target handler. Flushing occurs whenever the buffer
1299 is full, or when an event of a certain severity or greater is seen.
1300 """
Vinay Sajipcccf6062016-07-22 16:27:31 +01001301 def __init__(self, capacity, flushLevel=logging.ERROR, target=None,
1302 flushOnClose=True):
Guido van Rossum57102f82002-11-13 16:15:58 +00001303 """
1304 Initialize the handler with the buffer size, the level at which
1305 flushing should occur and an optional target.
1306
1307 Note that without a target being set either here or via setTarget(),
1308 a MemoryHandler is no use to anyone!
Vinay Sajipcccf6062016-07-22 16:27:31 +01001309
1310 The ``flushOnClose`` argument is ``True`` for backward compatibility
1311 reasons - the old behaviour is that when the handler is closed, the
1312 buffer is flushed, even if the flush level hasn't been exceeded nor the
1313 capacity exceeded. To prevent this, set ``flushOnClose`` to ``False``.
Guido van Rossum57102f82002-11-13 16:15:58 +00001314 """
1315 BufferingHandler.__init__(self, capacity)
1316 self.flushLevel = flushLevel
1317 self.target = target
Vinay Sajipcccf6062016-07-22 16:27:31 +01001318 # See Issue #26559 for why this has been added
1319 self.flushOnClose = flushOnClose
Guido van Rossum57102f82002-11-13 16:15:58 +00001320
1321 def shouldFlush(self, record):
1322 """
1323 Check for buffer full or a record at the flushLevel or higher.
1324 """
1325 return (len(self.buffer) >= self.capacity) or \
1326 (record.levelno >= self.flushLevel)
1327
1328 def setTarget(self, target):
1329 """
1330 Set the target handler for this handler.
1331 """
Irit Katriel2353d772020-08-16 16:10:13 +01001332 self.acquire()
1333 try:
1334 self.target = target
1335 finally:
1336 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +00001337
1338 def flush(self):
1339 """
1340 For a MemoryHandler, flushing means just sending the buffered
1341 records to the target, if there is one. Override if you want
1342 different behaviour.
Vinay Sajipc84f0162010-09-21 11:25:39 +00001343
1344 The record buffer is also cleared by this operation.
Guido van Rossum57102f82002-11-13 16:15:58 +00001345 """
Vinay Sajipf0509032012-02-23 20:49:08 +00001346 self.acquire()
1347 try:
Vinay Sajip0abf61d2012-02-23 19:45:52 +00001348 if self.target:
1349 for record in self.buffer:
1350 self.target.handle(record)
Daniel Anderssond89cea12019-11-13 10:03:45 +01001351 self.buffer.clear()
Vinay Sajipf0509032012-02-23 20:49:08 +00001352 finally:
1353 self.release()
Guido van Rossum57102f82002-11-13 16:15:58 +00001354
1355 def close(self):
1356 """
Vinay Sajipcccf6062016-07-22 16:27:31 +01001357 Flush, if appropriately configured, set the target to None and lose the
1358 buffer.
Guido van Rossum57102f82002-11-13 16:15:58 +00001359 """
Vinay Sajipf0509032012-02-23 20:49:08 +00001360 try:
Vinay Sajipcccf6062016-07-22 16:27:31 +01001361 if self.flushOnClose:
1362 self.flush()
Vinay Sajipf0509032012-02-23 20:49:08 +00001363 finally:
Serhiy Storchaka7e7a3db2015-04-10 13:24:41 +03001364 self.acquire()
1365 try:
1366 self.target = None
1367 BufferingHandler.close(self)
1368 finally:
1369 self.release()
Vinay Sajip121a1c42010-09-08 10:46:15 +00001370
1371
1372class QueueHandler(logging.Handler):
1373 """
1374 This handler sends events to a queue. Typically, it would be used together
1375 with a multiprocessing Queue to centralise logging to file in one process
1376 (in a multi-process application), so as to avoid file write contention
1377 between processes.
1378
1379 This code is new in Python 3.2, but this class can be copy pasted into
1380 user code for use with earlier Python versions.
1381 """
1382
1383 def __init__(self, queue):
1384 """
1385 Initialise an instance, using the passed queue.
1386 """
1387 logging.Handler.__init__(self)
1388 self.queue = queue
1389
1390 def enqueue(self, record):
1391 """
1392 Enqueue a record.
1393
1394 The base implementation uses put_nowait. You may want to override
1395 this method if you want to use blocking, timeouts or custom queue
1396 implementations.
1397 """
1398 self.queue.put_nowait(record)
1399
Vinay Sajip0258ce82010-09-22 20:34:53 +00001400 def prepare(self, record):
1401 """
Vinay Sajip0637d492010-09-23 08:15:54 +00001402 Prepares a record for queuing. The object returned by this method is
1403 enqueued.
Vinay Sajip0258ce82010-09-22 20:34:53 +00001404
1405 The base implementation formats the record to merge the message
1406 and arguments, and removes unpickleable items from the record
1407 in-place.
1408
1409 You might want to override this method if you want to convert
1410 the record to a dict or JSON string, or send a modified copy
1411 of the record while leaving the original intact.
1412 """
1413 # The format operation gets traceback text into record.exc_text
favlladfe3442017-08-01 20:12:26 +02001414 # (if there's exception data), and also returns the formatted
1415 # message. We can then use this to replace the original
Vinay Sajip0258ce82010-09-22 20:34:53 +00001416 # msg + args, as these might be unpickleable. We also zap the
Cheryl Sabellad345bb42018-09-25 19:00:08 -04001417 # exc_info and exc_text attributes, as they are no longer
1418 # needed and, if not None, will typically not be pickleable.
favlladfe3442017-08-01 20:12:26 +02001419 msg = self.format(record)
Manjusakada6424e2019-01-23 15:08:38 +08001420 # bpo-35726: make copy of record to avoid affecting other handlers in the chain.
1421 record = copy.copy(record)
favlladfe3442017-08-01 20:12:26 +02001422 record.message = msg
1423 record.msg = msg
Vinay Sajip0258ce82010-09-22 20:34:53 +00001424 record.args = None
1425 record.exc_info = None
Cheryl Sabellad345bb42018-09-25 19:00:08 -04001426 record.exc_text = None
Vinay Sajip0258ce82010-09-22 20:34:53 +00001427 return record
1428
Vinay Sajip121a1c42010-09-08 10:46:15 +00001429 def emit(self, record):
1430 """
1431 Emit a record.
1432
Vinay Sajip0637d492010-09-23 08:15:54 +00001433 Writes the LogRecord to the queue, preparing it for pickling first.
Vinay Sajip121a1c42010-09-08 10:46:15 +00001434 """
1435 try:
Vinay Sajip0258ce82010-09-22 20:34:53 +00001436 self.enqueue(self.prepare(record))
Vinay Sajip8cf4eb12012-10-09 08:06:13 +01001437 except Exception:
Vinay Sajip121a1c42010-09-08 10:46:15 +00001438 self.handleError(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001439
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001440
1441class QueueListener(object):
1442 """
1443 This class implements an internal threaded listener which watches for
1444 LogRecords being added to a queue, removes them and passes them to a
1445 list of handlers for processing.
1446 """
1447 _sentinel = None
1448
1449 def __init__(self, queue, *handlers, respect_handler_level=False):
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001450 """
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001451 Initialise an instance with the specified queue and
1452 handlers.
Victor Stinnercafa2ef2011-05-02 16:11:28 +02001453 """
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001454 self.queue = queue
1455 self.handlers = handlers
1456 self._thread = None
1457 self.respect_handler_level = respect_handler_level
Vinay Sajip0637d492010-09-23 08:15:54 +00001458
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001459 def dequeue(self, block):
1460 """
1461 Dequeue a record and return it, optionally blocking.
Vinay Sajip0637d492010-09-23 08:15:54 +00001462
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001463 The base implementation uses get. You may want to override this method
1464 if you want to use timeouts or work with custom queue implementations.
1465 """
1466 return self.queue.get(block)
Vinay Sajip0637d492010-09-23 08:15:54 +00001467
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001468 def start(self):
1469 """
1470 Start the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001471
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001472 This starts up a background thread to monitor the queue for
1473 LogRecords to process.
1474 """
1475 self._thread = t = threading.Thread(target=self._monitor)
1476 t.daemon = True
1477 t.start()
Vinay Sajip0637d492010-09-23 08:15:54 +00001478
Boris Feld800d7862019-06-04 17:20:18 +02001479 def prepare(self, record):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001480 """
1481 Prepare a record for handling.
Vinay Sajip0637d492010-09-23 08:15:54 +00001482
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001483 This method just returns the passed-in record. You may want to
1484 override this method if you need to do any custom marshalling or
1485 manipulation of the record before passing it to the handlers.
1486 """
1487 return record
Vinay Sajip0637d492010-09-23 08:15:54 +00001488
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001489 def handle(self, record):
1490 """
1491 Handle a record.
Vinay Sajip0637d492010-09-23 08:15:54 +00001492
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001493 This just loops through the handlers offering them the record
1494 to handle.
1495 """
1496 record = self.prepare(record)
1497 for handler in self.handlers:
1498 if not self.respect_handler_level:
1499 process = True
1500 else:
1501 process = record.levelno >= handler.level
1502 if process:
1503 handler.handle(record)
Vinay Sajip0637d492010-09-23 08:15:54 +00001504
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001505 def _monitor(self):
1506 """
1507 Monitor the queue for records, and ask the handler
1508 to deal with them.
Vinay Sajip0637d492010-09-23 08:15:54 +00001509
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001510 This method runs on a separate, internal thread.
1511 The thread will terminate if it sees a sentinel object in the queue.
1512 """
1513 q = self.queue
1514 has_task_done = hasattr(q, 'task_done')
1515 while True:
1516 try:
1517 record = self.dequeue(True)
1518 if record is self._sentinel:
Bar Harel6b282e12019-06-01 12:19:09 +03001519 if has_task_done:
1520 q.task_done()
Vinay Sajip0637d492010-09-23 08:15:54 +00001521 break
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001522 self.handle(record)
1523 if has_task_done:
1524 q.task_done()
1525 except queue.Empty:
1526 break
Vinay Sajip0637d492010-09-23 08:15:54 +00001527
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001528 def enqueue_sentinel(self):
1529 """
1530 This is used to enqueue the sentinel record.
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001531
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001532 The base implementation uses put_nowait. You may want to override this
1533 method if you want to use timeouts or work with custom queue
1534 implementations.
1535 """
1536 self.queue.put_nowait(self._sentinel)
Vinay Sajipaa7c1792011-02-25 15:56:55 +00001537
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001538 def stop(self):
1539 """
1540 Stop the listener.
Vinay Sajip0637d492010-09-23 08:15:54 +00001541
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001542 This asks the thread to terminate, and then waits for it to do so.
1543 Note that if you don't call this before your application exits, there
1544 may be some records still left on the queue, which won't be processed.
1545 """
1546 self.enqueue_sentinel()
1547 self._thread.join()
1548 self._thread = None