blob: 62759c07017a34a45251f8f7bc9762403b219cd1 [file] [log] [blame]
Vinay Sajipca7b5042019-06-17 17:40:52 +01001# Copyright 2001-2019 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +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.
16
17"""Test harness for the logging module. Run all tests.
18
Vinay Sajipca7b5042019-06-17 17:40:52 +010019Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000020"""
21
Christian Heimes180510d2008-03-03 19:15:45 +000022import logging
23import logging.handlers
24import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000025
Benjamin Petersonf91df042009-02-13 02:50:59 +000026import codecs
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010027import configparser
Pablo Galindo137b0632018-10-16 15:17:57 +010028import copy
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Vinay Sajipdb8f4632016-09-08 01:37:03 +010030import pathlib
Christian Heimes180510d2008-03-03 19:15:45 +000031import pickle
32import io
33import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000034import json
Christian Heimes180510d2008-03-03 19:15:45 +000035import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000036import queue
Vinay Sajip66b8b082012-04-24 23:25:30 +010037import random
Christian Heimes180510d2008-03-03 19:15:45 +000038import re
Christian Heimes180510d2008-03-03 19:15:45 +000039import socket
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Rémi Lapeyre65f64b192019-03-15 07:53:34 +010043from test.support.script_helper import assert_python_ok, assert_python_failure
Vinay Sajip1feedb42014-05-31 08:19:12 +010044from test import support
Serhiy Storchaka16994912020-04-25 10:06:29 +030045from test.support import socket_helper
Serhiy Storchaka515fce42020-04-25 11:35:18 +030046from test.support.logging_helper import TestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000047import textwrap
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020048import threading
Vinay Sajip37eb3382011-04-26 20:26:41 +010049import time
Christian Heimes180510d2008-03-03 19:15:45 +000050import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000051import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000052import weakref
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020053
54import asyncore
55from http.server import HTTPServer, BaseHTTPRequestHandler
56import smtpd
57from urllib.parse import urlparse, parse_qs
58from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
59 ThreadingTCPServer, StreamRequestHandler)
60
Vinay Sajip60ccd822011-05-09 17:32:09 +010061try:
Vinay Sajip223349c2015-10-01 20:37:54 +010062 import win32evtlog, win32evtlogutil, pywintypes
Vinay Sajip60ccd822011-05-09 17:32:09 +010063except ImportError:
Vinay Sajip223349c2015-10-01 20:37:54 +010064 win32evtlog = win32evtlogutil = pywintypes = None
65
Eric V. Smith851cad72012-03-11 22:46:04 -070066try:
67 import zlib
68except ImportError:
69 pass
Christian Heimes18c66892008-02-17 13:31:39 +000070
Christian Heimes180510d2008-03-03 19:15:45 +000071class BaseTest(unittest.TestCase):
72
73 """Base class for logging tests."""
74
75 log_format = "%(name)s -> %(levelname)s: %(message)s"
Ezio Melotti3ffd29b2012-10-02 19:45:00 +030076 expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +000077 message_num = 0
78
79 def setUp(self):
80 """Setup the default logging stream to an internal StringIO instance,
81 so that we can examine log output as we want."""
Victor Stinner69669602017-08-18 23:47:54 +020082 self._threading_key = support.threading_setup()
83
Christian Heimes180510d2008-03-03 19:15:45 +000084 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000085 logging._acquireLock()
86 try:
Christian Heimes180510d2008-03-03 19:15:45 +000087 self.saved_handlers = logging._handlers.copy()
88 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000089 self.saved_loggers = saved_loggers = logger_dict.copy()
Vinay Sajip3b84eae2013-05-25 03:20:34 -070090 self.saved_name_to_level = logging._nameToLevel.copy()
91 self.saved_level_to_name = logging._levelToName.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000092 self.logger_states = logger_states = {}
93 for name in saved_loggers:
94 logger_states[name] = getattr(saved_loggers[name],
95 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000096 finally:
97 logging._releaseLock()
98
Florent Xicluna5252f9f2011-11-07 19:43:05 +010099 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000100 self.logger1 = logging.getLogger("\xab\xd7\xbb")
101 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000102
Christian Heimes180510d2008-03-03 19:15:45 +0000103 self.root_logger = logging.getLogger("")
104 self.original_logging_level = self.root_logger.getEffectiveLevel()
105
106 self.stream = io.StringIO()
107 self.root_logger.setLevel(logging.DEBUG)
108 self.root_hdlr = logging.StreamHandler(self.stream)
109 self.root_formatter = logging.Formatter(self.log_format)
110 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000111 if self.logger1.hasHandlers():
112 hlist = self.logger1.handlers + self.root_logger.handlers
113 raise AssertionError('Unexpected handlers: %s' % hlist)
114 if self.logger2.hasHandlers():
115 hlist = self.logger2.handlers + self.root_logger.handlers
116 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000117 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000118 self.assertTrue(self.logger1.hasHandlers())
119 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000120
121 def tearDown(self):
122 """Remove our logging stream, and restore the original logging
123 level."""
124 self.stream.close()
125 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000126 while self.root_logger.handlers:
127 h = self.root_logger.handlers[0]
128 self.root_logger.removeHandler(h)
129 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000130 self.root_logger.setLevel(self.original_logging_level)
131 logging._acquireLock()
132 try:
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700133 logging._levelToName.clear()
134 logging._levelToName.update(self.saved_level_to_name)
135 logging._nameToLevel.clear()
136 logging._nameToLevel.update(self.saved_name_to_level)
Christian Heimes180510d2008-03-03 19:15:45 +0000137 logging._handlers.clear()
138 logging._handlers.update(self.saved_handlers)
139 logging._handlerList[:] = self.saved_handler_list
Avram Lubkin78c18a92017-07-30 05:36:33 -0400140 manager = logging.getLogger().manager
141 manager.disable = 0
142 loggerDict = manager.loggerDict
Christian Heimes180510d2008-03-03 19:15:45 +0000143 loggerDict.clear()
144 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000145 logger_states = self.logger_states
146 for name in self.logger_states:
147 if logger_states[name] is not None:
148 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000149 finally:
150 logging._releaseLock()
151
Victor Stinner69669602017-08-18 23:47:54 +0200152 self.doCleanups()
153 support.threading_cleanup(*self._threading_key)
154
Vinay Sajip4ded5512012-10-02 15:56:16 +0100155 def assert_log_lines(self, expected_values, stream=None, pat=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000156 """Match the collected log lines against the regular expression
157 self.expected_log_pat, and compare the extracted group values to
158 the expected_values list of tuples."""
159 stream = stream or self.stream
Vinay Sajip4ded5512012-10-02 15:56:16 +0100160 pat = re.compile(pat or self.expected_log_pat)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300161 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100162 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000163 for actual, expected in zip(actual_lines, expected_values):
164 match = pat.search(actual)
165 if not match:
166 self.fail("Log line does not match expected pattern:\n" +
167 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000168 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000169 s = stream.read()
170 if s:
171 self.fail("Remaining output at end of log stream:\n" + s)
172
173 def next_message(self):
174 """Generate a message consisting solely of an auto-incrementing
175 integer."""
176 self.message_num += 1
177 return "%d" % self.message_num
178
179
180class BuiltinLevelsTest(BaseTest):
181 """Test builtin levels and their inheritance."""
182
183 def test_flat(self):
Mike53f7a7c2017-12-14 14:04:53 +0300184 # Logging levels in a flat logger namespace.
Christian Heimes180510d2008-03-03 19:15:45 +0000185 m = self.next_message
186
187 ERR = logging.getLogger("ERR")
188 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000189 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000190 INF.setLevel(logging.INFO)
191 DEB = logging.getLogger("DEB")
192 DEB.setLevel(logging.DEBUG)
193
194 # These should log.
195 ERR.log(logging.CRITICAL, m())
196 ERR.error(m())
197
198 INF.log(logging.CRITICAL, m())
199 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100200 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000201 INF.info(m())
202
203 DEB.log(logging.CRITICAL, m())
204 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100205 DEB.warning(m())
206 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000207 DEB.debug(m())
208
209 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100210 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000211 ERR.info(m())
212 ERR.debug(m())
213
214 INF.debug(m())
215
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000216 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000217 ('ERR', 'CRITICAL', '1'),
218 ('ERR', 'ERROR', '2'),
219 ('INF', 'CRITICAL', '3'),
220 ('INF', 'ERROR', '4'),
221 ('INF', 'WARNING', '5'),
222 ('INF', 'INFO', '6'),
223 ('DEB', 'CRITICAL', '7'),
224 ('DEB', 'ERROR', '8'),
225 ('DEB', 'WARNING', '9'),
226 ('DEB', 'INFO', '10'),
227 ('DEB', 'DEBUG', '11'),
228 ])
229
230 def test_nested_explicit(self):
231 # Logging levels in a nested namespace, all explicitly set.
232 m = self.next_message
233
234 INF = logging.getLogger("INF")
235 INF.setLevel(logging.INFO)
236 INF_ERR = logging.getLogger("INF.ERR")
237 INF_ERR.setLevel(logging.ERROR)
238
239 # These should log.
240 INF_ERR.log(logging.CRITICAL, m())
241 INF_ERR.error(m())
242
243 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100244 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000245 INF_ERR.info(m())
246 INF_ERR.debug(m())
247
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000248 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000249 ('INF.ERR', 'CRITICAL', '1'),
250 ('INF.ERR', 'ERROR', '2'),
251 ])
252
253 def test_nested_inherited(self):
Mike53f7a7c2017-12-14 14:04:53 +0300254 # Logging levels in a nested namespace, inherited from parent loggers.
Christian Heimes180510d2008-03-03 19:15:45 +0000255 m = self.next_message
256
257 INF = logging.getLogger("INF")
258 INF.setLevel(logging.INFO)
259 INF_ERR = logging.getLogger("INF.ERR")
260 INF_ERR.setLevel(logging.ERROR)
261 INF_UNDEF = logging.getLogger("INF.UNDEF")
262 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
263 UNDEF = logging.getLogger("UNDEF")
264
265 # These should log.
266 INF_UNDEF.log(logging.CRITICAL, m())
267 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100268 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000269 INF_UNDEF.info(m())
270 INF_ERR_UNDEF.log(logging.CRITICAL, m())
271 INF_ERR_UNDEF.error(m())
272
273 # These should not log.
274 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100275 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000276 INF_ERR_UNDEF.info(m())
277 INF_ERR_UNDEF.debug(m())
278
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000279 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000280 ('INF.UNDEF', 'CRITICAL', '1'),
281 ('INF.UNDEF', 'ERROR', '2'),
282 ('INF.UNDEF', 'WARNING', '3'),
283 ('INF.UNDEF', 'INFO', '4'),
284 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
285 ('INF.ERR.UNDEF', 'ERROR', '6'),
286 ])
287
288 def test_nested_with_virtual_parent(self):
289 # Logging levels when some parent does not exist yet.
290 m = self.next_message
291
292 INF = logging.getLogger("INF")
293 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
294 CHILD = logging.getLogger("INF.BADPARENT")
295 INF.setLevel(logging.INFO)
296
297 # These should log.
298 GRANDCHILD.log(logging.FATAL, m())
299 GRANDCHILD.info(m())
300 CHILD.log(logging.FATAL, m())
301 CHILD.info(m())
302
303 # These should not log.
304 GRANDCHILD.debug(m())
305 CHILD.debug(m())
306
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000307 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000308 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
309 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
310 ('INF.BADPARENT', 'CRITICAL', '3'),
311 ('INF.BADPARENT', 'INFO', '4'),
312 ])
313
Vinay Sajipd1d4fbf2014-09-11 23:06:09 +0100314 def test_regression_22386(self):
315 """See issue #22386 for more information."""
316 self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
317 self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
Christian Heimes180510d2008-03-03 19:15:45 +0000318
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100319 def test_issue27935(self):
320 fatal = logging.getLevelName('FATAL')
321 self.assertEqual(fatal, logging.FATAL)
322
Vinay Sajip924aaae2017-01-11 17:35:36 +0000323 def test_regression_29220(self):
324 """See issue #29220 for more information."""
325 logging.addLevelName(logging.INFO, '')
326 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
327 self.assertEqual(logging.getLevelName(logging.INFO), '')
328 self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
329 self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
330
Christian Heimes180510d2008-03-03 19:15:45 +0000331class BasicFilterTest(BaseTest):
332
333 """Test the bundled Filter class."""
334
335 def test_filter(self):
336 # Only messages satisfying the specified criteria pass through the
337 # filter.
338 filter_ = logging.Filter("spam.eggs")
339 handler = self.root_logger.handlers[0]
340 try:
341 handler.addFilter(filter_)
342 spam = logging.getLogger("spam")
343 spam_eggs = logging.getLogger("spam.eggs")
344 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
345 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
346
347 spam.info(self.next_message())
348 spam_eggs.info(self.next_message()) # Good.
349 spam_eggs_fish.info(self.next_message()) # Good.
350 spam_bakedbeans.info(self.next_message())
351
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000352 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000353 ('spam.eggs', 'INFO', '2'),
354 ('spam.eggs.fish', 'INFO', '3'),
355 ])
356 finally:
357 handler.removeFilter(filter_)
358
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000359 def test_callable_filter(self):
360 # Only messages satisfying the specified criteria pass through the
361 # filter.
362
363 def filterfunc(record):
364 parts = record.name.split('.')
365 prefix = '.'.join(parts[:2])
366 return prefix == 'spam.eggs'
367
368 handler = self.root_logger.handlers[0]
369 try:
370 handler.addFilter(filterfunc)
371 spam = logging.getLogger("spam")
372 spam_eggs = logging.getLogger("spam.eggs")
373 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
374 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
375
376 spam.info(self.next_message())
377 spam_eggs.info(self.next_message()) # Good.
378 spam_eggs_fish.info(self.next_message()) # Good.
379 spam_bakedbeans.info(self.next_message())
380
381 self.assert_log_lines([
382 ('spam.eggs', 'INFO', '2'),
383 ('spam.eggs.fish', 'INFO', '3'),
384 ])
385 finally:
386 handler.removeFilter(filterfunc)
387
Vinay Sajip985ef872011-04-26 19:34:04 +0100388 def test_empty_filter(self):
389 f = logging.Filter()
390 r = logging.makeLogRecord({'name': 'spam.eggs'})
391 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000392
393#
394# First, we define our levels. There can be as many as you want - the only
395# limitations are that they should be integers, the lowest should be > 0 and
396# larger values mean less information being logged. If you need specific
397# level values which do not fit into these limitations, you can use a
398# mapping dictionary to convert between your application levels and the
399# logging system.
400#
401SILENT = 120
402TACITURN = 119
403TERSE = 118
404EFFUSIVE = 117
405SOCIABLE = 116
406VERBOSE = 115
407TALKATIVE = 114
408GARRULOUS = 113
409CHATTERBOX = 112
410BORING = 111
411
412LEVEL_RANGE = range(BORING, SILENT + 1)
413
414#
415# Next, we define names for our levels. You don't need to do this - in which
416# case the system will use "Level n" to denote the text for the level.
417#
418my_logging_levels = {
419 SILENT : 'Silent',
420 TACITURN : 'Taciturn',
421 TERSE : 'Terse',
422 EFFUSIVE : 'Effusive',
423 SOCIABLE : 'Sociable',
424 VERBOSE : 'Verbose',
425 TALKATIVE : 'Talkative',
426 GARRULOUS : 'Garrulous',
427 CHATTERBOX : 'Chatterbox',
428 BORING : 'Boring',
429}
430
431class GarrulousFilter(logging.Filter):
432
433 """A filter which blocks garrulous messages."""
434
435 def filter(self, record):
436 return record.levelno != GARRULOUS
437
438class VerySpecificFilter(logging.Filter):
439
440 """A filter which blocks sociable and taciturn messages."""
441
442 def filter(self, record):
443 return record.levelno not in [SOCIABLE, TACITURN]
444
445
446class CustomLevelsAndFiltersTest(BaseTest):
447
448 """Test various filtering possibilities with custom logging levels."""
449
450 # Skip the logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +0300451 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +0000452
453 def setUp(self):
454 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000455 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000456 logging.addLevelName(k, v)
457
458 def log_at_all_levels(self, logger):
459 for lvl in LEVEL_RANGE:
460 logger.log(lvl, self.next_message())
461
462 def test_logger_filter(self):
463 # Filter at logger level.
464 self.root_logger.setLevel(VERBOSE)
465 # Levels >= 'Verbose' are good.
466 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000467 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000468 ('Verbose', '5'),
469 ('Sociable', '6'),
470 ('Effusive', '7'),
471 ('Terse', '8'),
472 ('Taciturn', '9'),
473 ('Silent', '10'),
474 ])
475
476 def test_handler_filter(self):
477 # Filter at handler level.
478 self.root_logger.handlers[0].setLevel(SOCIABLE)
479 try:
480 # Levels >= 'Sociable' are good.
481 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000482 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000483 ('Sociable', '6'),
484 ('Effusive', '7'),
485 ('Terse', '8'),
486 ('Taciturn', '9'),
487 ('Silent', '10'),
488 ])
489 finally:
490 self.root_logger.handlers[0].setLevel(logging.NOTSET)
491
492 def test_specific_filters(self):
493 # Set a specific filter object on the handler, and then add another
494 # filter object on the logger itself.
495 handler = self.root_logger.handlers[0]
496 specific_filter = None
497 garr = GarrulousFilter()
498 handler.addFilter(garr)
499 try:
500 self.log_at_all_levels(self.root_logger)
501 first_lines = [
502 # Notice how 'Garrulous' is missing
503 ('Boring', '1'),
504 ('Chatterbox', '2'),
505 ('Talkative', '4'),
506 ('Verbose', '5'),
507 ('Sociable', '6'),
508 ('Effusive', '7'),
509 ('Terse', '8'),
510 ('Taciturn', '9'),
511 ('Silent', '10'),
512 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000513 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000514
515 specific_filter = VerySpecificFilter()
516 self.root_logger.addFilter(specific_filter)
517 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000518 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000519 # Not only 'Garrulous' is still missing, but also 'Sociable'
520 # and 'Taciturn'
521 ('Boring', '11'),
522 ('Chatterbox', '12'),
523 ('Talkative', '14'),
524 ('Verbose', '15'),
525 ('Effusive', '17'),
526 ('Terse', '18'),
527 ('Silent', '20'),
528 ])
529 finally:
530 if specific_filter:
531 self.root_logger.removeFilter(specific_filter)
532 handler.removeFilter(garr)
533
534
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100535class HandlerTest(BaseTest):
536 def test_name(self):
537 h = logging.Handler()
538 h.name = 'generic'
539 self.assertEqual(h.name, 'generic')
540 h.name = 'anothergeneric'
541 self.assertEqual(h.name, 'anothergeneric')
542 self.assertRaises(NotImplementedError, h.emit, None)
543
Vinay Sajip5a35b062011-04-27 11:31:14 +0100544 def test_builtin_handlers(self):
545 # We can't actually *use* too many handlers in the tests,
546 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200547 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100548 for existing in (True, False):
549 fd, fn = tempfile.mkstemp()
550 os.close(fd)
551 if not existing:
552 os.unlink(fn)
553 h = logging.handlers.WatchedFileHandler(fn, delay=True)
554 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100555 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100556 self.assertEqual(dev, -1)
557 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100558 r = logging.makeLogRecord({'msg': 'Test'})
559 h.handle(r)
560 # Now remove the file.
561 os.unlink(fn)
562 self.assertFalse(os.path.exists(fn))
563 # The next call should recreate the file.
564 h.handle(r)
565 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100566 else:
567 self.assertEqual(h.dev, -1)
568 self.assertEqual(h.ino, -1)
569 h.close()
570 if existing:
571 os.unlink(fn)
572 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100573 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100574 else:
575 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100576 try:
577 h = logging.handlers.SysLogHandler(sockname)
578 self.assertEqual(h.facility, h.LOG_USER)
579 self.assertTrue(h.unixsocket)
580 h.close()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200581 except OSError: # syslogd might not be available
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100582 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100583 for method in ('GET', 'POST', 'PUT'):
584 if method == 'PUT':
585 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
586 'localhost', '/log', method)
587 else:
588 h = logging.handlers.HTTPHandler('localhost', '/log', method)
589 h.close()
590 h = logging.handlers.BufferingHandler(0)
591 r = logging.makeLogRecord({})
592 self.assertTrue(h.shouldFlush(r))
593 h.close()
594 h = logging.handlers.BufferingHandler(1)
595 self.assertFalse(h.shouldFlush(r))
596 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100597
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100598 def test_path_objects(self):
599 """
600 Test that Path objects are accepted as filename arguments to handlers.
601
602 See Issue #27493.
603 """
604 fd, fn = tempfile.mkstemp()
605 os.close(fd)
606 os.unlink(fn)
607 pfn = pathlib.Path(fn)
608 cases = (
609 (logging.FileHandler, (pfn, 'w')),
610 (logging.handlers.RotatingFileHandler, (pfn, 'a')),
611 (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
612 )
613 if sys.platform in ('linux', 'darwin'):
614 cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
615 for cls, args in cases:
616 h = cls(*args)
617 self.assertTrue(os.path.exists(fn))
618 h.close()
619 os.unlink(fn)
620
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100621 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100622 def test_race(self):
623 # Issue #14632 refers.
624 def remove_loop(fname, tries):
625 for _ in range(tries):
626 try:
627 os.unlink(fname)
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000628 self.deletion_time = time.time()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100629 except OSError:
630 pass
631 time.sleep(0.004 * random.randint(0, 4))
632
Vinay Sajipc94871a2012-04-25 10:51:35 +0100633 del_count = 500
634 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100635
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000636 self.handle_time = None
637 self.deletion_time = None
638
Vinay Sajipa5798de2012-04-24 23:33:33 +0100639 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100640 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
641 os.close(fd)
642 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
643 remover.daemon = True
644 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100645 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100646 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
647 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100648 try:
649 for _ in range(log_count):
650 time.sleep(0.005)
651 r = logging.makeLogRecord({'msg': 'testing' })
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000652 try:
653 self.handle_time = time.time()
654 h.handle(r)
655 except Exception:
656 print('Deleted at %s, '
657 'opened at %s' % (self.deletion_time,
658 self.handle_time))
659 raise
Vinay Sajipc94871a2012-04-25 10:51:35 +0100660 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100661 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100662 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100663 if os.path.exists(fn):
664 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100665
Gregory P. Smith19003842018-09-13 22:08:31 -0700666 # The implementation relies on os.register_at_fork existing, but we test
667 # based on os.fork existing because that is what users and this test use.
668 # This helps ensure that when fork exists (the important concept) that the
669 # register_at_fork mechanism is also present and used.
670 @unittest.skipIf(not hasattr(os, 'fork'), 'Test requires os.fork().')
671 def test_post_fork_child_no_deadlock(self):
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400672 """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
673 class _OurHandler(logging.Handler):
674 def __init__(self):
675 super().__init__()
676 self.sub_handler = logging.StreamHandler(
677 stream=open('/dev/null', 'wt'))
678
679 def emit(self, record):
680 self.sub_handler.acquire()
681 try:
682 self.sub_handler.emit(record)
683 finally:
684 self.sub_handler.release()
685
686 self.assertEqual(len(logging._handlers), 0)
687 refed_h = _OurHandler()
Xtreak2c105382019-05-13 20:18:52 +0530688 self.addCleanup(refed_h.sub_handler.stream.close)
Gregory P. Smith19003842018-09-13 22:08:31 -0700689 refed_h.name = 'because we need at least one for this test'
690 self.assertGreater(len(logging._handlers), 0)
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400691 self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
692 test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
693 test_logger.addHandler(refed_h)
694 test_logger.setLevel(logging.DEBUG)
Gregory P. Smith19003842018-09-13 22:08:31 -0700695
696 locks_held__ready_to_fork = threading.Event()
697 fork_happened__release_locks_and_end_thread = threading.Event()
698
699 def lock_holder_thread_fn():
700 logging._acquireLock()
701 try:
702 refed_h.acquire()
703 try:
704 # Tell the main thread to do the fork.
705 locks_held__ready_to_fork.set()
706
707 # If the deadlock bug exists, the fork will happen
708 # without dealing with the locks we hold, deadlocking
709 # the child.
710
711 # Wait for a successful fork or an unreasonable amount of
712 # time before releasing our locks. To avoid a timing based
713 # test we'd need communication from os.fork() as to when it
714 # has actually happened. Given this is a regression test
715 # for a fixed issue, potentially less reliably detecting
716 # regression via timing is acceptable for simplicity.
717 # The test will always take at least this long. :(
718 fork_happened__release_locks_and_end_thread.wait(0.5)
719 finally:
720 refed_h.release()
721 finally:
722 logging._releaseLock()
723
724 lock_holder_thread = threading.Thread(
725 target=lock_holder_thread_fn,
726 name='test_post_fork_child_no_deadlock lock holder')
727 lock_holder_thread.start()
728
729 locks_held__ready_to_fork.wait()
730 pid = os.fork()
Victor Stinner278c1e12020-03-31 20:08:12 +0200731 if pid == 0:
732 # Child process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400733 try:
734 test_logger.info(r'Child process did not deadlock. \o/')
735 finally:
736 os._exit(0)
Victor Stinner278c1e12020-03-31 20:08:12 +0200737 else:
738 # Parent process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400739 test_logger.info(r'Parent process returned from fork. \o/')
Gregory P. Smith19003842018-09-13 22:08:31 -0700740 fork_happened__release_locks_and_end_thread.set()
741 lock_holder_thread.join()
Victor Stinner278c1e12020-03-31 20:08:12 +0200742
743 support.wait_process(pid, exitcode=0)
Gregory P. Smith19003842018-09-13 22:08:31 -0700744
Vinay Sajipa5798de2012-04-24 23:33:33 +0100745
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100746class BadStream(object):
747 def write(self, data):
748 raise RuntimeError('deliberate mistake')
749
750class TestStreamHandler(logging.StreamHandler):
751 def handleError(self, record):
752 self.error_record = record
753
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200754class StreamWithIntName(object):
755 level = logging.NOTSET
756 name = 2
757
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100758class StreamHandlerTest(BaseTest):
759 def test_error_handling(self):
760 h = TestStreamHandler(BadStream())
761 r = logging.makeLogRecord({})
762 old_raise = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +0100763
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100764 try:
765 h.handle(r)
766 self.assertIs(h.error_record, r)
Vinay Sajip1feedb42014-05-31 08:19:12 +0100767
Vinay Sajip985ef872011-04-26 19:34:04 +0100768 h = logging.StreamHandler(BadStream())
Vinay Sajip1feedb42014-05-31 08:19:12 +0100769 with support.captured_stderr() as stderr:
770 h.handle(r)
771 msg = '\nRuntimeError: deliberate mistake\n'
772 self.assertIn(msg, stderr.getvalue())
773
Vinay Sajip985ef872011-04-26 19:34:04 +0100774 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +0100775 with support.captured_stderr() as stderr:
776 h.handle(r)
777 self.assertEqual('', stderr.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100778 finally:
779 logging.raiseExceptions = old_raise
780
Vinay Sajip2543f502017-07-30 10:41:45 +0100781 def test_stream_setting(self):
782 """
783 Test setting the handler's stream
784 """
785 h = logging.StreamHandler()
786 stream = io.StringIO()
787 old = h.setStream(stream)
788 self.assertIs(old, sys.stderr)
789 actual = h.setStream(old)
790 self.assertIs(actual, stream)
791 # test that setting to existing value returns None
792 actual = h.setStream(old)
793 self.assertIsNone(actual)
794
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200795 def test_can_represent_stream_with_int_name(self):
796 h = logging.StreamHandler(StreamWithIntName())
797 self.assertEqual(repr(h), '<StreamHandler 2 (NOTSET)>')
798
Vinay Sajip7367d082011-05-02 13:17:27 +0100799# -- The following section could be moved into a server_helper.py module
800# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100801
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200802class TestSMTPServer(smtpd.SMTPServer):
803 """
804 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100805
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200806 :param addr: A (host, port) tuple which the server listens on.
807 You can specify a port value of zero: the server's
808 *port* attribute will hold the actual port number
809 used, which can be used in client connections.
810 :param handler: A callable which will be called to process
811 incoming messages. The handler will be passed
812 the client address tuple, who the message is from,
813 a list of recipients and the message data.
814 :param poll_interval: The interval, in seconds, used in the underlying
815 :func:`select` or :func:`poll` call by
816 :func:`asyncore.loop`.
817 :param sockmap: A dictionary which will be used to hold
818 :class:`asyncore.dispatcher` instances used by
819 :func:`asyncore.loop`. This avoids changing the
820 :mod:`asyncore` module's global state.
821 """
822
823 def __init__(self, addr, handler, poll_interval, sockmap):
824 smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
825 decode_data=True)
826 self.port = self.socket.getsockname()[1]
827 self._handler = handler
828 self._thread = None
829 self.poll_interval = poll_interval
830
831 def process_message(self, peer, mailfrom, rcpttos, data):
832 """
833 Delegates to the handler passed in to the server's constructor.
834
835 Typically, this will be a test case method.
836 :param peer: The client (host, port) tuple.
837 :param mailfrom: The address of the sender.
838 :param rcpttos: The addresses of the recipients.
839 :param data: The message.
840 """
841 self._handler(peer, mailfrom, rcpttos, data)
842
843 def start(self):
844 """
845 Start the server running on a separate daemon thread.
846 """
847 self._thread = t = threading.Thread(target=self.serve_forever,
848 args=(self.poll_interval,))
849 t.setDaemon(True)
850 t.start()
851
852 def serve_forever(self, poll_interval):
853 """
854 Run the :mod:`asyncore` loop until normal termination
855 conditions arise.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100856 :param poll_interval: The interval, in seconds, used in the underlying
857 :func:`select` or :func:`poll` call by
858 :func:`asyncore.loop`.
859 """
Victor Stinner13ff2452018-01-22 18:32:50 +0100860 asyncore.loop(poll_interval, map=self._map)
Vinay Sajipce7c9782011-05-17 07:15:53 +0100861
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100862 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200863 """
864 Stop the thread by closing the server instance.
865 Wait for the server thread to terminate.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200866 """
867 self.close()
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100868 support.join_thread(self._thread)
Victor Stinnerb9b69002017-09-14 14:40:56 -0700869 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200870 asyncore.close_all(map=self._map, ignore_all=True)
Vinay Sajipa463d252011-04-30 21:52:48 +0100871
Vinay Sajip7367d082011-05-02 13:17:27 +0100872
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200873class ControlMixin(object):
874 """
875 This mixin is used to start a server on a separate thread, and
876 shut it down programmatically. Request handling is simplified - instead
877 of needing to derive a suitable RequestHandler subclass, you just
878 provide a callable which will be passed each received request to be
879 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100880
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200881 :param handler: A handler callable which will be called with a
882 single parameter - the request - in order to
883 process the request. This handler is called on the
884 server thread, effectively meaning that requests are
885 processed serially. While not quite Web scale ;-),
886 this should be fine for testing applications.
887 :param poll_interval: The polling interval in seconds.
888 """
889 def __init__(self, handler, poll_interval):
890 self._thread = None
891 self.poll_interval = poll_interval
892 self._handler = handler
893 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100894
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200895 def start(self):
896 """
897 Create a daemon thread to run the server, and start it.
898 """
899 self._thread = t = threading.Thread(target=self.serve_forever,
900 args=(self.poll_interval,))
901 t.setDaemon(True)
902 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100903
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200904 def serve_forever(self, poll_interval):
905 """
906 Run the server. Set the ready flag before entering the
907 service loop.
908 """
909 self.ready.set()
910 super(ControlMixin, self).serve_forever(poll_interval)
911
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100912 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200913 """
914 Tell the server thread to stop, and wait for it to do so.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200915 """
916 self.shutdown()
917 if self._thread is not None:
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100918 support.join_thread(self._thread)
Vinay Sajip7367d082011-05-02 13:17:27 +0100919 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200920 self.server_close()
921 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100922
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200923class TestHTTPServer(ControlMixin, HTTPServer):
924 """
925 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100926
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200927 :param addr: A tuple with the IP address and port to listen on.
928 :param handler: A handler callable which will be called with a
929 single parameter - the request - in order to
930 process the request.
931 :param poll_interval: The polling interval in seconds.
932 :param log: Pass ``True`` to enable log messages.
933 """
934 def __init__(self, addr, handler, poll_interval=0.5,
935 log=False, sslctx=None):
936 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
937 def __getattr__(self, name, default=None):
938 if name.startswith('do_'):
939 return self.process_request
940 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100941
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200942 def process_request(self):
943 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100944
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200945 def log_message(self, format, *args):
946 if log:
947 super(DelegatingHTTPRequestHandler,
948 self).log_message(format, *args)
949 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
950 ControlMixin.__init__(self, handler, poll_interval)
951 self.sslctx = sslctx
Vinay Sajip7367d082011-05-02 13:17:27 +0100952
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200953 def get_request(self):
954 try:
955 sock, addr = self.socket.accept()
956 if self.sslctx:
957 sock = self.sslctx.wrap_socket(sock, server_side=True)
958 except OSError as e:
959 # socket errors are silenced by the caller, print them here
960 sys.stderr.write("Got an error:\n%s\n" % e)
961 raise
962 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100963
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200964class TestTCPServer(ControlMixin, ThreadingTCPServer):
965 """
966 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100967
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200968 :param addr: A tuple with the IP address and port to listen on.
969 :param handler: A handler callable which will be called with a single
970 parameter - the request - in order to process the request.
971 :param poll_interval: The polling interval in seconds.
972 :bind_and_activate: If True (the default), binds the server and starts it
973 listening. If False, you need to call
974 :meth:`server_bind` and :meth:`server_activate` at
975 some later time before calling :meth:`start`, so that
976 the server will set up the socket and listen on it.
977 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100978
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200979 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100980
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200981 def __init__(self, addr, handler, poll_interval=0.5,
982 bind_and_activate=True):
983 class DelegatingTCPRequestHandler(StreamRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +0100984
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200985 def handle(self):
986 self.server._handler(self)
987 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
988 bind_and_activate)
989 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100990
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200991 def server_bind(self):
992 super(TestTCPServer, self).server_bind()
993 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100994
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200995class TestUDPServer(ControlMixin, ThreadingUDPServer):
996 """
997 A UDP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100998
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200999 :param addr: A tuple with the IP address and port to listen on.
1000 :param handler: A handler callable which will be called with a
1001 single parameter - the request - in order to
1002 process the request.
1003 :param poll_interval: The polling interval for shutdown requests,
1004 in seconds.
1005 :bind_and_activate: If True (the default), binds the server and
1006 starts it listening. If False, you need to
1007 call :meth:`server_bind` and
1008 :meth:`server_activate` at some later time
1009 before calling :meth:`start`, so that the server will
1010 set up the socket and listen on it.
1011 """
1012 def __init__(self, addr, handler, poll_interval=0.5,
1013 bind_and_activate=True):
1014 class DelegatingUDPRequestHandler(DatagramRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +01001015
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001016 def handle(self):
1017 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001018
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001019 def finish(self):
1020 data = self.wfile.getvalue()
1021 if data:
1022 try:
1023 super(DelegatingUDPRequestHandler, self).finish()
1024 except OSError:
1025 if not self.server._closed:
1026 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +01001027
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001028 ThreadingUDPServer.__init__(self, addr,
1029 DelegatingUDPRequestHandler,
1030 bind_and_activate)
1031 ControlMixin.__init__(self, handler, poll_interval)
1032 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +01001033
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001034 def server_bind(self):
1035 super(TestUDPServer, self).server_bind()
1036 self.port = self.socket.getsockname()[1]
Vinay Sajipce7c9782011-05-17 07:15:53 +01001037
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001038 def server_close(self):
1039 super(TestUDPServer, self).server_close()
1040 self._closed = True
Vinay Sajipce7c9782011-05-17 07:15:53 +01001041
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001042if hasattr(socket, "AF_UNIX"):
1043 class TestUnixStreamServer(TestTCPServer):
1044 address_family = socket.AF_UNIX
Vinay Sajipce7c9782011-05-17 07:15:53 +01001045
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001046 class TestUnixDatagramServer(TestUDPServer):
1047 address_family = socket.AF_UNIX
Vinay Sajip5421f352013-09-27 18:18:28 +01001048
Vinay Sajip7367d082011-05-02 13:17:27 +01001049# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +01001050
1051class SMTPHandlerTest(BaseTest):
Victor Stinnerc98b0192019-12-10 21:12:26 +01001052 # bpo-14314, bpo-19665, bpo-34092: don't wait forever
1053 TIMEOUT = support.LONG_TIMEOUT
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02001054
Vinay Sajipa463d252011-04-30 21:52:48 +01001055 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +01001056 sockmap = {}
Serhiy Storchaka16994912020-04-25 10:06:29 +03001057 server = TestSMTPServer((socket_helper.HOST, 0), self.process_message, 0.001,
Vinay Sajip7367d082011-05-02 13:17:27 +01001058 sockmap)
1059 server.start()
Serhiy Storchaka16994912020-04-25 10:06:29 +03001060 addr = (socket_helper.HOST, server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +00001061 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
1062 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001063 self.assertEqual(h.toaddrs, ['you'])
1064 self.messages = []
Vinay Sajip277640a2015-10-17 16:13:10 +01001065 r = logging.makeLogRecord({'msg': 'Hello \u2713'})
Vinay Sajip7367d082011-05-02 13:17:27 +01001066 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +01001067 h.handle(r)
Victor Stinner31b50b82018-07-11 12:35:38 +02001068 self.handled.wait(self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001069 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +00001070 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +01001071 self.assertEqual(len(self.messages), 1)
1072 peer, mailfrom, rcpttos, data = self.messages[0]
1073 self.assertEqual(mailfrom, 'me')
1074 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001075 self.assertIn('\nSubject: Log\n', data)
Vinay Sajip277640a2015-10-17 16:13:10 +01001076 self.assertTrue(data.endswith('\n\nHello \u2713'))
Vinay Sajipa463d252011-04-30 21:52:48 +01001077 h.close()
1078
1079 def process_message(self, *args):
1080 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +01001081 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +01001082
Christian Heimes180510d2008-03-03 19:15:45 +00001083class MemoryHandlerTest(BaseTest):
1084
1085 """Tests for the MemoryHandler."""
1086
1087 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001088 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001089
1090 def setUp(self):
1091 BaseTest.setUp(self)
1092 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001093 self.root_hdlr)
Christian Heimes180510d2008-03-03 19:15:45 +00001094 self.mem_logger = logging.getLogger('mem')
1095 self.mem_logger.propagate = 0
1096 self.mem_logger.addHandler(self.mem_hdlr)
1097
1098 def tearDown(self):
1099 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001100 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001101
1102 def test_flush(self):
1103 # The memory handler flushes to its target handler based on specific
1104 # criteria (message count and message level).
1105 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001106 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001107 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001108 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001109 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001110 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001111 lines = [
1112 ('DEBUG', '1'),
1113 ('INFO', '2'),
1114 ('WARNING', '3'),
1115 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001116 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001117 for n in (4, 14):
1118 for i in range(9):
1119 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001120 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001121 # This will flush because it's the 10th message since the last
1122 # flush.
1123 self.mem_logger.debug(self.next_message())
1124 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001125 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001126
1127 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001128 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001129
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001130 def test_flush_on_close(self):
1131 """
1132 Test that the flush-on-close configuration works as expected.
1133 """
1134 self.mem_logger.debug(self.next_message())
1135 self.assert_log_lines([])
1136 self.mem_logger.info(self.next_message())
1137 self.assert_log_lines([])
1138 self.mem_logger.removeHandler(self.mem_hdlr)
1139 # Default behaviour is to flush on close. Check that it happens.
1140 self.mem_hdlr.close()
1141 lines = [
1142 ('DEBUG', '1'),
1143 ('INFO', '2'),
1144 ]
1145 self.assert_log_lines(lines)
1146 # Now configure for flushing not to be done on close.
1147 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1148 self.root_hdlr,
1149 False)
1150 self.mem_logger.addHandler(self.mem_hdlr)
1151 self.mem_logger.debug(self.next_message())
1152 self.assert_log_lines(lines) # no change
1153 self.mem_logger.info(self.next_message())
1154 self.assert_log_lines(lines) # no change
1155 self.mem_logger.removeHandler(self.mem_hdlr)
1156 self.mem_hdlr.close()
1157 # assert that no new lines have been added
1158 self.assert_log_lines(lines) # no change
1159
Miss Islington (bot)2c050e52020-08-16 08:34:27 -07001160 def test_race_between_set_target_and_flush(self):
1161 class MockRaceConditionHandler:
1162 def __init__(self, mem_hdlr):
1163 self.mem_hdlr = mem_hdlr
1164
1165 def removeTarget(self):
1166 self.mem_hdlr.setTarget(None)
1167
1168 def handle(self, msg):
1169 t = threading.Thread(target=self.removeTarget)
1170 t.daemon = True
1171 t.start()
1172
1173 target = MockRaceConditionHandler(self.mem_hdlr)
1174 self.mem_hdlr.setTarget(target)
1175
1176 for _ in range(10):
1177 time.sleep(0.005)
1178 self.mem_logger.info("not flushed")
1179 self.mem_logger.warning("flushed")
1180
Christian Heimes180510d2008-03-03 19:15:45 +00001181
1182class ExceptionFormatter(logging.Formatter):
1183 """A special exception formatter."""
1184 def formatException(self, ei):
1185 return "Got a [%s]" % ei[0].__name__
1186
1187
1188class ConfigFileTest(BaseTest):
1189
1190 """Reading logging config from a .ini-style config file."""
1191
Xtreak087570a2018-07-02 14:27:46 +05301192 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001193 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001194
1195 # config0 is a standard configuration.
1196 config0 = """
1197 [loggers]
1198 keys=root
1199
1200 [handlers]
1201 keys=hand1
1202
1203 [formatters]
1204 keys=form1
1205
1206 [logger_root]
1207 level=WARNING
1208 handlers=hand1
1209
1210 [handler_hand1]
1211 class=StreamHandler
1212 level=NOTSET
1213 formatter=form1
1214 args=(sys.stdout,)
1215
1216 [formatter_form1]
1217 format=%(levelname)s ++ %(message)s
1218 datefmt=
1219 """
1220
1221 # config1 adds a little to the standard configuration.
1222 config1 = """
1223 [loggers]
1224 keys=root,parser
1225
1226 [handlers]
1227 keys=hand1
1228
1229 [formatters]
1230 keys=form1
1231
1232 [logger_root]
1233 level=WARNING
1234 handlers=
1235
1236 [logger_parser]
1237 level=DEBUG
1238 handlers=hand1
1239 propagate=1
1240 qualname=compiler.parser
1241
1242 [handler_hand1]
1243 class=StreamHandler
1244 level=NOTSET
1245 formatter=form1
1246 args=(sys.stdout,)
1247
1248 [formatter_form1]
1249 format=%(levelname)s ++ %(message)s
1250 datefmt=
1251 """
1252
Vinay Sajip3f84b072011-03-07 17:49:33 +00001253 # config1a moves the handler to the root.
1254 config1a = """
1255 [loggers]
1256 keys=root,parser
1257
1258 [handlers]
1259 keys=hand1
1260
1261 [formatters]
1262 keys=form1
1263
1264 [logger_root]
1265 level=WARNING
1266 handlers=hand1
1267
1268 [logger_parser]
1269 level=DEBUG
1270 handlers=
1271 propagate=1
1272 qualname=compiler.parser
1273
1274 [handler_hand1]
1275 class=StreamHandler
1276 level=NOTSET
1277 formatter=form1
1278 args=(sys.stdout,)
1279
1280 [formatter_form1]
1281 format=%(levelname)s ++ %(message)s
1282 datefmt=
1283 """
1284
Christian Heimes180510d2008-03-03 19:15:45 +00001285 # config2 has a subtle configuration error that should be reported
1286 config2 = config1.replace("sys.stdout", "sys.stbout")
1287
1288 # config3 has a less subtle configuration error
1289 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1290
1291 # config4 specifies a custom formatter class to be loaded
1292 config4 = """
1293 [loggers]
1294 keys=root
1295
1296 [handlers]
1297 keys=hand1
1298
1299 [formatters]
1300 keys=form1
1301
1302 [logger_root]
1303 level=NOTSET
1304 handlers=hand1
1305
1306 [handler_hand1]
1307 class=StreamHandler
1308 level=NOTSET
1309 formatter=form1
1310 args=(sys.stdout,)
1311
1312 [formatter_form1]
1313 class=""" + __name__ + """.ExceptionFormatter
1314 format=%(levelname)s:%(name)s:%(message)s
1315 datefmt=
1316 """
1317
Georg Brandl3dbca812008-07-23 16:10:53 +00001318 # config5 specifies a custom handler class to be loaded
1319 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1320
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001321 # config6 uses ', ' delimiters in the handlers and formatters sections
1322 config6 = """
1323 [loggers]
1324 keys=root,parser
1325
1326 [handlers]
1327 keys=hand1, hand2
1328
1329 [formatters]
1330 keys=form1, form2
1331
1332 [logger_root]
1333 level=WARNING
1334 handlers=
1335
1336 [logger_parser]
1337 level=DEBUG
1338 handlers=hand1
1339 propagate=1
1340 qualname=compiler.parser
1341
1342 [handler_hand1]
1343 class=StreamHandler
1344 level=NOTSET
1345 formatter=form1
1346 args=(sys.stdout,)
1347
1348 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001349 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001350 level=NOTSET
1351 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001352 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001353
1354 [formatter_form1]
1355 format=%(levelname)s ++ %(message)s
1356 datefmt=
1357
1358 [formatter_form2]
1359 format=%(message)s
1360 datefmt=
1361 """
1362
Preston Landers6ea56d22017-08-02 15:44:28 -05001363 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001364 config7 = """
1365 [loggers]
1366 keys=root,parser,compiler
1367
1368 [handlers]
1369 keys=hand1
1370
1371 [formatters]
1372 keys=form1
1373
1374 [logger_root]
1375 level=WARNING
1376 handlers=hand1
1377
1378 [logger_compiler]
1379 level=DEBUG
1380 handlers=
1381 propagate=1
1382 qualname=compiler
1383
1384 [logger_parser]
1385 level=DEBUG
1386 handlers=
1387 propagate=1
1388 qualname=compiler.parser
1389
1390 [handler_hand1]
1391 class=StreamHandler
1392 level=NOTSET
1393 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001394 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001395
1396 [formatter_form1]
1397 format=%(levelname)s ++ %(message)s
1398 datefmt=
1399 """
1400
Xtreak087570a2018-07-02 14:27:46 +05301401 # config 8, check for resource warning
1402 config8 = r"""
1403 [loggers]
1404 keys=root
1405
1406 [handlers]
1407 keys=file
1408
1409 [formatters]
1410 keys=
1411
1412 [logger_root]
1413 level=DEBUG
1414 handlers=file
1415
1416 [handler_file]
1417 class=FileHandler
1418 level=DEBUG
1419 args=("{tempfile}",)
1420 """
1421
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001422 disable_test = """
1423 [loggers]
1424 keys=root
1425
1426 [handlers]
1427 keys=screen
1428
1429 [formatters]
1430 keys=
1431
1432 [logger_root]
1433 level=DEBUG
1434 handlers=screen
1435
1436 [handler_screen]
1437 level=DEBUG
1438 class=StreamHandler
1439 args=(sys.stdout,)
1440 formatter=
1441 """
1442
1443 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001444 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001445 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001446
1447 def test_config0_ok(self):
1448 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001449 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001450 self.apply_config(self.config0)
1451 logger = logging.getLogger()
1452 # Won't output anything
1453 logger.info(self.next_message())
1454 # Outputs a message
1455 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001456 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001457 ('ERROR', '2'),
1458 ], stream=output)
1459 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001460 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001461
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001462 def test_config0_using_cp_ok(self):
1463 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001464 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001465 file = io.StringIO(textwrap.dedent(self.config0))
1466 cp = configparser.ConfigParser()
1467 cp.read_file(file)
1468 logging.config.fileConfig(cp)
1469 logger = logging.getLogger()
1470 # Won't output anything
1471 logger.info(self.next_message())
1472 # Outputs a message
1473 logger.error(self.next_message())
1474 self.assert_log_lines([
1475 ('ERROR', '2'),
1476 ], stream=output)
1477 # Original logger output is empty.
1478 self.assert_log_lines([])
1479
Georg Brandl3dbca812008-07-23 16:10:53 +00001480 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001481 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001482 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001483 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001484 logger = logging.getLogger("compiler.parser")
1485 # Both will output a message
1486 logger.info(self.next_message())
1487 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001488 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001489 ('INFO', '1'),
1490 ('ERROR', '2'),
1491 ], stream=output)
1492 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001493 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001494
1495 def test_config2_failure(self):
1496 # A simple config file which overrides the default settings.
1497 self.assertRaises(Exception, self.apply_config, self.config2)
1498
1499 def test_config3_failure(self):
1500 # A simple config file which overrides the default settings.
1501 self.assertRaises(Exception, self.apply_config, self.config3)
1502
1503 def test_config4_ok(self):
1504 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001505 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001506 self.apply_config(self.config4)
1507 logger = logging.getLogger()
1508 try:
1509 raise RuntimeError()
1510 except RuntimeError:
1511 logging.exception("just testing")
1512 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001513 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001514 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1515 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001516 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001517
Georg Brandl3dbca812008-07-23 16:10:53 +00001518 def test_config5_ok(self):
1519 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001520
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001521 def test_config6_ok(self):
1522 self.test_config1_ok(config=self.config6)
1523
Vinay Sajip3f84b072011-03-07 17:49:33 +00001524 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001525 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001526 self.apply_config(self.config1a)
1527 logger = logging.getLogger("compiler.parser")
1528 # See issue #11424. compiler-hyphenated sorts
1529 # between compiler and compiler.xyz and this
1530 # was preventing compiler.xyz from being included
1531 # in the child loggers of compiler because of an
1532 # overzealous loop termination condition.
1533 hyphenated = logging.getLogger('compiler-hyphenated')
1534 # All will output a message
1535 logger.info(self.next_message())
1536 logger.error(self.next_message())
1537 hyphenated.critical(self.next_message())
1538 self.assert_log_lines([
1539 ('INFO', '1'),
1540 ('ERROR', '2'),
1541 ('CRITICAL', '3'),
1542 ], stream=output)
1543 # Original logger output is empty.
1544 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001545 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001546 self.apply_config(self.config7)
1547 logger = logging.getLogger("compiler.parser")
1548 self.assertFalse(logger.disabled)
1549 # Both will output a message
1550 logger.info(self.next_message())
1551 logger.error(self.next_message())
1552 logger = logging.getLogger("compiler.lexer")
1553 # Both will output a message
1554 logger.info(self.next_message())
1555 logger.error(self.next_message())
1556 # Will not appear
1557 hyphenated.critical(self.next_message())
1558 self.assert_log_lines([
1559 ('INFO', '4'),
1560 ('ERROR', '5'),
1561 ('INFO', '6'),
1562 ('ERROR', '7'),
1563 ], stream=output)
1564 # Original logger output is empty.
1565 self.assert_log_lines([])
1566
Xtreak087570a2018-07-02 14:27:46 +05301567 def test_config8_ok(self):
1568
1569 def cleanup(h1, fn):
1570 h1.close()
1571 os.remove(fn)
1572
1573 with self.check_no_resource_warning():
1574 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1575 os.close(fd)
1576
1577 # Replace single backslash with double backslash in windows
1578 # to avoid unicode error during string formatting
1579 if os.name == "nt":
1580 fn = fn.replace("\\", "\\\\")
1581
1582 config8 = self.config8.format(tempfile=fn)
1583
1584 self.apply_config(config8)
1585 self.apply_config(config8)
1586
1587 handler = logging.root.handlers[0]
1588 self.addCleanup(cleanup, handler, fn)
1589
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001590 def test_logger_disabling(self):
1591 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001592 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001593 self.assertFalse(logger.disabled)
1594 self.apply_config(self.disable_test)
1595 self.assertTrue(logger.disabled)
1596 self.apply_config(self.disable_test, disable_existing_loggers=False)
1597 self.assertFalse(logger.disabled)
1598
Lucas Cimonb15100f2019-10-31 09:06:25 +01001599 def test_config_set_handler_names(self):
1600 test_config = """
1601 [loggers]
1602 keys=root
1603
1604 [handlers]
1605 keys=hand1
1606
1607 [formatters]
1608 keys=form1
1609
1610 [logger_root]
1611 handlers=hand1
1612
1613 [handler_hand1]
1614 class=StreamHandler
1615 formatter=form1
1616
1617 [formatter_form1]
1618 format=%(levelname)s ++ %(message)s
1619 """
1620 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001621 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001622
Łukasz Langa214f18e2018-06-08 04:02:48 -07001623 def test_defaults_do_no_interpolation(self):
1624 """bpo-33802 defaults should not get interpolated"""
1625 ini = textwrap.dedent("""
1626 [formatters]
1627 keys=default
1628
1629 [formatter_default]
1630
1631 [handlers]
1632 keys=console
1633
1634 [handler_console]
1635 class=logging.StreamHandler
1636 args=tuple()
1637
1638 [loggers]
1639 keys=root
1640
1641 [logger_root]
1642 formatter=default
1643 handlers=console
1644 """).strip()
1645 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1646 try:
1647 os.write(fd, ini.encode('ascii'))
1648 os.close(fd)
1649 logging.config.fileConfig(
1650 fn,
1651 defaults=dict(
1652 version=1,
1653 disable_existing_loggers=False,
1654 formatters={
1655 "generic": {
1656 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1657 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1658 "class": "logging.Formatter"
1659 },
1660 },
1661 )
1662 )
1663 finally:
1664 os.unlink(fn)
1665
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001666
Christian Heimes180510d2008-03-03 19:15:45 +00001667class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001668
Christian Heimes180510d2008-03-03 19:15:45 +00001669 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001670
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001671 server_class = TestTCPServer
1672 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001673
Christian Heimes180510d2008-03-03 19:15:45 +00001674 def setUp(self):
1675 """Set up a TCP server to receive log messages, and a SocketHandler
1676 pointing to that server's address and port."""
1677 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001678 # Issue #29177: deal with errors that happen during setup
1679 self.server = self.sock_hdlr = self.server_exception = None
1680 try:
1681 self.server = server = self.server_class(self.address,
1682 self.handle_socket, 0.01)
1683 server.start()
1684 # Uncomment next line to test error recovery in setUp()
1685 # raise OSError('dummy error raised')
1686 except OSError as e:
1687 self.server_exception = e
1688 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001689 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001690 hcls = logging.handlers.SocketHandler
1691 if isinstance(server.server_address, tuple):
1692 self.sock_hdlr = hcls('localhost', server.port)
1693 else:
1694 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001695 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001696 self.root_logger.removeHandler(self.root_logger.handlers[0])
1697 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001698 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001699
Christian Heimes180510d2008-03-03 19:15:45 +00001700 def tearDown(self):
1701 """Shutdown the TCP server."""
1702 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001703 if self.sock_hdlr:
1704 self.root_logger.removeHandler(self.sock_hdlr)
1705 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001706 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001707 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001708 finally:
1709 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001710
Vinay Sajip7367d082011-05-02 13:17:27 +01001711 def handle_socket(self, request):
1712 conn = request.connection
1713 while True:
1714 chunk = conn.recv(4)
1715 if len(chunk) < 4:
1716 break
1717 slen = struct.unpack(">L", chunk)[0]
1718 chunk = conn.recv(slen)
1719 while len(chunk) < slen:
1720 chunk = chunk + conn.recv(slen - len(chunk))
1721 obj = pickle.loads(chunk)
1722 record = logging.makeLogRecord(obj)
1723 self.log_output += record.msg + '\n'
1724 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001725
Christian Heimes180510d2008-03-03 19:15:45 +00001726 def test_output(self):
1727 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001728 if self.server_exception:
1729 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001730 logger = logging.getLogger("tcp")
1731 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001732 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001733 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001734 self.handled.acquire()
1735 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001736
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001737 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001738 if self.server_exception:
1739 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001740 # Avoid timing-related failures due to SocketHandler's own hard-wired
1741 # one-second timeout on socket.create_connection() (issue #16264).
1742 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001743 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001744 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001745 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001746 try:
1747 raise RuntimeError('Deliberate mistake')
1748 except RuntimeError:
1749 self.root_logger.exception('Never sent')
1750 self.root_logger.error('Never sent, either')
1751 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001752 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001753 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1754 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001755
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001756def _get_temp_domain_socket():
1757 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1758 os.close(fd)
1759 # just need a name - file can't be present, or we'll get an
1760 # 'address already in use' error.
1761 os.remove(fn)
1762 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001763
Victor Stinnerec5a8602014-06-02 14:41:51 +02001764@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001765class UnixSocketHandlerTest(SocketHandlerTest):
1766
1767 """Test for SocketHandler with unix sockets."""
1768
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001769 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001770 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001771
1772 def setUp(self):
1773 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001774 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001775 SocketHandlerTest.setUp(self)
1776
1777 def tearDown(self):
1778 SocketHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001779 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001780
Vinay Sajip7367d082011-05-02 13:17:27 +01001781class DatagramHandlerTest(BaseTest):
1782
1783 """Test for DatagramHandler."""
1784
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001785 server_class = TestUDPServer
1786 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001787
Vinay Sajip7367d082011-05-02 13:17:27 +01001788 def setUp(self):
1789 """Set up a UDP server to receive log messages, and a DatagramHandler
1790 pointing to that server's address and port."""
1791 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001792 # Issue #29177: deal with errors that happen during setup
1793 self.server = self.sock_hdlr = self.server_exception = None
1794 try:
1795 self.server = server = self.server_class(self.address,
1796 self.handle_datagram, 0.01)
1797 server.start()
1798 # Uncomment next line to test error recovery in setUp()
1799 # raise OSError('dummy error raised')
1800 except OSError as e:
1801 self.server_exception = e
1802 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001803 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001804 hcls = logging.handlers.DatagramHandler
1805 if isinstance(server.server_address, tuple):
1806 self.sock_hdlr = hcls('localhost', server.port)
1807 else:
1808 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001809 self.log_output = ''
1810 self.root_logger.removeHandler(self.root_logger.handlers[0])
1811 self.root_logger.addHandler(self.sock_hdlr)
1812 self.handled = threading.Event()
1813
1814 def tearDown(self):
1815 """Shutdown the UDP server."""
1816 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001817 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001818 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001819 if self.sock_hdlr:
1820 self.root_logger.removeHandler(self.sock_hdlr)
1821 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001822 finally:
1823 BaseTest.tearDown(self)
1824
1825 def handle_datagram(self, request):
1826 slen = struct.pack('>L', 0) # length of prefix
1827 packet = request.packet[len(slen):]
1828 obj = pickle.loads(packet)
1829 record = logging.makeLogRecord(obj)
1830 self.log_output += record.msg + '\n'
1831 self.handled.set()
1832
1833 def test_output(self):
1834 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001835 if self.server_exception:
1836 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001837 logger = logging.getLogger("udp")
1838 logger.error("spam")
1839 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001840 self.handled.clear()
1841 logger.error("eggs")
1842 self.handled.wait()
1843 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001844
Victor Stinnerec5a8602014-06-02 14:41:51 +02001845@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001846class UnixDatagramHandlerTest(DatagramHandlerTest):
1847
1848 """Test for DatagramHandler using Unix sockets."""
1849
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001850 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001851 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001852
1853 def setUp(self):
1854 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001855 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001856 DatagramHandlerTest.setUp(self)
1857
1858 def tearDown(self):
1859 DatagramHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001860 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001861
Vinay Sajip7367d082011-05-02 13:17:27 +01001862class SysLogHandlerTest(BaseTest):
1863
1864 """Test for SysLogHandler using UDP."""
1865
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001866 server_class = TestUDPServer
1867 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001868
Vinay Sajip7367d082011-05-02 13:17:27 +01001869 def setUp(self):
1870 """Set up a UDP server to receive log messages, and a SysLogHandler
1871 pointing to that server's address and port."""
1872 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001873 # Issue #29177: deal with errors that happen during setup
1874 self.server = self.sl_hdlr = self.server_exception = None
1875 try:
1876 self.server = server = self.server_class(self.address,
1877 self.handle_datagram, 0.01)
1878 server.start()
1879 # Uncomment next line to test error recovery in setUp()
1880 # raise OSError('dummy error raised')
1881 except OSError as e:
1882 self.server_exception = e
1883 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001884 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001885 hcls = logging.handlers.SysLogHandler
1886 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001887 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001888 else:
1889 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001890 self.log_output = ''
1891 self.root_logger.removeHandler(self.root_logger.handlers[0])
1892 self.root_logger.addHandler(self.sl_hdlr)
1893 self.handled = threading.Event()
1894
1895 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001896 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001897 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001898 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001899 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001900 if self.sl_hdlr:
1901 self.root_logger.removeHandler(self.sl_hdlr)
1902 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001903 finally:
1904 BaseTest.tearDown(self)
1905
1906 def handle_datagram(self, request):
1907 self.log_output = request.packet
1908 self.handled.set()
1909
1910 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001911 if self.server_exception:
1912 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001913 # The log message sent to the SysLogHandler is properly received.
1914 logger = logging.getLogger("slh")
1915 logger.error("sp\xe4m")
1916 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001917 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001918 self.handled.clear()
1919 self.sl_hdlr.append_nul = False
1920 logger.error("sp\xe4m")
1921 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001922 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001923 self.handled.clear()
1924 self.sl_hdlr.ident = "h\xe4m-"
1925 logger.error("sp\xe4m")
1926 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001927 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001928
Victor Stinnerec5a8602014-06-02 14:41:51 +02001929@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001930class UnixSysLogHandlerTest(SysLogHandlerTest):
1931
1932 """Test for SysLogHandler with Unix sockets."""
1933
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001934 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001935 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001936
1937 def setUp(self):
1938 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001939 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001940 SysLogHandlerTest.setUp(self)
1941
1942 def tearDown(self):
1943 SysLogHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001944 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001945
Serhiy Storchaka16994912020-04-25 10:06:29 +03001946@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001947 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001948class IPv6SysLogHandlerTest(SysLogHandlerTest):
1949
1950 """Test for SysLogHandler with IPv6 host."""
1951
1952 server_class = TestUDPServer
1953 address = ('::1', 0)
1954
1955 def setUp(self):
1956 self.server_class.address_family = socket.AF_INET6
1957 super(IPv6SysLogHandlerTest, self).setUp()
1958
1959 def tearDown(self):
1960 self.server_class.address_family = socket.AF_INET
1961 super(IPv6SysLogHandlerTest, self).tearDown()
1962
Vinay Sajip7367d082011-05-02 13:17:27 +01001963class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001964 """Test for HTTPHandler."""
1965
1966 def setUp(self):
1967 """Set up an HTTP server to receive log messages, and a HTTPHandler
1968 pointing to that server's address and port."""
1969 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001970 self.handled = threading.Event()
1971
Vinay Sajip7367d082011-05-02 13:17:27 +01001972 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001973 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001974 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001975 if self.command == 'POST':
1976 try:
1977 rlen = int(request.headers['Content-Length'])
1978 self.post_data = request.rfile.read(rlen)
1979 except:
1980 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001981 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001982 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001983 self.handled.set()
1984
1985 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001986 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001987 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001988 root_logger = self.root_logger
1989 root_logger.removeHandler(self.root_logger.handlers[0])
1990 for secure in (False, True):
1991 addr = ('localhost', 0)
1992 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001993 try:
1994 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01001995 except ImportError:
1996 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001997 else:
1998 here = os.path.dirname(__file__)
1999 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02002000 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002001 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06002002
2003 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01002004 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01002005 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06002006 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01002007 self.server = server = TestHTTPServer(addr, self.handle_request,
2008 0.01, sslctx=sslctx)
2009 server.start()
2010 server.ready.wait()
2011 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01002012 secure_client = secure and sslctx
2013 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06002014 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01002015 context=context,
2016 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01002017 self.log_data = None
2018 root_logger.addHandler(self.h_hdlr)
2019
2020 for method in ('GET', 'POST'):
2021 self.h_hdlr.method = method
2022 self.handled.clear()
2023 msg = "sp\xe4m"
2024 logger.error(msg)
2025 self.handled.wait()
2026 self.assertEqual(self.log_data.path, '/frob')
2027 self.assertEqual(self.command, method)
2028 if method == 'GET':
2029 d = parse_qs(self.log_data.query)
2030 else:
2031 d = parse_qs(self.post_data.decode('utf-8'))
2032 self.assertEqual(d['name'], ['http'])
2033 self.assertEqual(d['funcName'], ['test_output'])
2034 self.assertEqual(d['msg'], [msg])
2035
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002036 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002037 self.root_logger.removeHandler(self.h_hdlr)
2038 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002039
Christian Heimes180510d2008-03-03 19:15:45 +00002040class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002041
Christian Heimes180510d2008-03-03 19:15:45 +00002042 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002043
Christian Heimes180510d2008-03-03 19:15:45 +00002044 def setUp(self):
2045 """Create a dict to remember potentially destroyed objects."""
2046 BaseTest.setUp(self)
2047 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002048
Christian Heimes180510d2008-03-03 19:15:45 +00002049 def _watch_for_survival(self, *args):
2050 """Watch the given objects for survival, by creating weakrefs to
2051 them."""
2052 for obj in args:
2053 key = id(obj), repr(obj)
2054 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002055
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002056 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002057 """Assert that all objects watched for survival have survived."""
2058 # Trigger cycle breaking.
2059 gc.collect()
2060 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002061 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002062 if ref() is None:
2063 dead.append(repr_)
2064 if dead:
2065 self.fail("%d objects should have survived "
2066 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002067
Christian Heimes180510d2008-03-03 19:15:45 +00002068 def test_persistent_loggers(self):
2069 # Logger objects are persistent and retain their configuration, even
2070 # if visible references are destroyed.
2071 self.root_logger.setLevel(logging.INFO)
2072 foo = logging.getLogger("foo")
2073 self._watch_for_survival(foo)
2074 foo.setLevel(logging.DEBUG)
2075 self.root_logger.debug(self.next_message())
2076 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002077 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002078 ('foo', 'DEBUG', '2'),
2079 ])
2080 del foo
2081 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002082 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002083 # foo has retained its settings.
2084 bar = logging.getLogger("foo")
2085 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002086 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002087 ('foo', 'DEBUG', '2'),
2088 ('foo', 'DEBUG', '3'),
2089 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002090
Benjamin Petersonf91df042009-02-13 02:50:59 +00002091
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002092class EncodingTest(BaseTest):
2093 def test_encoding_plain_file(self):
2094 # In Python 2.x, a plain file object is treated as having no encoding.
2095 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002096 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2097 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002098 # the non-ascii data we write to the log.
2099 data = "foo\x80"
2100 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002101 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002102 log.addHandler(handler)
2103 try:
2104 # write non-ascii data to the log.
2105 log.warning(data)
2106 finally:
2107 log.removeHandler(handler)
2108 handler.close()
2109 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002110 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002111 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002112 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002113 finally:
2114 f.close()
2115 finally:
2116 if os.path.isfile(fn):
2117 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002118
Benjamin Petersonf91df042009-02-13 02:50:59 +00002119 def test_encoding_cyrillic_unicode(self):
2120 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002121 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002122 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002123 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002124 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002125 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002126 stream = io.BytesIO()
2127 writer = writer_class(stream, 'strict')
2128 handler = logging.StreamHandler(writer)
2129 log.addHandler(handler)
2130 try:
2131 log.warning(message)
2132 finally:
2133 log.removeHandler(handler)
2134 handler.close()
2135 # check we wrote exactly those bytes, ignoring trailing \n etc
2136 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002137 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002138 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2139
2140
Georg Brandlf9734072008-12-07 15:30:06 +00002141class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002142
Georg Brandlf9734072008-12-07 15:30:06 +00002143 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002144 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002145 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002146 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002147 warnings.filterwarnings("always", category=UserWarning)
2148 stream = io.StringIO()
2149 h = logging.StreamHandler(stream)
2150 logger = logging.getLogger("py.warnings")
2151 logger.addHandler(h)
2152 warnings.warn("I'm warning you...")
2153 logger.removeHandler(h)
2154 s = stream.getvalue()
2155 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002156 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002157
Mike53f7a7c2017-12-14 14:04:53 +03002158 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002159 a_file = io.StringIO()
2160 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2161 a_file, "Dummy line")
2162 s = a_file.getvalue()
2163 a_file.close()
2164 self.assertEqual(s,
2165 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2166
2167 def test_warnings_no_handlers(self):
2168 with warnings.catch_warnings():
2169 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002170 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002171
2172 # confirm our assumption: no loggers are set
2173 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002174 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002175
2176 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002177 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002178 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002179
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002180
2181def formatFunc(format, datefmt=None):
2182 return logging.Formatter(format, datefmt)
2183
BNMetrics18fb1fb2018-10-15 19:41:36 +01002184class myCustomFormatter:
2185 def __init__(self, fmt, datefmt=None):
2186 pass
2187
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002188def handlerFunc():
2189 return logging.StreamHandler()
2190
2191class CustomHandler(logging.StreamHandler):
2192 pass
2193
2194class ConfigDictTest(BaseTest):
2195
2196 """Reading logging config from a dictionary."""
2197
Xtreak087570a2018-07-02 14:27:46 +05302198 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002199 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002200
2201 # config0 is a standard configuration.
2202 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002203 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002204 'formatters': {
2205 'form1' : {
2206 'format' : '%(levelname)s ++ %(message)s',
2207 },
2208 },
2209 'handlers' : {
2210 'hand1' : {
2211 'class' : 'logging.StreamHandler',
2212 'formatter' : 'form1',
2213 'level' : 'NOTSET',
2214 'stream' : 'ext://sys.stdout',
2215 },
2216 },
2217 'root' : {
2218 'level' : 'WARNING',
2219 'handlers' : ['hand1'],
2220 },
2221 }
2222
2223 # config1 adds a little to the standard configuration.
2224 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002225 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002226 'formatters': {
2227 'form1' : {
2228 'format' : '%(levelname)s ++ %(message)s',
2229 },
2230 },
2231 'handlers' : {
2232 'hand1' : {
2233 'class' : 'logging.StreamHandler',
2234 'formatter' : 'form1',
2235 'level' : 'NOTSET',
2236 'stream' : 'ext://sys.stdout',
2237 },
2238 },
2239 'loggers' : {
2240 'compiler.parser' : {
2241 'level' : 'DEBUG',
2242 'handlers' : ['hand1'],
2243 },
2244 },
2245 'root' : {
2246 'level' : 'WARNING',
2247 },
2248 }
2249
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002250 # config1a moves the handler to the root. Used with config8a
2251 config1a = {
2252 'version': 1,
2253 'formatters': {
2254 'form1' : {
2255 'format' : '%(levelname)s ++ %(message)s',
2256 },
2257 },
2258 'handlers' : {
2259 'hand1' : {
2260 'class' : 'logging.StreamHandler',
2261 'formatter' : 'form1',
2262 'level' : 'NOTSET',
2263 'stream' : 'ext://sys.stdout',
2264 },
2265 },
2266 'loggers' : {
2267 'compiler.parser' : {
2268 'level' : 'DEBUG',
2269 },
2270 },
2271 'root' : {
2272 'level' : 'WARNING',
2273 'handlers' : ['hand1'],
2274 },
2275 }
2276
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002277 # config2 has a subtle configuration error that should be reported
2278 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002279 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002280 'formatters': {
2281 'form1' : {
2282 'format' : '%(levelname)s ++ %(message)s',
2283 },
2284 },
2285 'handlers' : {
2286 'hand1' : {
2287 'class' : 'logging.StreamHandler',
2288 'formatter' : 'form1',
2289 'level' : 'NOTSET',
2290 'stream' : 'ext://sys.stdbout',
2291 },
2292 },
2293 'loggers' : {
2294 'compiler.parser' : {
2295 'level' : 'DEBUG',
2296 'handlers' : ['hand1'],
2297 },
2298 },
2299 'root' : {
2300 'level' : 'WARNING',
2301 },
2302 }
2303
Mike53f7a7c2017-12-14 14:04:53 +03002304 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002305 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002306 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002307 'formatters': {
2308 'form1' : {
2309 'format' : '%(levelname)s ++ %(message)s',
2310 },
2311 },
2312 'handlers' : {
2313 'hand1' : {
2314 'class' : 'logging.StreamHandler',
2315 'formatter' : 'form1',
2316 'level' : 'NTOSET',
2317 'stream' : 'ext://sys.stdout',
2318 },
2319 },
2320 'loggers' : {
2321 'compiler.parser' : {
2322 'level' : 'DEBUG',
2323 'handlers' : ['hand1'],
2324 },
2325 },
2326 'root' : {
2327 'level' : 'WARNING',
2328 },
2329 }
2330
2331
Mike53f7a7c2017-12-14 14:04:53 +03002332 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002333 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002334 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002335 'formatters': {
2336 'form1' : {
2337 'format' : '%(levelname)s ++ %(message)s',
2338 },
2339 },
2340 'handlers' : {
2341 'hand1' : {
2342 'class' : 'logging.StreamHandler',
2343 'formatter' : 'form1',
2344 'level' : 'NOTSET',
2345 'stream' : 'ext://sys.stdout',
2346 },
2347 },
2348 'loggers' : {
2349 'compiler.parser' : {
2350 'level' : 'DEBUG',
2351 'handlers' : ['hand1'],
2352 },
2353 },
2354 'root' : {
2355 'level' : 'WRANING',
2356 },
2357 }
2358
2359 # config3 has a less subtle configuration error
2360 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002361 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002362 'formatters': {
2363 'form1' : {
2364 'format' : '%(levelname)s ++ %(message)s',
2365 },
2366 },
2367 'handlers' : {
2368 'hand1' : {
2369 'class' : 'logging.StreamHandler',
2370 'formatter' : 'misspelled_name',
2371 'level' : 'NOTSET',
2372 'stream' : 'ext://sys.stdout',
2373 },
2374 },
2375 'loggers' : {
2376 'compiler.parser' : {
2377 'level' : 'DEBUG',
2378 'handlers' : ['hand1'],
2379 },
2380 },
2381 'root' : {
2382 'level' : 'WARNING',
2383 },
2384 }
2385
2386 # config4 specifies a custom formatter class to be loaded
2387 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002388 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002389 'formatters': {
2390 'form1' : {
2391 '()' : __name__ + '.ExceptionFormatter',
2392 'format' : '%(levelname)s:%(name)s:%(message)s',
2393 },
2394 },
2395 'handlers' : {
2396 'hand1' : {
2397 'class' : 'logging.StreamHandler',
2398 'formatter' : 'form1',
2399 'level' : 'NOTSET',
2400 'stream' : 'ext://sys.stdout',
2401 },
2402 },
2403 'root' : {
2404 'level' : 'NOTSET',
2405 'handlers' : ['hand1'],
2406 },
2407 }
2408
2409 # As config4 but using an actual callable rather than a string
2410 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002411 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002412 'formatters': {
2413 'form1' : {
2414 '()' : ExceptionFormatter,
2415 'format' : '%(levelname)s:%(name)s:%(message)s',
2416 },
2417 'form2' : {
2418 '()' : __name__ + '.formatFunc',
2419 'format' : '%(levelname)s:%(name)s:%(message)s',
2420 },
2421 'form3' : {
2422 '()' : formatFunc,
2423 'format' : '%(levelname)s:%(name)s:%(message)s',
2424 },
2425 },
2426 'handlers' : {
2427 'hand1' : {
2428 'class' : 'logging.StreamHandler',
2429 'formatter' : 'form1',
2430 'level' : 'NOTSET',
2431 'stream' : 'ext://sys.stdout',
2432 },
2433 'hand2' : {
2434 '()' : handlerFunc,
2435 },
2436 },
2437 'root' : {
2438 'level' : 'NOTSET',
2439 'handlers' : ['hand1'],
2440 },
2441 }
2442
2443 # config5 specifies a custom handler class to be loaded
2444 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002445 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002446 'formatters': {
2447 'form1' : {
2448 'format' : '%(levelname)s ++ %(message)s',
2449 },
2450 },
2451 'handlers' : {
2452 'hand1' : {
2453 'class' : __name__ + '.CustomHandler',
2454 'formatter' : 'form1',
2455 'level' : 'NOTSET',
2456 'stream' : 'ext://sys.stdout',
2457 },
2458 },
2459 'loggers' : {
2460 'compiler.parser' : {
2461 'level' : 'DEBUG',
2462 'handlers' : ['hand1'],
2463 },
2464 },
2465 'root' : {
2466 'level' : 'WARNING',
2467 },
2468 }
2469
2470 # config6 specifies a custom handler class to be loaded
2471 # but has bad arguments
2472 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002473 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002474 'formatters': {
2475 'form1' : {
2476 'format' : '%(levelname)s ++ %(message)s',
2477 },
2478 },
2479 'handlers' : {
2480 'hand1' : {
2481 'class' : __name__ + '.CustomHandler',
2482 'formatter' : 'form1',
2483 'level' : 'NOTSET',
2484 'stream' : 'ext://sys.stdout',
2485 '9' : 'invalid parameter name',
2486 },
2487 },
2488 'loggers' : {
2489 'compiler.parser' : {
2490 'level' : 'DEBUG',
2491 'handlers' : ['hand1'],
2492 },
2493 },
2494 'root' : {
2495 'level' : 'WARNING',
2496 },
2497 }
2498
Mike53f7a7c2017-12-14 14:04:53 +03002499 # config 7 does not define compiler.parser but defines compiler.lexer
2500 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002501 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002502 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002503 'formatters': {
2504 'form1' : {
2505 'format' : '%(levelname)s ++ %(message)s',
2506 },
2507 },
2508 'handlers' : {
2509 'hand1' : {
2510 'class' : 'logging.StreamHandler',
2511 'formatter' : 'form1',
2512 'level' : 'NOTSET',
2513 'stream' : 'ext://sys.stdout',
2514 },
2515 },
2516 'loggers' : {
2517 'compiler.lexer' : {
2518 'level' : 'DEBUG',
2519 'handlers' : ['hand1'],
2520 },
2521 },
2522 'root' : {
2523 'level' : 'WARNING',
2524 },
2525 }
2526
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002527 # config8 defines both compiler and compiler.lexer
2528 # so compiler.parser should not be disabled (since
2529 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002530 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002531 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002532 'disable_existing_loggers' : False,
2533 'formatters': {
2534 'form1' : {
2535 'format' : '%(levelname)s ++ %(message)s',
2536 },
2537 },
2538 'handlers' : {
2539 'hand1' : {
2540 'class' : 'logging.StreamHandler',
2541 'formatter' : 'form1',
2542 'level' : 'NOTSET',
2543 'stream' : 'ext://sys.stdout',
2544 },
2545 },
2546 'loggers' : {
2547 'compiler' : {
2548 'level' : 'DEBUG',
2549 'handlers' : ['hand1'],
2550 },
2551 'compiler.lexer' : {
2552 },
2553 },
2554 'root' : {
2555 'level' : 'WARNING',
2556 },
2557 }
2558
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002559 # config8a disables existing loggers
2560 config8a = {
2561 'version': 1,
2562 'disable_existing_loggers' : True,
2563 'formatters': {
2564 'form1' : {
2565 'format' : '%(levelname)s ++ %(message)s',
2566 },
2567 },
2568 'handlers' : {
2569 'hand1' : {
2570 'class' : 'logging.StreamHandler',
2571 'formatter' : 'form1',
2572 'level' : 'NOTSET',
2573 'stream' : 'ext://sys.stdout',
2574 },
2575 },
2576 'loggers' : {
2577 'compiler' : {
2578 'level' : 'DEBUG',
2579 'handlers' : ['hand1'],
2580 },
2581 'compiler.lexer' : {
2582 },
2583 },
2584 'root' : {
2585 'level' : 'WARNING',
2586 },
2587 }
2588
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002589 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002590 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002591 'formatters': {
2592 'form1' : {
2593 'format' : '%(levelname)s ++ %(message)s',
2594 },
2595 },
2596 'handlers' : {
2597 'hand1' : {
2598 'class' : 'logging.StreamHandler',
2599 'formatter' : 'form1',
2600 'level' : 'WARNING',
2601 'stream' : 'ext://sys.stdout',
2602 },
2603 },
2604 'loggers' : {
2605 'compiler.parser' : {
2606 'level' : 'WARNING',
2607 'handlers' : ['hand1'],
2608 },
2609 },
2610 'root' : {
2611 'level' : 'NOTSET',
2612 },
2613 }
2614
2615 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002616 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002617 'incremental' : True,
2618 'handlers' : {
2619 'hand1' : {
2620 'level' : 'WARNING',
2621 },
2622 },
2623 'loggers' : {
2624 'compiler.parser' : {
2625 'level' : 'INFO',
2626 },
2627 },
2628 }
2629
2630 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002631 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002632 'incremental' : True,
2633 'handlers' : {
2634 'hand1' : {
2635 'level' : 'INFO',
2636 },
2637 },
2638 'loggers' : {
2639 'compiler.parser' : {
2640 'level' : 'INFO',
2641 },
2642 },
2643 }
2644
Mike53f7a7c2017-12-14 14:04:53 +03002645 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002646 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002647 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002648 'formatters': {
2649 'form1' : {
2650 'format' : '%(levelname)s ++ %(message)s',
2651 },
2652 },
2653 'filters' : {
2654 'filt1' : {
2655 'name' : 'compiler.parser',
2656 },
2657 },
2658 'handlers' : {
2659 'hand1' : {
2660 'class' : 'logging.StreamHandler',
2661 'formatter' : 'form1',
2662 'level' : 'NOTSET',
2663 'stream' : 'ext://sys.stdout',
2664 'filters' : ['filt1'],
2665 },
2666 },
2667 'loggers' : {
2668 'compiler.parser' : {
2669 'level' : 'DEBUG',
2670 'filters' : ['filt1'],
2671 },
2672 },
2673 'root' : {
2674 'level' : 'WARNING',
2675 'handlers' : ['hand1'],
2676 },
2677 }
2678
Mike53f7a7c2017-12-14 14:04:53 +03002679 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002680 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002681 'version': 1,
2682 'true_formatters': {
2683 'form1' : {
2684 'format' : '%(levelname)s ++ %(message)s',
2685 },
2686 },
2687 'handler_configs': {
2688 'hand1' : {
2689 'class' : 'logging.StreamHandler',
2690 'formatter' : 'form1',
2691 'level' : 'NOTSET',
2692 'stream' : 'ext://sys.stdout',
2693 },
2694 },
2695 'formatters' : 'cfg://true_formatters',
2696 'handlers' : {
2697 'hand1' : 'cfg://handler_configs[hand1]',
2698 },
2699 'loggers' : {
2700 'compiler.parser' : {
2701 'level' : 'DEBUG',
2702 'handlers' : ['hand1'],
2703 },
2704 },
2705 'root' : {
2706 'level' : 'WARNING',
2707 },
2708 }
2709
Mike53f7a7c2017-12-14 14:04:53 +03002710 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002711 config12 = {
2712 'true_formatters': {
2713 'form1' : {
2714 'format' : '%(levelname)s ++ %(message)s',
2715 },
2716 },
2717 'handler_configs': {
2718 'hand1' : {
2719 'class' : 'logging.StreamHandler',
2720 'formatter' : 'form1',
2721 'level' : 'NOTSET',
2722 'stream' : 'ext://sys.stdout',
2723 },
2724 },
2725 'formatters' : 'cfg://true_formatters',
2726 'handlers' : {
2727 'hand1' : 'cfg://handler_configs[hand1]',
2728 },
2729 'loggers' : {
2730 'compiler.parser' : {
2731 'level' : 'DEBUG',
2732 'handlers' : ['hand1'],
2733 },
2734 },
2735 'root' : {
2736 'level' : 'WARNING',
2737 },
2738 }
2739
Mike53f7a7c2017-12-14 14:04:53 +03002740 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002741 config13 = {
2742 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002743 'true_formatters': {
2744 'form1' : {
2745 'format' : '%(levelname)s ++ %(message)s',
2746 },
2747 },
2748 'handler_configs': {
2749 'hand1' : {
2750 'class' : 'logging.StreamHandler',
2751 'formatter' : 'form1',
2752 'level' : 'NOTSET',
2753 'stream' : 'ext://sys.stdout',
2754 },
2755 },
2756 'formatters' : 'cfg://true_formatters',
2757 'handlers' : {
2758 'hand1' : 'cfg://handler_configs[hand1]',
2759 },
2760 'loggers' : {
2761 'compiler.parser' : {
2762 'level' : 'DEBUG',
2763 'handlers' : ['hand1'],
2764 },
2765 },
2766 'root' : {
2767 'level' : 'WARNING',
2768 },
2769 }
2770
Vinay Sajip8d270232012-11-15 14:20:18 +00002771 # As config0, but with properties
2772 config14 = {
2773 'version': 1,
2774 'formatters': {
2775 'form1' : {
2776 'format' : '%(levelname)s ++ %(message)s',
2777 },
2778 },
2779 'handlers' : {
2780 'hand1' : {
2781 'class' : 'logging.StreamHandler',
2782 'formatter' : 'form1',
2783 'level' : 'NOTSET',
2784 'stream' : 'ext://sys.stdout',
2785 '.': {
2786 'foo': 'bar',
2787 'terminator': '!\n',
2788 }
2789 },
2790 },
2791 'root' : {
2792 'level' : 'WARNING',
2793 'handlers' : ['hand1'],
2794 },
2795 }
2796
Vinay Sajip3f885b52013-03-22 15:19:54 +00002797 out_of_order = {
2798 "version": 1,
2799 "formatters": {
2800 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002801 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2802 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002803 }
2804 },
2805 "handlers": {
2806 "fileGlobal": {
2807 "class": "logging.StreamHandler",
2808 "level": "DEBUG",
2809 "formatter": "mySimpleFormatter"
2810 },
2811 "bufferGlobal": {
2812 "class": "logging.handlers.MemoryHandler",
2813 "capacity": 5,
2814 "formatter": "mySimpleFormatter",
2815 "target": "fileGlobal",
2816 "level": "DEBUG"
2817 }
2818 },
2819 "loggers": {
2820 "mymodule": {
2821 "level": "DEBUG",
2822 "handlers": ["bufferGlobal"],
2823 "propagate": "true"
2824 }
2825 }
2826 }
2827
BNMetrics18fb1fb2018-10-15 19:41:36 +01002828 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2829 custom_formatter_class_validate = {
2830 'version': 1,
2831 'formatters': {
2832 'form1': {
2833 '()': __name__ + '.ExceptionFormatter',
2834 'format': '%(levelname)s:%(name)s:%(message)s',
2835 'validate': False,
2836 },
2837 },
2838 'handlers' : {
2839 'hand1' : {
2840 'class': 'logging.StreamHandler',
2841 'formatter': 'form1',
2842 'level': 'NOTSET',
2843 'stream': 'ext://sys.stdout',
2844 },
2845 },
2846 "loggers": {
2847 "my_test_logger_custom_formatter": {
2848 "level": "DEBUG",
2849 "handlers": ["hand1"],
2850 "propagate": "true"
2851 }
2852 }
2853 }
2854
2855 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2856 custom_formatter_class_validate2 = {
2857 'version': 1,
2858 'formatters': {
2859 'form1': {
2860 'class': __name__ + '.ExceptionFormatter',
2861 'format': '%(levelname)s:%(name)s:%(message)s',
2862 'validate': False,
2863 },
2864 },
2865 'handlers' : {
2866 'hand1' : {
2867 'class': 'logging.StreamHandler',
2868 'formatter': 'form1',
2869 'level': 'NOTSET',
2870 'stream': 'ext://sys.stdout',
2871 },
2872 },
2873 "loggers": {
2874 "my_test_logger_custom_formatter": {
2875 "level": "DEBUG",
2876 "handlers": ["hand1"],
2877 "propagate": "true"
2878 }
2879 }
2880 }
2881
2882 # Configuration with custom class that is not inherited from logging.Formatter
2883 custom_formatter_class_validate3 = {
2884 'version': 1,
2885 'formatters': {
2886 'form1': {
2887 'class': __name__ + '.myCustomFormatter',
2888 'format': '%(levelname)s:%(name)s:%(message)s',
2889 'validate': False,
2890 },
2891 },
2892 'handlers' : {
2893 'hand1' : {
2894 'class': 'logging.StreamHandler',
2895 'formatter': 'form1',
2896 'level': 'NOTSET',
2897 'stream': 'ext://sys.stdout',
2898 },
2899 },
2900 "loggers": {
2901 "my_test_logger_custom_formatter": {
2902 "level": "DEBUG",
2903 "handlers": ["hand1"],
2904 "propagate": "true"
2905 }
2906 }
2907 }
2908
2909 # Configuration with custom function and 'validate' set to False
2910 custom_formatter_with_function = {
2911 'version': 1,
2912 'formatters': {
2913 'form1': {
2914 '()': formatFunc,
2915 'format': '%(levelname)s:%(name)s:%(message)s',
2916 'validate': False,
2917 },
2918 },
2919 'handlers' : {
2920 'hand1' : {
2921 'class': 'logging.StreamHandler',
2922 'formatter': 'form1',
2923 'level': 'NOTSET',
2924 'stream': 'ext://sys.stdout',
2925 },
2926 },
2927 "loggers": {
2928 "my_test_logger_custom_formatter": {
2929 "level": "DEBUG",
2930 "handlers": ["hand1"],
2931 "propagate": "true"
2932 }
2933 }
2934 }
2935
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002936 def apply_config(self, conf):
2937 logging.config.dictConfig(conf)
2938
2939 def test_config0_ok(self):
2940 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002941 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002942 self.apply_config(self.config0)
2943 logger = logging.getLogger()
2944 # Won't output anything
2945 logger.info(self.next_message())
2946 # Outputs a message
2947 logger.error(self.next_message())
2948 self.assert_log_lines([
2949 ('ERROR', '2'),
2950 ], stream=output)
2951 # Original logger output is empty.
2952 self.assert_log_lines([])
2953
2954 def test_config1_ok(self, config=config1):
2955 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002956 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002957 self.apply_config(config)
2958 logger = logging.getLogger("compiler.parser")
2959 # Both will output a message
2960 logger.info(self.next_message())
2961 logger.error(self.next_message())
2962 self.assert_log_lines([
2963 ('INFO', '1'),
2964 ('ERROR', '2'),
2965 ], stream=output)
2966 # Original logger output is empty.
2967 self.assert_log_lines([])
2968
2969 def test_config2_failure(self):
2970 # A simple config which overrides the default settings.
2971 self.assertRaises(Exception, self.apply_config, self.config2)
2972
2973 def test_config2a_failure(self):
2974 # A simple config which overrides the default settings.
2975 self.assertRaises(Exception, self.apply_config, self.config2a)
2976
2977 def test_config2b_failure(self):
2978 # A simple config which overrides the default settings.
2979 self.assertRaises(Exception, self.apply_config, self.config2b)
2980
2981 def test_config3_failure(self):
2982 # A simple config which overrides the default settings.
2983 self.assertRaises(Exception, self.apply_config, self.config3)
2984
2985 def test_config4_ok(self):
2986 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002987 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002988 self.apply_config(self.config4)
2989 #logger = logging.getLogger()
2990 try:
2991 raise RuntimeError()
2992 except RuntimeError:
2993 logging.exception("just testing")
2994 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002995 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002996 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2997 # Original logger output is empty
2998 self.assert_log_lines([])
2999
3000 def test_config4a_ok(self):
3001 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003002 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003003 self.apply_config(self.config4a)
3004 #logger = logging.getLogger()
3005 try:
3006 raise RuntimeError()
3007 except RuntimeError:
3008 logging.exception("just testing")
3009 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003010 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003011 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3012 # Original logger output is empty
3013 self.assert_log_lines([])
3014
3015 def test_config5_ok(self):
3016 self.test_config1_ok(config=self.config5)
3017
3018 def test_config6_failure(self):
3019 self.assertRaises(Exception, self.apply_config, self.config6)
3020
3021 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003022 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003023 self.apply_config(self.config1)
3024 logger = logging.getLogger("compiler.parser")
3025 # Both will output a message
3026 logger.info(self.next_message())
3027 logger.error(self.next_message())
3028 self.assert_log_lines([
3029 ('INFO', '1'),
3030 ('ERROR', '2'),
3031 ], stream=output)
3032 # Original logger output is empty.
3033 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003034 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003035 self.apply_config(self.config7)
3036 logger = logging.getLogger("compiler.parser")
3037 self.assertTrue(logger.disabled)
3038 logger = logging.getLogger("compiler.lexer")
3039 # Both will output a message
3040 logger.info(self.next_message())
3041 logger.error(self.next_message())
3042 self.assert_log_lines([
3043 ('INFO', '3'),
3044 ('ERROR', '4'),
3045 ], stream=output)
3046 # Original logger output is empty.
3047 self.assert_log_lines([])
3048
Mike53f7a7c2017-12-14 14:04:53 +03003049 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003050 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003051 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003052 self.apply_config(self.config1)
3053 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003054 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003055 logger.info(self.next_message())
3056 logger.error(self.next_message())
3057 self.assert_log_lines([
3058 ('INFO', '1'),
3059 ('ERROR', '2'),
3060 ], stream=output)
3061 # Original logger output is empty.
3062 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003063 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003064 self.apply_config(self.config8)
3065 logger = logging.getLogger("compiler.parser")
3066 self.assertFalse(logger.disabled)
3067 # Both will output a message
3068 logger.info(self.next_message())
3069 logger.error(self.next_message())
3070 logger = logging.getLogger("compiler.lexer")
3071 # Both will output a message
3072 logger.info(self.next_message())
3073 logger.error(self.next_message())
3074 self.assert_log_lines([
3075 ('INFO', '3'),
3076 ('ERROR', '4'),
3077 ('INFO', '5'),
3078 ('ERROR', '6'),
3079 ], stream=output)
3080 # Original logger output is empty.
3081 self.assert_log_lines([])
3082
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003083 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003084 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003085 self.apply_config(self.config1a)
3086 logger = logging.getLogger("compiler.parser")
3087 # See issue #11424. compiler-hyphenated sorts
3088 # between compiler and compiler.xyz and this
3089 # was preventing compiler.xyz from being included
3090 # in the child loggers of compiler because of an
3091 # overzealous loop termination condition.
3092 hyphenated = logging.getLogger('compiler-hyphenated')
3093 # All will output a message
3094 logger.info(self.next_message())
3095 logger.error(self.next_message())
3096 hyphenated.critical(self.next_message())
3097 self.assert_log_lines([
3098 ('INFO', '1'),
3099 ('ERROR', '2'),
3100 ('CRITICAL', '3'),
3101 ], stream=output)
3102 # Original logger output is empty.
3103 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003104 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003105 self.apply_config(self.config8a)
3106 logger = logging.getLogger("compiler.parser")
3107 self.assertFalse(logger.disabled)
3108 # Both will output a message
3109 logger.info(self.next_message())
3110 logger.error(self.next_message())
3111 logger = logging.getLogger("compiler.lexer")
3112 # Both will output a message
3113 logger.info(self.next_message())
3114 logger.error(self.next_message())
3115 # Will not appear
3116 hyphenated.critical(self.next_message())
3117 self.assert_log_lines([
3118 ('INFO', '4'),
3119 ('ERROR', '5'),
3120 ('INFO', '6'),
3121 ('ERROR', '7'),
3122 ], stream=output)
3123 # Original logger output is empty.
3124 self.assert_log_lines([])
3125
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003126 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003127 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003128 self.apply_config(self.config9)
3129 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003130 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003131 logger.info(self.next_message())
3132 self.assert_log_lines([], stream=output)
3133 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003134 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003135 logger.info(self.next_message())
3136 self.assert_log_lines([], stream=output)
3137 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003138 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003139 logger.info(self.next_message())
3140 self.assert_log_lines([
3141 ('INFO', '3'),
3142 ], stream=output)
3143
3144 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003145 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003146 self.apply_config(self.config10)
3147 logger = logging.getLogger("compiler.parser")
3148 logger.warning(self.next_message())
3149 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003150 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003151 logger.warning(self.next_message())
3152 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003153 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003154 logger.warning(self.next_message())
3155 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003156 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003157 logger.error(self.next_message())
3158 self.assert_log_lines([
3159 ('WARNING', '1'),
3160 ('ERROR', '4'),
3161 ], stream=output)
3162
3163 def test_config11_ok(self):
3164 self.test_config1_ok(self.config11)
3165
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003166 def test_config12_failure(self):
3167 self.assertRaises(Exception, self.apply_config, self.config12)
3168
3169 def test_config13_failure(self):
3170 self.assertRaises(Exception, self.apply_config, self.config13)
3171
Vinay Sajip8d270232012-11-15 14:20:18 +00003172 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003173 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003174 self.apply_config(self.config14)
3175 h = logging._handlers['hand1']
3176 self.assertEqual(h.foo, 'bar')
3177 self.assertEqual(h.terminator, '!\n')
3178 logging.warning('Exclamation')
3179 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3180
Xtreak087570a2018-07-02 14:27:46 +05303181 def test_config15_ok(self):
3182
3183 def cleanup(h1, fn):
3184 h1.close()
3185 os.remove(fn)
3186
3187 with self.check_no_resource_warning():
3188 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3189 os.close(fd)
3190
3191 config = {
3192 "version": 1,
3193 "handlers": {
3194 "file": {
3195 "class": "logging.FileHandler",
3196 "filename": fn
3197 }
3198 },
3199 "root": {
3200 "handlers": ["file"]
3201 }
3202 }
3203
3204 self.apply_config(config)
3205 self.apply_config(config)
3206
3207 handler = logging.root.handlers[0]
3208 self.addCleanup(cleanup, handler, fn)
3209
Vinay Sajip4ded5512012-10-02 15:56:16 +01003210 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003211 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003212 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003213 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003214 t.start()
3215 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003216 # Now get the port allocated
3217 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003218 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003219 try:
3220 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3221 sock.settimeout(2.0)
3222 sock.connect(('localhost', port))
3223
3224 slen = struct.pack('>L', len(text))
3225 s = slen + text
3226 sentsofar = 0
3227 left = len(s)
3228 while left > 0:
3229 sent = sock.send(s[sentsofar:])
3230 sentsofar += sent
3231 left -= sent
3232 sock.close()
3233 finally:
3234 t.ready.wait(2.0)
3235 logging.config.stopListening()
Victor Stinnerbbc8b792019-12-10 20:41:23 +01003236 support.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003237
3238 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003239 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003240 self.setup_via_listener(json.dumps(self.config10))
3241 logger = logging.getLogger("compiler.parser")
3242 logger.warning(self.next_message())
3243 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003244 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003245 logger.warning(self.next_message())
3246 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003247 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003248 logger.warning(self.next_message())
3249 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003250 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003251 logger.error(self.next_message())
3252 self.assert_log_lines([
3253 ('WARNING', '1'),
3254 ('ERROR', '4'),
3255 ], stream=output)
3256
3257 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003258 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003259 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3260 logger = logging.getLogger("compiler.parser")
3261 # Both will output a message
3262 logger.info(self.next_message())
3263 logger.error(self.next_message())
3264 self.assert_log_lines([
3265 ('INFO', '1'),
3266 ('ERROR', '2'),
3267 ], stream=output)
3268 # Original logger output is empty.
3269 self.assert_log_lines([])
3270
Vinay Sajip4ded5512012-10-02 15:56:16 +01003271 def test_listen_verify(self):
3272
3273 def verify_fail(stuff):
3274 return None
3275
3276 def verify_reverse(stuff):
3277 return stuff[::-1]
3278
3279 logger = logging.getLogger("compiler.parser")
3280 to_send = textwrap.dedent(ConfigFileTest.config1)
3281 # First, specify a verification function that will fail.
3282 # We expect to see no output, since our configuration
3283 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003284 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003285 self.setup_via_listener(to_send, verify_fail)
3286 # Both will output a message
3287 logger.info(self.next_message())
3288 logger.error(self.next_message())
3289 self.assert_log_lines([], stream=output)
3290 # Original logger output has the stuff we logged.
3291 self.assert_log_lines([
3292 ('INFO', '1'),
3293 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003294 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003295
3296 # Now, perform no verification. Our configuration
3297 # should take effect.
3298
Vinay Sajip1feedb42014-05-31 08:19:12 +01003299 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003300 self.setup_via_listener(to_send) # no verify callable specified
3301 logger = logging.getLogger("compiler.parser")
3302 # Both will output a message
3303 logger.info(self.next_message())
3304 logger.error(self.next_message())
3305 self.assert_log_lines([
3306 ('INFO', '3'),
3307 ('ERROR', '4'),
3308 ], stream=output)
3309 # Original logger output still has the stuff we logged before.
3310 self.assert_log_lines([
3311 ('INFO', '1'),
3312 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003313 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003314
3315 # Now, perform verification which transforms the bytes.
3316
Vinay Sajip1feedb42014-05-31 08:19:12 +01003317 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003318 self.setup_via_listener(to_send[::-1], verify_reverse)
3319 logger = logging.getLogger("compiler.parser")
3320 # Both will output a message
3321 logger.info(self.next_message())
3322 logger.error(self.next_message())
3323 self.assert_log_lines([
3324 ('INFO', '5'),
3325 ('ERROR', '6'),
3326 ], stream=output)
3327 # Original logger output still has the stuff we logged before.
3328 self.assert_log_lines([
3329 ('INFO', '1'),
3330 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003331 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003332
Vinay Sajip3f885b52013-03-22 15:19:54 +00003333 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003334 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3335
3336 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003337 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003338 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3339
3340 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003341 handler = logging.getLogger('mymodule').handlers[0]
3342 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003343 self.assertIsInstance(handler.formatter._style,
3344 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003345
BNMetrics18fb1fb2018-10-15 19:41:36 +01003346 def test_custom_formatter_class_with_validate(self):
3347 self.apply_config(self.custom_formatter_class_validate)
3348 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3349 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3350
3351 def test_custom_formatter_class_with_validate2(self):
3352 self.apply_config(self.custom_formatter_class_validate2)
3353 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3354 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3355
3356 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3357 config = self.custom_formatter_class_validate.copy()
3358 config['formatters']['form1']['style'] = "$"
3359
3360 # Exception should not be raise as we have configured 'validate' to False
3361 self.apply_config(config)
3362 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3363 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3364
3365 def test_custom_formatter_class_with_validate3(self):
3366 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3367
3368 def test_custom_formatter_function_with_validate(self):
3369 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3370
Vinay Sajip373baef2011-04-26 20:05:24 +01003371 def test_baseconfig(self):
3372 d = {
3373 'atuple': (1, 2, 3),
3374 'alist': ['a', 'b', 'c'],
3375 'adict': {'d': 'e', 'f': 3 },
3376 'nest1': ('g', ('h', 'i'), 'j'),
3377 'nest2': ['k', ['l', 'm'], 'n'],
3378 'nest3': ['o', 'cfg://alist', 'p'],
3379 }
3380 bc = logging.config.BaseConfigurator(d)
3381 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3382 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3383 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3384 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3385 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3386 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3387 v = bc.convert('cfg://nest3')
3388 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3389 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3390 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3391 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003392
Vinay Sajip46abfc12020-01-01 19:32:11 +00003393 def test_namedtuple(self):
3394 # see bpo-39142
3395 from collections import namedtuple
3396
3397 class MyHandler(logging.StreamHandler):
3398 def __init__(self, resource, *args, **kwargs):
3399 super().__init__(*args, **kwargs)
3400 self.resource: namedtuple = resource
3401
3402 def emit(self, record):
3403 record.msg += f' {self.resource.type}'
3404 return super().emit(record)
3405
3406 Resource = namedtuple('Resource', ['type', 'labels'])
3407 resource = Resource(type='my_type', labels=['a'])
3408
3409 config = {
3410 'version': 1,
3411 'handlers': {
3412 'myhandler': {
3413 '()': MyHandler,
3414 'resource': resource
3415 }
3416 },
3417 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3418 }
3419 with support.captured_stderr() as stderr:
3420 self.apply_config(config)
3421 logging.info('some log')
3422 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3423
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003424class ManagerTest(BaseTest):
3425 def test_manager_loggerclass(self):
3426 logged = []
3427
3428 class MyLogger(logging.Logger):
3429 def _log(self, level, msg, args, exc_info=None, extra=None):
3430 logged.append(msg)
3431
3432 man = logging.Manager(None)
3433 self.assertRaises(TypeError, man.setLoggerClass, int)
3434 man.setLoggerClass(MyLogger)
3435 logger = man.getLogger('test')
3436 logger.warning('should appear in logged')
3437 logging.warning('should not appear in logged')
3438
3439 self.assertEqual(logged, ['should appear in logged'])
3440
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003441 def test_set_log_record_factory(self):
3442 man = logging.Manager(None)
3443 expected = object()
3444 man.setLogRecordFactory(expected)
3445 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003446
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003447class ChildLoggerTest(BaseTest):
3448 def test_child_loggers(self):
3449 r = logging.getLogger()
3450 l1 = logging.getLogger('abc')
3451 l2 = logging.getLogger('def.ghi')
3452 c1 = r.getChild('xyz')
3453 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003454 self.assertIs(c1, logging.getLogger('xyz'))
3455 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003456 c1 = l1.getChild('def')
3457 c2 = c1.getChild('ghi')
3458 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003459 self.assertIs(c1, logging.getLogger('abc.def'))
3460 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3461 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003462
3463
Vinay Sajip6fac8172010-10-19 20:44:14 +00003464class DerivedLogRecord(logging.LogRecord):
3465 pass
3466
Vinay Sajip61561522010-12-03 11:50:38 +00003467class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003468
3469 def setUp(self):
3470 class CheckingFilter(logging.Filter):
3471 def __init__(self, cls):
3472 self.cls = cls
3473
3474 def filter(self, record):
3475 t = type(record)
3476 if t is not self.cls:
3477 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3478 self.cls)
3479 raise TypeError(msg)
3480 return True
3481
3482 BaseTest.setUp(self)
3483 self.filter = CheckingFilter(DerivedLogRecord)
3484 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003485 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003486
3487 def tearDown(self):
3488 self.root_logger.removeFilter(self.filter)
3489 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003490 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003491
3492 def test_logrecord_class(self):
3493 self.assertRaises(TypeError, self.root_logger.warning,
3494 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003495 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003496 self.root_logger.error(self.next_message())
3497 self.assert_log_lines([
3498 ('root', 'ERROR', '2'),
3499 ])
3500
3501
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003502class QueueHandlerTest(BaseTest):
3503 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003504 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003505
3506 def setUp(self):
3507 BaseTest.setUp(self)
3508 self.queue = queue.Queue(-1)
3509 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003510 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003511 self.que_logger = logging.getLogger('que')
3512 self.que_logger.propagate = False
3513 self.que_logger.setLevel(logging.WARNING)
3514 self.que_logger.addHandler(self.que_hdlr)
3515
3516 def tearDown(self):
3517 self.que_hdlr.close()
3518 BaseTest.tearDown(self)
3519
3520 def test_queue_handler(self):
3521 self.que_logger.debug(self.next_message())
3522 self.assertRaises(queue.Empty, self.queue.get_nowait)
3523 self.que_logger.info(self.next_message())
3524 self.assertRaises(queue.Empty, self.queue.get_nowait)
3525 msg = self.next_message()
3526 self.que_logger.warning(msg)
3527 data = self.queue.get_nowait()
3528 self.assertTrue(isinstance(data, logging.LogRecord))
3529 self.assertEqual(data.name, self.que_logger.name)
3530 self.assertEqual((data.msg, data.args), (msg, None))
3531
favlladfe3442017-08-01 20:12:26 +02003532 def test_formatting(self):
3533 msg = self.next_message()
3534 levelname = logging.getLevelName(logging.WARNING)
3535 log_format_str = '{name} -> {levelname}: {message}'
3536 formatted_msg = log_format_str.format(name=self.name,
3537 levelname=levelname, message=msg)
3538 formatter = logging.Formatter(self.log_format)
3539 self.que_hdlr.setFormatter(formatter)
3540 self.que_logger.warning(msg)
3541 log_record = self.queue.get_nowait()
3542 self.assertEqual(formatted_msg, log_record.msg)
3543 self.assertEqual(formatted_msg, log_record.message)
3544
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003545 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3546 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003547 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003548 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003549 listener = logging.handlers.QueueListener(self.queue, handler)
3550 listener.start()
3551 try:
3552 self.que_logger.warning(self.next_message())
3553 self.que_logger.error(self.next_message())
3554 self.que_logger.critical(self.next_message())
3555 finally:
3556 listener.stop()
3557 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3558 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3559 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003560 handler.close()
3561
3562 # Now test with respect_handler_level set
3563
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003564 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003565 handler.setLevel(logging.CRITICAL)
3566 listener = logging.handlers.QueueListener(self.queue, handler,
3567 respect_handler_level=True)
3568 listener.start()
3569 try:
3570 self.que_logger.warning(self.next_message())
3571 self.que_logger.error(self.next_message())
3572 self.que_logger.critical(self.next_message())
3573 finally:
3574 listener.stop()
3575 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3576 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3577 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003578 handler.close()
3579
3580 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3581 'logging.handlers.QueueListener required for this test')
3582 def test_queue_listener_with_StreamHandler(self):
3583 # Test that traceback only appends once (bpo-34334).
3584 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3585 listener.start()
3586 try:
3587 1 / 0
3588 except ZeroDivisionError as e:
3589 exc = e
3590 self.que_logger.exception(self.next_message(), exc_info=exc)
3591 listener.stop()
3592 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003593
Xtreak2dad9602019-04-07 13:21:27 +05303594 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3595 'logging.handlers.QueueListener required for this test')
3596 def test_queue_listener_with_multiple_handlers(self):
3597 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3598 self.que_hdlr.setFormatter(self.root_formatter)
3599 self.que_logger.addHandler(self.root_hdlr)
3600
3601 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3602 listener.start()
3603 self.que_logger.error("error")
3604 listener.stop()
3605 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3606
Vinay Sajipd61910c2016-09-08 01:13:39 +01003607if hasattr(logging.handlers, 'QueueListener'):
3608 import multiprocessing
3609 from unittest.mock import patch
3610
3611 class QueueListenerTest(BaseTest):
3612 """
3613 Tests based on patch submitted for issue #27930. Ensure that
3614 QueueListener handles all log messages.
3615 """
3616
3617 repeat = 20
3618
3619 @staticmethod
3620 def setup_and_log(log_queue, ident):
3621 """
3622 Creates a logger with a QueueHandler that logs to a queue read by a
3623 QueueListener. Starts the listener, logs five messages, and stops
3624 the listener.
3625 """
3626 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3627 logger.setLevel(logging.DEBUG)
3628 handler = logging.handlers.QueueHandler(log_queue)
3629 logger.addHandler(handler)
3630 listener = logging.handlers.QueueListener(log_queue)
3631 listener.start()
3632
3633 logger.info('one')
3634 logger.info('two')
3635 logger.info('three')
3636 logger.info('four')
3637 logger.info('five')
3638
3639 listener.stop()
3640 logger.removeHandler(handler)
3641 handler.close()
3642
3643 @patch.object(logging.handlers.QueueListener, 'handle')
3644 def test_handle_called_with_queue_queue(self, mock_handle):
3645 for i in range(self.repeat):
3646 log_queue = queue.Queue()
3647 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3648 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3649 'correct number of handled log messages')
3650
3651 @patch.object(logging.handlers.QueueListener, 'handle')
3652 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerb1e73612020-06-18 17:19:59 +02003653 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003654 # when the mp.synchronize module cannot be imported.
Victor Stinnerb1e73612020-06-18 17:19:59 +02003655 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003656 for i in range(self.repeat):
3657 log_queue = multiprocessing.Queue()
3658 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003659 log_queue.close()
3660 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003661 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3662 'correct number of handled log messages')
3663
3664 @staticmethod
3665 def get_all_from_queue(log_queue):
3666 try:
3667 while True:
3668 yield log_queue.get_nowait()
3669 except queue.Empty:
3670 return []
3671
3672 def test_no_messages_in_queue_after_stop(self):
3673 """
3674 Five messages are logged then the QueueListener is stopped. This
3675 test then gets everything off the queue. Failure of this test
3676 indicates that messages were not registered on the queue until
3677 _after_ the QueueListener stopped.
3678 """
Victor Stinnerb1e73612020-06-18 17:19:59 +02003679 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003680 # when the mp.synchronize module cannot be imported.
Victor Stinnerb1e73612020-06-18 17:19:59 +02003681 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003682 for i in range(self.repeat):
3683 queue = multiprocessing.Queue()
3684 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3685 # time.sleep(1)
3686 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003687 queue.close()
3688 queue.join_thread()
3689
Vinay Sajipd61910c2016-09-08 01:13:39 +01003690 expected = [[], [logging.handlers.QueueListener._sentinel]]
3691 self.assertIn(items, expected,
3692 'Found unexpected messages in queue: %s' % (
3693 [m.msg if isinstance(m, logging.LogRecord)
3694 else m for m in items]))
3695
Bar Harel6b282e12019-06-01 12:19:09 +03003696 def test_calls_task_done_after_stop(self):
3697 # Issue 36813: Make sure queue.join does not deadlock.
3698 log_queue = queue.Queue()
3699 listener = logging.handlers.QueueListener(log_queue)
3700 listener.start()
3701 listener.stop()
3702 with self.assertRaises(ValueError):
3703 # Make sure all tasks are done and .join won't block.
3704 log_queue.task_done()
3705
Vinay Sajipe723e962011-04-15 22:27:17 +01003706
Vinay Sajip37eb3382011-04-26 20:26:41 +01003707ZERO = datetime.timedelta(0)
3708
3709class UTC(datetime.tzinfo):
3710 def utcoffset(self, dt):
3711 return ZERO
3712
3713 dst = utcoffset
3714
3715 def tzname(self, dt):
3716 return 'UTC'
3717
3718utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003719
Vinay Sajipa39c5712010-10-25 13:57:39 +00003720class FormatterTest(unittest.TestCase):
3721 def setUp(self):
3722 self.common = {
3723 'name': 'formatter.test',
3724 'level': logging.DEBUG,
3725 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3726 'lineno': 42,
3727 'exc_info': None,
3728 'func': None,
3729 'msg': 'Message with %d %s',
3730 'args': (2, 'placeholders'),
3731 }
3732 self.variants = {
3733 }
3734
3735 def get_record(self, name=None):
3736 result = dict(self.common)
3737 if name is not None:
3738 result.update(self.variants[name])
3739 return logging.makeLogRecord(result)
3740
BNMetrics18fb1fb2018-10-15 19:41:36 +01003741 def assert_error_message(self, exception, message, *args, **kwargs):
3742 try:
3743 self.assertRaises(exception, *args, **kwargs)
3744 except exception as e:
3745 self.assertEqual(message, e.message)
3746
Vinay Sajipa39c5712010-10-25 13:57:39 +00003747 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003748 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003749 r = self.get_record()
3750 f = logging.Formatter('${%(message)s}')
3751 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3752 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003753 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003754 self.assertFalse(f.usesTime())
3755 f = logging.Formatter('%(asctime)s')
3756 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003757 f = logging.Formatter('%(asctime)-15s')
3758 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003759 f = logging.Formatter('%(asctime)#15s')
3760 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003761
3762 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003763 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003764 r = self.get_record()
3765 f = logging.Formatter('$%{message}%$', style='{')
3766 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3767 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003768 self.assertRaises(ValueError, f.format, r)
3769 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003770 self.assertFalse(f.usesTime())
3771 f = logging.Formatter('{asctime}', style='{')
3772 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003773 f = logging.Formatter('{asctime!s:15}', style='{')
3774 self.assertTrue(f.usesTime())
3775 f = logging.Formatter('{asctime:15}', style='{')
3776 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003777
3778 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003779 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003780 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003781 f = logging.Formatter('${message}', style='$')
3782 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003783 f = logging.Formatter('$message', style='$')
3784 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3785 f = logging.Formatter('$$%${message}%$$', style='$')
3786 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3787 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003788 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003789 self.assertFalse(f.usesTime())
3790 f = logging.Formatter('${asctime}', style='$')
3791 self.assertTrue(f.usesTime())
3792 f = logging.Formatter('$asctime', style='$')
3793 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003794 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003795 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003796 f = logging.Formatter('${asctime}--', style='$')
3797 self.assertTrue(f.usesTime())
3798
3799 def test_format_validate(self):
3800 # Check correct formatting
3801 # Percentage style
3802 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3803 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3804 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3805 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3806 f = logging.Formatter("%(process)#+027.23X")
3807 self.assertEqual(f._fmt, "%(process)#+027.23X")
3808 f = logging.Formatter("%(foo)#.*g")
3809 self.assertEqual(f._fmt, "%(foo)#.*g")
3810
3811 # StrFormat Style
3812 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3813 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3814 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3815 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3816 f = logging.Formatter("{customfield!s:#<30}", style="{")
3817 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3818 f = logging.Formatter("{message!r}", style="{")
3819 self.assertEqual(f._fmt, "{message!r}")
3820 f = logging.Formatter("{message!s}", style="{")
3821 self.assertEqual(f._fmt, "{message!s}")
3822 f = logging.Formatter("{message!a}", style="{")
3823 self.assertEqual(f._fmt, "{message!a}")
3824 f = logging.Formatter("{process!r:4.2}", style="{")
3825 self.assertEqual(f._fmt, "{process!r:4.2}")
3826 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3827 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3828 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3829 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3830 f = logging.Formatter("{foo:12.{p}}", style="{")
3831 self.assertEqual(f._fmt, "{foo:12.{p}}")
3832 f = logging.Formatter("{foo:{w}.6}", style="{")
3833 self.assertEqual(f._fmt, "{foo:{w}.6}")
3834 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3835 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3836 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3837 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3838 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3839 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3840
3841 # Dollar style
3842 f = logging.Formatter("${asctime} - $message", style="$")
3843 self.assertEqual(f._fmt, "${asctime} - $message")
3844 f = logging.Formatter("$bar $$", style="$")
3845 self.assertEqual(f._fmt, "$bar $$")
3846 f = logging.Formatter("$bar $$$$", style="$")
3847 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3848
3849 # Testing when ValueError being raised from incorrect format
3850 # Percentage Style
3851 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3852 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3853 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3854 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3855 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3856 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3857 self.assertRaises(ValueError, logging.Formatter, '${message}')
3858 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3859 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3860
3861 # StrFormat Style
3862 # Testing failure for '-' in field name
3863 self.assert_error_message(
3864 ValueError,
3865 "invalid field name/expression: 'name-thing'",
3866 logging.Formatter, "{name-thing}", style="{"
3867 )
3868 # Testing failure for style mismatch
3869 self.assert_error_message(
3870 ValueError,
3871 "invalid format: no fields",
3872 logging.Formatter, '%(asctime)s', style='{'
3873 )
3874 # Testing failure for invalid conversion
3875 self.assert_error_message(
3876 ValueError,
3877 "invalid conversion: 'Z'"
3878 )
3879 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3880 self.assert_error_message(
3881 ValueError,
3882 "invalid format: expected ':' after conversion specifier",
3883 logging.Formatter, '{asctime!aa:15}', style='{'
3884 )
3885 # Testing failure for invalid spec
3886 self.assert_error_message(
3887 ValueError,
3888 "bad specifier: '.2ff'",
3889 logging.Formatter, '{process:.2ff}', style='{'
3890 )
3891 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3892 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3893 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3894 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3895 # Testing failure for mismatch braces
3896 self.assert_error_message(
3897 ValueError,
3898 "invalid format: unmatched '{' in format spec",
3899 logging.Formatter, '{process', style='{'
3900 )
3901 self.assert_error_message(
3902 ValueError,
3903 "invalid format: unmatched '{' in format spec",
3904 logging.Formatter, 'process}', style='{'
3905 )
3906 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3907 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3908 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3909 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3910 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3911 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3912 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3913 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3914 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3915
3916 # Dollar style
3917 # Testing failure for mismatch bare $
3918 self.assert_error_message(
3919 ValueError,
3920 "invalid format: bare \'$\' not allowed",
3921 logging.Formatter, '$bar $$$', style='$'
3922 )
3923 self.assert_error_message(
3924 ValueError,
3925 "invalid format: bare \'$\' not allowed",
3926 logging.Formatter, 'bar $', style='$'
3927 )
3928 self.assert_error_message(
3929 ValueError,
3930 "invalid format: bare \'$\' not allowed",
3931 logging.Formatter, 'foo $.', style='$'
3932 )
3933 # Testing failure for mismatch style
3934 self.assert_error_message(
3935 ValueError,
3936 "invalid format: no fields",
3937 logging.Formatter, '{asctime}', style='$'
3938 )
3939 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3940
3941 # Testing failure for incorrect fields
3942 self.assert_error_message(
3943 ValueError,
3944 "invalid format: no fields",
3945 logging.Formatter, 'foo', style='$'
3946 )
3947 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003948
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003949 def test_invalid_style(self):
3950 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3951
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003952 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003953 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003954 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3955 # We use None to indicate we want the local timezone
3956 # We're essentially converting a UTC time to local time
3957 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003958 r.msecs = 123
3959 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003960 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003961 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3962 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003963 f.format(r)
3964 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
3965
Mariusz Felisiak06a35542020-04-17 18:02:47 +02003966 def test_default_msec_format_none(self):
3967 class NoMsecFormatter(logging.Formatter):
3968 default_msec_format = None
3969 default_time_format = '%d/%m/%Y %H:%M:%S'
3970
3971 r = self.get_record()
3972 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
3973 r.created = time.mktime(dt.astimezone(None).timetuple())
3974 f = NoMsecFormatter()
3975 f.converter = time.gmtime
3976 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
3977
3978
Vinay Sajip985ef872011-04-26 19:34:04 +01003979class TestBufferingFormatter(logging.BufferingFormatter):
3980 def formatHeader(self, records):
3981 return '[(%d)' % len(records)
3982
3983 def formatFooter(self, records):
3984 return '(%d)]' % len(records)
3985
3986class BufferingFormatterTest(unittest.TestCase):
3987 def setUp(self):
3988 self.records = [
3989 logging.makeLogRecord({'msg': 'one'}),
3990 logging.makeLogRecord({'msg': 'two'}),
3991 ]
3992
3993 def test_default(self):
3994 f = logging.BufferingFormatter()
3995 self.assertEqual('', f.format([]))
3996 self.assertEqual('onetwo', f.format(self.records))
3997
3998 def test_custom(self):
3999 f = TestBufferingFormatter()
4000 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
4001 lf = logging.Formatter('<%(message)s>')
4002 f = TestBufferingFormatter(lf)
4003 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004004
4005class ExceptionTest(BaseTest):
4006 def test_formatting(self):
4007 r = self.root_logger
4008 h = RecordingHandler()
4009 r.addHandler(h)
4010 try:
4011 raise RuntimeError('deliberate mistake')
4012 except:
4013 logging.exception('failed', stack_info=True)
4014 r.removeHandler(h)
4015 h.close()
4016 r = h.records[0]
4017 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
4018 'call last):\n'))
4019 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4020 'deliberate mistake'))
4021 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4022 'call last):\n'))
4023 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4024 'stack_info=True)'))
4025
4026
Vinay Sajip5a27d402010-12-10 11:42:57 +00004027class LastResortTest(BaseTest):
4028 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004029 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004030 root = self.root_logger
4031 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004032 old_lastresort = logging.lastResort
4033 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004034
Vinay Sajip5a27d402010-12-10 11:42:57 +00004035 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004036 with support.captured_stderr() as stderr:
4037 root.debug('This should not appear')
4038 self.assertEqual(stderr.getvalue(), '')
4039 root.warning('Final chance!')
4040 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4041
4042 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004043 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004044 with support.captured_stderr() as stderr:
4045 root.warning('Final chance!')
4046 msg = 'No handlers could be found for logger "root"\n'
4047 self.assertEqual(stderr.getvalue(), msg)
4048
Vinay Sajip5a27d402010-12-10 11:42:57 +00004049 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004050 with support.captured_stderr() as stderr:
4051 root.warning('Final chance!')
4052 self.assertEqual(stderr.getvalue(), '')
4053
4054 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004055 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004056 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004057 with support.captured_stderr() as stderr:
4058 root.warning('Final chance!')
4059 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004060 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004061 root.addHandler(self.root_hdlr)
4062 logging.lastResort = old_lastresort
4063 logging.raiseExceptions = old_raise_exceptions
4064
4065
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004066class FakeHandler:
4067
4068 def __init__(self, identifier, called):
4069 for method in ('acquire', 'flush', 'close', 'release'):
4070 setattr(self, method, self.record_call(identifier, method, called))
4071
4072 def record_call(self, identifier, method_name, called):
4073 def inner():
4074 called.append('{} - {}'.format(identifier, method_name))
4075 return inner
4076
4077
4078class RecordingHandler(logging.NullHandler):
4079
4080 def __init__(self, *args, **kwargs):
4081 super(RecordingHandler, self).__init__(*args, **kwargs)
4082 self.records = []
4083
4084 def handle(self, record):
4085 """Keep track of all the emitted records."""
4086 self.records.append(record)
4087
4088
4089class ShutdownTest(BaseTest):
4090
Vinay Sajip5e66b162011-04-20 15:41:14 +01004091 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004092
4093 def setUp(self):
4094 super(ShutdownTest, self).setUp()
4095 self.called = []
4096
4097 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004098 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004099
4100 def raise_error(self, error):
4101 def inner():
4102 raise error()
4103 return inner
4104
4105 def test_no_failure(self):
4106 # create some fake handlers
4107 handler0 = FakeHandler(0, self.called)
4108 handler1 = FakeHandler(1, self.called)
4109 handler2 = FakeHandler(2, self.called)
4110
4111 # create live weakref to those handlers
4112 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4113
4114 logging.shutdown(handlerList=list(handlers))
4115
4116 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4117 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4118 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4119 self.assertEqual(expected, self.called)
4120
4121 def _test_with_failure_in_method(self, method, error):
4122 handler = FakeHandler(0, self.called)
4123 setattr(handler, method, self.raise_error(error))
4124 handlers = [logging.weakref.ref(handler)]
4125
4126 logging.shutdown(handlerList=list(handlers))
4127
4128 self.assertEqual('0 - release', self.called[-1])
4129
4130 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004131 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004132
4133 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004134 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004135
4136 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004137 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004138
4139 def test_with_valueerror_in_acquire(self):
4140 self._test_with_failure_in_method('acquire', ValueError)
4141
4142 def test_with_valueerror_in_flush(self):
4143 self._test_with_failure_in_method('flush', ValueError)
4144
4145 def test_with_valueerror_in_close(self):
4146 self._test_with_failure_in_method('close', ValueError)
4147
4148 def test_with_other_error_in_acquire_without_raise(self):
4149 logging.raiseExceptions = False
4150 self._test_with_failure_in_method('acquire', IndexError)
4151
4152 def test_with_other_error_in_flush_without_raise(self):
4153 logging.raiseExceptions = False
4154 self._test_with_failure_in_method('flush', IndexError)
4155
4156 def test_with_other_error_in_close_without_raise(self):
4157 logging.raiseExceptions = False
4158 self._test_with_failure_in_method('close', IndexError)
4159
4160 def test_with_other_error_in_acquire_with_raise(self):
4161 logging.raiseExceptions = True
4162 self.assertRaises(IndexError, self._test_with_failure_in_method,
4163 'acquire', IndexError)
4164
4165 def test_with_other_error_in_flush_with_raise(self):
4166 logging.raiseExceptions = True
4167 self.assertRaises(IndexError, self._test_with_failure_in_method,
4168 'flush', IndexError)
4169
4170 def test_with_other_error_in_close_with_raise(self):
4171 logging.raiseExceptions = True
4172 self.assertRaises(IndexError, self._test_with_failure_in_method,
4173 'close', IndexError)
4174
4175
4176class ModuleLevelMiscTest(BaseTest):
4177
Vinay Sajip5e66b162011-04-20 15:41:14 +01004178 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004179
4180 def test_disable(self):
4181 old_disable = logging.root.manager.disable
4182 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004183 self.assertEqual(old_disable, 0)
4184 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004185
4186 logging.disable(83)
4187 self.assertEqual(logging.root.manager.disable, 83)
4188
Vinay Sajipd489ac92016-12-31 11:40:11 +00004189 # test the default value introduced in 3.7
4190 # (Issue #28524)
4191 logging.disable()
4192 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4193
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004194 def _test_log(self, method, level=None):
4195 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004196 support.patch(self, logging, 'basicConfig',
4197 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004198
4199 recording = RecordingHandler()
4200 logging.root.addHandler(recording)
4201
4202 log_method = getattr(logging, method)
4203 if level is not None:
4204 log_method(level, "test me: %r", recording)
4205 else:
4206 log_method("test me: %r", recording)
4207
4208 self.assertEqual(len(recording.records), 1)
4209 record = recording.records[0]
4210 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4211
4212 expected_level = level if level is not None else getattr(logging, method.upper())
4213 self.assertEqual(record.levelno, expected_level)
4214
4215 # basicConfig was not called!
4216 self.assertEqual(called, [])
4217
4218 def test_log(self):
4219 self._test_log('log', logging.ERROR)
4220
4221 def test_debug(self):
4222 self._test_log('debug')
4223
4224 def test_info(self):
4225 self._test_log('info')
4226
4227 def test_warning(self):
4228 self._test_log('warning')
4229
4230 def test_error(self):
4231 self._test_log('error')
4232
4233 def test_critical(self):
4234 self._test_log('critical')
4235
4236 def test_set_logger_class(self):
4237 self.assertRaises(TypeError, logging.setLoggerClass, object)
4238
4239 class MyLogger(logging.Logger):
4240 pass
4241
4242 logging.setLoggerClass(MyLogger)
4243 self.assertEqual(logging.getLoggerClass(), MyLogger)
4244
4245 logging.setLoggerClass(logging.Logger)
4246 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4247
Vinay Sajip01500012019-06-19 11:46:53 +01004248 def test_subclass_logger_cache(self):
4249 # bpo-37258
4250 message = []
4251
4252 class MyLogger(logging.getLoggerClass()):
4253 def __init__(self, name='MyLogger', level=logging.NOTSET):
4254 super().__init__(name, level)
4255 message.append('initialized')
4256
4257 logging.setLoggerClass(MyLogger)
4258 logger = logging.getLogger('just_some_logger')
4259 self.assertEqual(message, ['initialized'])
4260 stream = io.StringIO()
4261 h = logging.StreamHandler(stream)
4262 logger.addHandler(h)
4263 try:
4264 logger.setLevel(logging.DEBUG)
4265 logger.debug("hello")
4266 self.assertEqual(stream.getvalue().strip(), "hello")
4267
4268 stream.truncate(0)
4269 stream.seek(0)
4270
4271 logger.setLevel(logging.INFO)
4272 logger.debug("hello")
4273 self.assertEqual(stream.getvalue(), "")
4274 finally:
4275 logger.removeHandler(h)
4276 h.close()
4277 logging.setLoggerClass(logging.Logger)
4278
Antoine Pitrou712cb732013-12-21 15:51:54 +01004279 def test_logging_at_shutdown(self):
4280 # Issue #20037
4281 code = """if 1:
4282 import logging
4283
4284 class A:
4285 def __del__(self):
4286 try:
4287 raise ValueError("some error")
4288 except Exception:
4289 logging.exception("exception in __del__")
4290
4291 a = A()"""
4292 rc, out, err = assert_python_ok("-c", code)
4293 err = err.decode()
4294 self.assertIn("exception in __del__", err)
4295 self.assertIn("ValueError: some error", err)
4296
Rémi Lapeyre65f64b192019-03-15 07:53:34 +01004297 def test_recursion_error(self):
4298 # Issue 36272
4299 code = """if 1:
4300 import logging
4301
4302 def rec():
4303 logging.error("foo")
4304 rec()
4305
4306 rec()"""
4307 rc, out, err = assert_python_failure("-c", code)
4308 err = err.decode()
4309 self.assertNotIn("Cannot recover from stack overflow.", err)
4310 self.assertEqual(rc, 1)
4311
Antoine Pitrou712cb732013-12-21 15:51:54 +01004312
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004313class LogRecordTest(BaseTest):
4314 def test_str_rep(self):
4315 r = logging.makeLogRecord({})
4316 s = str(r)
4317 self.assertTrue(s.startswith('<LogRecord: '))
4318 self.assertTrue(s.endswith('>'))
4319
4320 def test_dict_arg(self):
4321 h = RecordingHandler()
4322 r = logging.getLogger()
4323 r.addHandler(h)
4324 d = {'less' : 'more' }
4325 logging.warning('less is %(less)s', d)
4326 self.assertIs(h.records[0].args, d)
4327 self.assertEqual(h.records[0].message, 'less is more')
4328 r.removeHandler(h)
4329 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004330
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004331 def test_multiprocessing(self):
4332 r = logging.makeLogRecord({})
4333 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004334 try:
4335 import multiprocessing as mp
4336 r = logging.makeLogRecord({})
4337 self.assertEqual(r.processName, mp.current_process().name)
4338 except ImportError:
4339 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004340
4341 def test_optional(self):
4342 r = logging.makeLogRecord({})
4343 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004344 NOT_NONE(r.thread)
4345 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004346 NOT_NONE(r.process)
4347 NOT_NONE(r.processName)
4348 log_threads = logging.logThreads
4349 log_processes = logging.logProcesses
4350 log_multiprocessing = logging.logMultiprocessing
4351 try:
4352 logging.logThreads = False
4353 logging.logProcesses = False
4354 logging.logMultiprocessing = False
4355 r = logging.makeLogRecord({})
4356 NONE = self.assertIsNone
4357 NONE(r.thread)
4358 NONE(r.threadName)
4359 NONE(r.process)
4360 NONE(r.processName)
4361 finally:
4362 logging.logThreads = log_threads
4363 logging.logProcesses = log_processes
4364 logging.logMultiprocessing = log_multiprocessing
4365
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004366class BasicConfigTest(unittest.TestCase):
4367
Vinay Sajip95bf5042011-04-20 11:50:56 +01004368 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004369
4370 def setUp(self):
4371 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004372 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004373 self.saved_handlers = logging._handlers.copy()
4374 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004375 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004376 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004377 logging.root.handlers = []
4378
4379 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004380 for h in logging.root.handlers[:]:
4381 logging.root.removeHandler(h)
4382 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004383 super(BasicConfigTest, self).tearDown()
4384
Vinay Sajip3def7e02011-04-20 10:58:06 +01004385 def cleanup(self):
4386 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004387 logging._handlers.clear()
4388 logging._handlers.update(self.saved_handlers)
4389 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004390 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004391
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004392 def test_no_kwargs(self):
4393 logging.basicConfig()
4394
4395 # handler defaults to a StreamHandler to sys.stderr
4396 self.assertEqual(len(logging.root.handlers), 1)
4397 handler = logging.root.handlers[0]
4398 self.assertIsInstance(handler, logging.StreamHandler)
4399 self.assertEqual(handler.stream, sys.stderr)
4400
4401 formatter = handler.formatter
4402 # format defaults to logging.BASIC_FORMAT
4403 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4404 # datefmt defaults to None
4405 self.assertIsNone(formatter.datefmt)
4406 # style defaults to %
4407 self.assertIsInstance(formatter._style, logging.PercentStyle)
4408
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004409 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004410 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004411
Vinay Sajip1fd12022014-01-13 21:59:56 +00004412 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004413 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004414 logging.basicConfig(stream=sys.stdout, style="{")
4415 logging.error("Log an error")
4416 sys.stdout.seek(0)
4417 self.assertEqual(output.getvalue().strip(),
4418 "ERROR:root:Log an error")
4419
4420 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004421 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004422 logging.basicConfig(stream=sys.stdout, style="$")
4423 logging.error("Log an error")
4424 sys.stdout.seek(0)
4425 self.assertEqual(output.getvalue().strip(),
4426 "ERROR:root:Log an error")
4427
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004428 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004429
4430 def cleanup(h1, h2, fn):
4431 h1.close()
4432 h2.close()
4433 os.remove(fn)
4434
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004435 logging.basicConfig(filename='test.log')
4436
4437 self.assertEqual(len(logging.root.handlers), 1)
4438 handler = logging.root.handlers[0]
4439 self.assertIsInstance(handler, logging.FileHandler)
4440
4441 expected = logging.FileHandler('test.log', 'a')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004442 self.assertEqual(handler.stream.mode, expected.stream.mode)
4443 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004444 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004445
4446 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004447
4448 def cleanup(h1, h2, fn):
4449 h1.close()
4450 h2.close()
4451 os.remove(fn)
4452
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004453 logging.basicConfig(filename='test.log', filemode='wb')
4454
4455 handler = logging.root.handlers[0]
4456 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004457 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004458 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004459
4460 def test_stream(self):
4461 stream = io.StringIO()
4462 self.addCleanup(stream.close)
4463 logging.basicConfig(stream=stream)
4464
4465 self.assertEqual(len(logging.root.handlers), 1)
4466 handler = logging.root.handlers[0]
4467 self.assertIsInstance(handler, logging.StreamHandler)
4468 self.assertEqual(handler.stream, stream)
4469
4470 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004471 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004472
4473 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004474 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004475
4476 def test_datefmt(self):
4477 logging.basicConfig(datefmt='bar')
4478
4479 formatter = logging.root.handlers[0].formatter
4480 self.assertEqual(formatter.datefmt, 'bar')
4481
4482 def test_style(self):
4483 logging.basicConfig(style='$')
4484
4485 formatter = logging.root.handlers[0].formatter
4486 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4487
4488 def test_level(self):
4489 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004490 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004491
4492 logging.basicConfig(level=57)
4493 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004494 # Test that second call has no effect
4495 logging.basicConfig(level=58)
4496 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004497
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004498 def test_incompatible(self):
4499 assertRaises = self.assertRaises
4500 handlers = [logging.StreamHandler()]
4501 stream = sys.stderr
4502 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004503 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004504 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004505 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004506 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004507 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004508 # Issue 23207: test for invalid kwargs
4509 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4510 # Should pop both filename and filemode even if filename is None
4511 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004512
4513 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004514 handlers = [
4515 logging.StreamHandler(),
4516 logging.StreamHandler(sys.stdout),
4517 logging.StreamHandler(),
4518 ]
4519 f = logging.Formatter()
4520 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004521 logging.basicConfig(handlers=handlers)
4522 self.assertIs(handlers[0], logging.root.handlers[0])
4523 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004524 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004525 self.assertIsNotNone(handlers[0].formatter)
4526 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004527 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004528 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4529
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004530 def test_force(self):
4531 old_string_io = io.StringIO()
4532 new_string_io = io.StringIO()
4533 old_handlers = [logging.StreamHandler(old_string_io)]
4534 new_handlers = [logging.StreamHandler(new_string_io)]
4535 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4536 logging.warning('warn')
4537 logging.info('info')
4538 logging.debug('debug')
4539 self.assertEqual(len(logging.root.handlers), 1)
4540 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4541 force=True)
4542 logging.warning('warn')
4543 logging.info('info')
4544 logging.debug('debug')
4545 self.assertEqual(len(logging.root.handlers), 1)
4546 self.assertEqual(old_string_io.getvalue().strip(),
4547 'WARNING:root:warn')
4548 self.assertEqual(new_string_io.getvalue().strip(),
4549 'WARNING:root:warn\nINFO:root:info')
4550
Vinay Sajipca7b5042019-06-17 17:40:52 +01004551 def test_encoding(self):
4552 try:
4553 encoding = 'utf-8'
4554 logging.basicConfig(filename='test.log', encoding=encoding,
4555 errors='strict',
4556 format='%(message)s', level=logging.DEBUG)
4557
4558 self.assertEqual(len(logging.root.handlers), 1)
4559 handler = logging.root.handlers[0]
4560 self.assertIsInstance(handler, logging.FileHandler)
4561 self.assertEqual(handler.encoding, encoding)
4562 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4563 finally:
4564 handler.close()
4565 with open('test.log', encoding='utf-8') as f:
4566 data = f.read().strip()
4567 os.remove('test.log')
4568 self.assertEqual(data,
4569 'The Øresund Bridge joins Copenhagen to Malmö')
4570
4571 def test_encoding_errors(self):
4572 try:
4573 encoding = 'ascii'
4574 logging.basicConfig(filename='test.log', encoding=encoding,
4575 errors='ignore',
4576 format='%(message)s', level=logging.DEBUG)
4577
4578 self.assertEqual(len(logging.root.handlers), 1)
4579 handler = logging.root.handlers[0]
4580 self.assertIsInstance(handler, logging.FileHandler)
4581 self.assertEqual(handler.encoding, encoding)
4582 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4583 finally:
4584 handler.close()
4585 with open('test.log', encoding='utf-8') as f:
4586 data = f.read().strip()
4587 os.remove('test.log')
4588 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4589
4590 def test_encoding_errors_default(self):
4591 try:
4592 encoding = 'ascii'
4593 logging.basicConfig(filename='test.log', encoding=encoding,
4594 format='%(message)s', level=logging.DEBUG)
4595
4596 self.assertEqual(len(logging.root.handlers), 1)
4597 handler = logging.root.handlers[0]
4598 self.assertIsInstance(handler, logging.FileHandler)
4599 self.assertEqual(handler.encoding, encoding)
4600 self.assertEqual(handler.errors, 'backslashreplace')
4601 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4602 finally:
4603 handler.close()
4604 with open('test.log', encoding='utf-8') as f:
4605 data = f.read().strip()
4606 os.remove('test.log')
4607 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4608 r'Bridge joins Copenhagen to Malm\xf6')
4609
4610 def test_encoding_errors_none(self):
4611 # Specifying None should behave as 'strict'
4612 try:
4613 encoding = 'ascii'
4614 logging.basicConfig(filename='test.log', encoding=encoding,
4615 errors=None,
4616 format='%(message)s', level=logging.DEBUG)
4617
4618 self.assertEqual(len(logging.root.handlers), 1)
4619 handler = logging.root.handlers[0]
4620 self.assertIsInstance(handler, logging.FileHandler)
4621 self.assertEqual(handler.encoding, encoding)
4622 self.assertIsNone(handler.errors)
4623
4624 message = []
4625
4626 def dummy_handle_error(record):
4627 _, v, _ = sys.exc_info()
4628 message.append(str(v))
4629
4630 handler.handleError = dummy_handle_error
4631 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4632 self.assertTrue(message)
4633 self.assertIn("'ascii' codec can't encode "
4634 "character '\\xd8' in position 4:", message[0])
4635 finally:
4636 handler.close()
4637 with open('test.log', encoding='utf-8') as f:
4638 data = f.read().strip()
4639 os.remove('test.log')
4640 # didn't write anything due to the encoding error
4641 self.assertEqual(data, r'')
4642
4643
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004644 def _test_log(self, method, level=None):
4645 # logging.root has no handlers so basicConfig should be called
4646 called = []
4647
4648 old_basic_config = logging.basicConfig
4649 def my_basic_config(*a, **kw):
4650 old_basic_config()
4651 old_level = logging.root.level
4652 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004653 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004654 called.append((a, kw))
4655
Vinay Sajip1feedb42014-05-31 08:19:12 +01004656 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004657
4658 log_method = getattr(logging, method)
4659 if level is not None:
4660 log_method(level, "test me")
4661 else:
4662 log_method("test me")
4663
4664 # basicConfig was called with no arguments
4665 self.assertEqual(called, [((), {})])
4666
4667 def test_log(self):
4668 self._test_log('log', logging.WARNING)
4669
4670 def test_debug(self):
4671 self._test_log('debug')
4672
4673 def test_info(self):
4674 self._test_log('info')
4675
4676 def test_warning(self):
4677 self._test_log('warning')
4678
4679 def test_error(self):
4680 self._test_log('error')
4681
4682 def test_critical(self):
4683 self._test_log('critical')
4684
4685
4686class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004687 def setUp(self):
4688 super(LoggerAdapterTest, self).setUp()
4689 old_handler_list = logging._handlerList[:]
4690
4691 self.recording = RecordingHandler()
4692 self.logger = logging.root
4693 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004694 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004695 self.addCleanup(self.recording.close)
4696
4697 def cleanup():
4698 logging._handlerList[:] = old_handler_list
4699
4700 self.addCleanup(cleanup)
4701 self.addCleanup(logging.shutdown)
4702 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4703
4704 def test_exception(self):
4705 msg = 'testing exception: %r'
4706 exc = None
4707 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004708 1 / 0
4709 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004710 exc = e
4711 self.adapter.exception(msg, self.recording)
4712
4713 self.assertEqual(len(self.recording.records), 1)
4714 record = self.recording.records[0]
4715 self.assertEqual(record.levelno, logging.ERROR)
4716 self.assertEqual(record.msg, msg)
4717 self.assertEqual(record.args, (self.recording,))
4718 self.assertEqual(record.exc_info,
4719 (exc.__class__, exc, exc.__traceback__))
4720
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004721 def test_exception_excinfo(self):
4722 try:
4723 1 / 0
4724 except ZeroDivisionError as e:
4725 exc = e
4726
4727 self.adapter.exception('exc_info test', exc_info=exc)
4728
4729 self.assertEqual(len(self.recording.records), 1)
4730 record = self.recording.records[0]
4731 self.assertEqual(record.exc_info,
4732 (exc.__class__, exc, exc.__traceback__))
4733
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004734 def test_critical(self):
4735 msg = 'critical test! %r'
4736 self.adapter.critical(msg, self.recording)
4737
4738 self.assertEqual(len(self.recording.records), 1)
4739 record = self.recording.records[0]
4740 self.assertEqual(record.levelno, logging.CRITICAL)
4741 self.assertEqual(record.msg, msg)
4742 self.assertEqual(record.args, (self.recording,))
4743
4744 def test_is_enabled_for(self):
4745 old_disable = self.adapter.logger.manager.disable
4746 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004747 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4748 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004749 self.assertFalse(self.adapter.isEnabledFor(32))
4750
4751 def test_has_handlers(self):
4752 self.assertTrue(self.adapter.hasHandlers())
4753
4754 for handler in self.logger.handlers:
4755 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004756
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004757 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004758 self.assertFalse(self.adapter.hasHandlers())
4759
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004760 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004761 class Adapter(logging.LoggerAdapter):
4762 prefix = 'Adapter'
4763
4764 def process(self, msg, kwargs):
4765 return f"{self.prefix} {msg}", kwargs
4766
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004767 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004768 adapter = Adapter(logger=self.logger, extra=None)
4769 adapter_adapter = Adapter(logger=adapter, extra=None)
4770 adapter_adapter.prefix = 'AdapterAdapter'
4771 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004772 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004773 self.assertEqual(len(self.recording.records), 1)
4774 record = self.recording.records[0]
4775 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004776 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004777 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004778 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004779 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004780 self.assertIs(self.logger.manager, orig_manager)
4781 temp_manager = object()
4782 try:
4783 adapter_adapter.manager = temp_manager
4784 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004785 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004786 self.assertIs(self.logger.manager, temp_manager)
4787 finally:
4788 adapter_adapter.manager = orig_manager
4789 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004790 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004791 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004792
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004793
4794class LoggerTest(BaseTest):
4795
4796 def setUp(self):
4797 super(LoggerTest, self).setUp()
4798 self.recording = RecordingHandler()
4799 self.logger = logging.Logger(name='blah')
4800 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004801 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004802 self.addCleanup(self.recording.close)
4803 self.addCleanup(logging.shutdown)
4804
4805 def test_set_invalid_level(self):
4806 self.assertRaises(TypeError, self.logger.setLevel, object())
4807
4808 def test_exception(self):
4809 msg = 'testing exception: %r'
4810 exc = None
4811 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004812 1 / 0
4813 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004814 exc = e
4815 self.logger.exception(msg, self.recording)
4816
4817 self.assertEqual(len(self.recording.records), 1)
4818 record = self.recording.records[0]
4819 self.assertEqual(record.levelno, logging.ERROR)
4820 self.assertEqual(record.msg, msg)
4821 self.assertEqual(record.args, (self.recording,))
4822 self.assertEqual(record.exc_info,
4823 (exc.__class__, exc, exc.__traceback__))
4824
4825 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004826 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004827 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004828
4829 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004830 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004831 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004832
4833 def test_find_caller_with_stack_info(self):
4834 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004835 support.patch(self, logging.traceback, 'print_stack',
4836 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004837
4838 self.logger.findCaller(stack_info=True)
4839
4840 self.assertEqual(len(called), 1)
4841 self.assertEqual('Stack (most recent call last):\n', called[0])
4842
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004843 def test_find_caller_with_stacklevel(self):
4844 the_level = 1
4845
4846 def innermost():
4847 self.logger.warning('test', stacklevel=the_level)
4848
4849 def inner():
4850 innermost()
4851
4852 def outer():
4853 inner()
4854
4855 records = self.recording.records
4856 outer()
4857 self.assertEqual(records[-1].funcName, 'innermost')
4858 lineno = records[-1].lineno
4859 the_level += 1
4860 outer()
4861 self.assertEqual(records[-1].funcName, 'inner')
4862 self.assertGreater(records[-1].lineno, lineno)
4863 lineno = records[-1].lineno
4864 the_level += 1
4865 outer()
4866 self.assertEqual(records[-1].funcName, 'outer')
4867 self.assertGreater(records[-1].lineno, lineno)
4868 lineno = records[-1].lineno
4869 the_level += 1
4870 outer()
4871 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
4872 self.assertGreater(records[-1].lineno, lineno)
4873
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004874 def test_make_record_with_extra_overwrite(self):
4875 name = 'my record'
4876 level = 13
4877 fn = lno = msg = args = exc_info = func = sinfo = None
4878 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
4879 exc_info, func, sinfo)
4880
4881 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
4882 extra = {key: 'some value'}
4883 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
4884 fn, lno, msg, args, exc_info,
4885 extra=extra, sinfo=sinfo)
4886
4887 def test_make_record_with_extra_no_overwrite(self):
4888 name = 'my record'
4889 level = 13
4890 fn = lno = msg = args = exc_info = func = sinfo = None
4891 extra = {'valid_key': 'some value'}
4892 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
4893 exc_info, extra=extra, sinfo=sinfo)
4894 self.assertIn('valid_key', result.__dict__)
4895
4896 def test_has_handlers(self):
4897 self.assertTrue(self.logger.hasHandlers())
4898
4899 for handler in self.logger.handlers:
4900 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004901 self.assertFalse(self.logger.hasHandlers())
4902
4903 def test_has_handlers_no_propagate(self):
4904 child_logger = logging.getLogger('blah.child')
4905 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004906 self.assertFalse(child_logger.hasHandlers())
4907
4908 def test_is_enabled_for(self):
4909 old_disable = self.logger.manager.disable
4910 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004911 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004912 self.assertFalse(self.logger.isEnabledFor(22))
4913
Timo Furrer6e3ca642018-06-01 09:29:46 +02004914 def test_is_enabled_for_disabled_logger(self):
4915 old_disabled = self.logger.disabled
4916 old_disable = self.logger.manager.disable
4917
4918 self.logger.disabled = True
4919 self.logger.manager.disable = 21
4920
4921 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
4922 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
4923
4924 self.assertFalse(self.logger.isEnabledFor(22))
4925
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004926 def test_root_logger_aliases(self):
4927 root = logging.getLogger()
4928 self.assertIs(root, logging.root)
4929 self.assertIs(root, logging.getLogger(None))
4930 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01004931 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004932 self.assertIs(root, logging.getLogger('foo').root)
4933 self.assertIs(root, logging.getLogger('foo.bar').root)
4934 self.assertIs(root, logging.getLogger('foo').parent)
4935
4936 self.assertIsNot(root, logging.getLogger('\0'))
4937 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
4938
4939 def test_invalid_names(self):
4940 self.assertRaises(TypeError, logging.getLogger, any)
4941 self.assertRaises(TypeError, logging.getLogger, b'foo')
4942
Vinay Sajip6260d9f2017-06-06 16:34:29 +01004943 def test_pickling(self):
4944 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
4945 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
4946 logger = logging.getLogger(name)
4947 s = pickle.dumps(logger, proto)
4948 unpickled = pickle.loads(s)
4949 self.assertIs(unpickled, logger)
4950
Avram Lubkin78c18a92017-07-30 05:36:33 -04004951 def test_caching(self):
4952 root = self.root_logger
4953 logger1 = logging.getLogger("abc")
4954 logger2 = logging.getLogger("abc.def")
4955
4956 # Set root logger level and ensure cache is empty
4957 root.setLevel(logging.ERROR)
4958 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
4959 self.assertEqual(logger2._cache, {})
4960
4961 # Ensure cache is populated and calls are consistent
4962 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4963 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
4964 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
4965 self.assertEqual(root._cache, {})
4966 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4967
4968 # Ensure root cache gets populated
4969 self.assertEqual(root._cache, {})
4970 self.assertTrue(root.isEnabledFor(logging.ERROR))
4971 self.assertEqual(root._cache, {logging.ERROR: True})
4972
4973 # Set parent logger level and ensure caches are emptied
4974 logger1.setLevel(logging.CRITICAL)
4975 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4976 self.assertEqual(logger2._cache, {})
4977
4978 # Ensure logger2 uses parent logger's effective level
4979 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4980
4981 # Set level to NOTSET and ensure caches are empty
4982 logger2.setLevel(logging.NOTSET)
4983 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4984 self.assertEqual(logger2._cache, {})
4985 self.assertEqual(logger1._cache, {})
4986 self.assertEqual(root._cache, {})
4987
4988 # Verify logger2 follows parent and not root
4989 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4990 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
4991 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
4992 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
4993 self.assertTrue(root.isEnabledFor(logging.ERROR))
4994
4995 # Disable logging in manager and ensure caches are clear
4996 logging.disable()
4997 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4998 self.assertEqual(logger2._cache, {})
4999 self.assertEqual(logger1._cache, {})
5000 self.assertEqual(root._cache, {})
5001
5002 # Ensure no loggers are enabled
5003 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5004 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5005 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5006
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005007
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005008class BaseFileTest(BaseTest):
5009 "Base class for handler tests that write log files"
5010
5011 def setUp(self):
5012 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005013 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5014 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005015 self.rmfiles = []
5016
5017 def tearDown(self):
5018 for fn in self.rmfiles:
5019 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005020 if os.path.exists(self.fn):
5021 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005022 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005023
5024 def assertLogFile(self, filename):
5025 "Assert a log file is there and register it for deletion"
5026 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005027 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005028 self.rmfiles.append(filename)
5029
5030
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005031class FileHandlerTest(BaseFileTest):
5032 def test_delay(self):
5033 os.unlink(self.fn)
5034 fh = logging.FileHandler(self.fn, delay=True)
5035 self.assertIsNone(fh.stream)
5036 self.assertFalse(os.path.exists(self.fn))
5037 fh.handle(logging.makeLogRecord({}))
5038 self.assertIsNotNone(fh.stream)
5039 self.assertTrue(os.path.exists(self.fn))
5040 fh.close()
5041
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005042class RotatingFileHandlerTest(BaseFileTest):
5043 def next_rec(self):
5044 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5045 self.next_message(), None, None, None)
5046
5047 def test_should_not_rollover(self):
5048 # If maxbytes is zero rollover never occurs
5049 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
5050 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005051 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005052
5053 def test_should_rollover(self):
5054 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
5055 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005056 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005057
5058 def test_file_created(self):
5059 # checks that the file is created and assumes it was created
5060 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005061 rh = logging.handlers.RotatingFileHandler(self.fn)
5062 rh.emit(self.next_rec())
5063 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005064 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005065
5066 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005067 def namer(name):
5068 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005069 rh = logging.handlers.RotatingFileHandler(
5070 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005071 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005072 rh.emit(self.next_rec())
5073 self.assertLogFile(self.fn)
5074 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005075 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005076 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005077 self.assertLogFile(namer(self.fn + ".2"))
5078 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5079 rh.close()
5080
l0rb519cb872019-11-06 22:21:40 +01005081 def test_namer_rotator_inheritance(self):
5082 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5083 def namer(self, name):
5084 return name + ".test"
5085
5086 def rotator(self, source, dest):
5087 if os.path.exists(source):
5088 os.rename(source, dest + ".rotated")
5089
5090 rh = HandlerWithNamerAndRotator(
5091 self.fn, backupCount=2, maxBytes=1)
5092 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5093 rh.emit(self.next_rec())
5094 self.assertLogFile(self.fn)
5095 rh.emit(self.next_rec())
5096 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5097 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5098 rh.close()
5099
Hai Shia3ec3ad2020-05-19 06:02:57 +08005100 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005101 def test_rotator(self):
5102 def namer(name):
5103 return name + ".gz"
5104
5105 def rotator(source, dest):
5106 with open(source, "rb") as sf:
5107 data = sf.read()
5108 compressed = zlib.compress(data, 9)
5109 with open(dest, "wb") as df:
5110 df.write(compressed)
5111 os.remove(source)
5112
5113 rh = logging.handlers.RotatingFileHandler(
5114 self.fn, backupCount=2, maxBytes=1)
5115 rh.rotator = rotator
5116 rh.namer = namer
5117 m1 = self.next_rec()
5118 rh.emit(m1)
5119 self.assertLogFile(self.fn)
5120 m2 = self.next_rec()
5121 rh.emit(m2)
5122 fn = namer(self.fn + ".1")
5123 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005124 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005125 with open(fn, "rb") as f:
5126 compressed = f.read()
5127 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005128 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005129 rh.emit(self.next_rec())
5130 fn = namer(self.fn + ".2")
5131 self.assertLogFile(fn)
5132 with open(fn, "rb") as f:
5133 compressed = f.read()
5134 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005135 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005136 rh.emit(self.next_rec())
5137 fn = namer(self.fn + ".2")
5138 with open(fn, "rb") as f:
5139 compressed = f.read()
5140 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005141 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005142 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005143 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005144
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005145class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005146 # other test methods added below
5147 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01005148 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
5149 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005150 fmt = logging.Formatter('%(asctime)s %(message)s')
5151 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005152 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005153 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005154 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005155 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005156 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005157 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005158 fh.close()
5159 # At this point, we should have a recent rotated file which we
5160 # can test for the existence of. However, in practice, on some
5161 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005162 # in time to go to look for the log file. So, we go back a fair
5163 # bit, and stop as soon as we see a rotated file. In theory this
5164 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005165 found = False
5166 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005167 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005168 for secs in range(GO_BACK):
5169 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005170 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5171 found = os.path.exists(fn)
5172 if found:
5173 self.rmfiles.append(fn)
5174 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005175 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5176 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005177 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005178 dn, fn = os.path.split(self.fn)
5179 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005180 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5181 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005182 for f in files:
5183 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005184 path = os.path.join(dn, f)
5185 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005186 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005187 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005188
Vinay Sajip0372e102011-05-05 12:59:14 +01005189 def test_invalid(self):
5190 assertRaises = self.assertRaises
5191 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005192 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005193 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005194 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005195 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005196 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005197
Vinay Sajipa7130792013-04-12 17:04:23 +01005198 def test_compute_rollover_daily_attime(self):
5199 currentTime = 0
5200 atTime = datetime.time(12, 0, 0)
5201 rh = logging.handlers.TimedRotatingFileHandler(
5202 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
5203 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005204 try:
5205 actual = rh.computeRollover(currentTime)
5206 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005207
Vinay Sajipd86ac962013-04-14 12:20:46 +01005208 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5209 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5210 finally:
5211 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005212
Vinay Sajip10e8c492013-05-18 10:19:54 -07005213 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005214 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005215 currentTime = int(time.time())
5216 today = currentTime - currentTime % 86400
5217
Vinay Sajipa7130792013-04-12 17:04:23 +01005218 atTime = datetime.time(12, 0, 0)
5219
Vinay Sajip10e8c492013-05-18 10:19:54 -07005220 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005221 for day in range(7):
5222 rh = logging.handlers.TimedRotatingFileHandler(
5223 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
5224 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005225 try:
5226 if wday > day:
5227 # The rollover day has already passed this week, so we
5228 # go over into next week
5229 expected = (7 - wday + day)
5230 else:
5231 expected = (day - wday)
5232 # At this point expected is in days from now, convert to seconds
5233 expected *= 24 * 60 * 60
5234 # Add in the rollover time
5235 expected += 12 * 60 * 60
5236 # Add in adjustment for today
5237 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005238 actual = rh.computeRollover(today)
5239 if actual != expected:
5240 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005241 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005242 self.assertEqual(actual, expected)
5243 if day == wday:
5244 # goes into following week
5245 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005246 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005247 if actual != expected:
5248 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005249 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005250 self.assertEqual(actual, expected)
5251 finally:
5252 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005253
5254
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005255def secs(**kw):
5256 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5257
5258for when, exp in (('S', 1),
5259 ('M', 60),
5260 ('H', 60 * 60),
5261 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005262 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005263 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005264 ('W0', secs(days=4, hours=24)),
5265 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005266 def test_compute_rollover(self, when=when, exp=exp):
5267 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00005268 self.fn, when=when, interval=1, backupCount=0, utc=True)
5269 currentTime = 0.0
5270 actual = rh.computeRollover(currentTime)
5271 if exp != actual:
5272 # Failures occur on some systems for MIDNIGHT and W0.
5273 # Print detailed calculation for MIDNIGHT so we can try to see
5274 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005275 if when == 'MIDNIGHT':
5276 try:
5277 if rh.utc:
5278 t = time.gmtime(currentTime)
5279 else:
5280 t = time.localtime(currentTime)
5281 currentHour = t[3]
5282 currentMinute = t[4]
5283 currentSecond = t[5]
5284 # r is the number of seconds left between now and midnight
5285 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5286 currentMinute) * 60 +
5287 currentSecond)
5288 result = currentTime + r
5289 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5290 print('currentHour: %s' % currentHour, file=sys.stderr)
5291 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5292 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5293 print('r: %s' % r, file=sys.stderr)
5294 print('result: %s' % result, file=sys.stderr)
5295 except Exception:
5296 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5297 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005298 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005299 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5300
Vinay Sajip60ccd822011-05-09 17:32:09 +01005301
Vinay Sajip223349c2015-10-01 20:37:54 +01005302@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005303class NTEventLogHandlerTest(BaseTest):
5304 def test_basic(self):
5305 logtype = 'Application'
5306 elh = win32evtlog.OpenEventLog(None, logtype)
5307 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005308
5309 try:
5310 h = logging.handlers.NTEventLogHandler('test_logging')
5311 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005312 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005313 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005314 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005315
Vinay Sajip60ccd822011-05-09 17:32:09 +01005316 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5317 h.handle(r)
5318 h.close()
5319 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005320 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005321 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5322 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5323 found = False
5324 GO_BACK = 100
5325 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5326 for e in events:
5327 if e.SourceName != 'test_logging':
5328 continue
5329 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5330 if msg != 'Test Log Message\r\n':
5331 continue
5332 found = True
5333 break
5334 msg = 'Record not found in event log, went back %d records' % GO_BACK
5335 self.assertTrue(found, msg=msg)
5336
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005337
5338class MiscTestCase(unittest.TestCase):
5339 def test__all__(self):
5340 blacklist = {'logThreads', 'logMultiprocessing',
5341 'logProcesses', 'currentframe',
5342 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5343 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger',
Martin Panter94332cb2016-10-20 05:10:44 +00005344 'root', 'threading'}
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005345 support.check__all__(self, logging, blacklist=blacklist)
5346
5347
Christian Heimes180510d2008-03-03 19:15:45 +00005348# Set the locale to the platform-dependent default. I have no idea
5349# why the test does this, but in any case we save the current locale
5350# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005351@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005352def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005353 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005354 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5355 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5356 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5357 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5358 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5359 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5360 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5361 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005362 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005363 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005364 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005365 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005366 ]
5367 if hasattr(logging.handlers, 'QueueListener'):
5368 tests.append(QueueListenerTest)
5369 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005370
Christian Heimes180510d2008-03-03 19:15:45 +00005371if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005372 test_main()