blob: 410eae2208688435aaa7251b81314850c0161471 [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
Victor Stinnerf5393dc2020-10-12 06:30:48 +02001164 self.threads = []
Miss Islington (bot)2c050e52020-08-16 08:34:27 -07001165
1166 def removeTarget(self):
1167 self.mem_hdlr.setTarget(None)
1168
1169 def handle(self, msg):
Victor Stinnerf5393dc2020-10-12 06:30:48 +02001170 thread = threading.Thread(target=self.removeTarget)
1171 self.threads.append(thread)
1172 thread.start()
Miss Islington (bot)2c050e52020-08-16 08:34:27 -07001173
1174 target = MockRaceConditionHandler(self.mem_hdlr)
Victor Stinnerf5393dc2020-10-12 06:30:48 +02001175 try:
1176 self.mem_hdlr.setTarget(target)
Miss Islington (bot)2c050e52020-08-16 08:34:27 -07001177
Victor Stinnerf5393dc2020-10-12 06:30:48 +02001178 for _ in range(10):
1179 time.sleep(0.005)
1180 self.mem_logger.info("not flushed")
1181 self.mem_logger.warning("flushed")
1182 finally:
1183 for thread in target.threads:
1184 support.join_thread(thread)
Miss Islington (bot)2c050e52020-08-16 08:34:27 -07001185
Christian Heimes180510d2008-03-03 19:15:45 +00001186
1187class ExceptionFormatter(logging.Formatter):
1188 """A special exception formatter."""
1189 def formatException(self, ei):
1190 return "Got a [%s]" % ei[0].__name__
1191
1192
1193class ConfigFileTest(BaseTest):
1194
1195 """Reading logging config from a .ini-style config file."""
1196
Xtreak087570a2018-07-02 14:27:46 +05301197 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001198 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001199
1200 # config0 is a standard configuration.
1201 config0 = """
1202 [loggers]
1203 keys=root
1204
1205 [handlers]
1206 keys=hand1
1207
1208 [formatters]
1209 keys=form1
1210
1211 [logger_root]
1212 level=WARNING
1213 handlers=hand1
1214
1215 [handler_hand1]
1216 class=StreamHandler
1217 level=NOTSET
1218 formatter=form1
1219 args=(sys.stdout,)
1220
1221 [formatter_form1]
1222 format=%(levelname)s ++ %(message)s
1223 datefmt=
1224 """
1225
1226 # config1 adds a little to the standard configuration.
1227 config1 = """
1228 [loggers]
1229 keys=root,parser
1230
1231 [handlers]
1232 keys=hand1
1233
1234 [formatters]
1235 keys=form1
1236
1237 [logger_root]
1238 level=WARNING
1239 handlers=
1240
1241 [logger_parser]
1242 level=DEBUG
1243 handlers=hand1
1244 propagate=1
1245 qualname=compiler.parser
1246
1247 [handler_hand1]
1248 class=StreamHandler
1249 level=NOTSET
1250 formatter=form1
1251 args=(sys.stdout,)
1252
1253 [formatter_form1]
1254 format=%(levelname)s ++ %(message)s
1255 datefmt=
1256 """
1257
Vinay Sajip3f84b072011-03-07 17:49:33 +00001258 # config1a moves the handler to the root.
1259 config1a = """
1260 [loggers]
1261 keys=root,parser
1262
1263 [handlers]
1264 keys=hand1
1265
1266 [formatters]
1267 keys=form1
1268
1269 [logger_root]
1270 level=WARNING
1271 handlers=hand1
1272
1273 [logger_parser]
1274 level=DEBUG
1275 handlers=
1276 propagate=1
1277 qualname=compiler.parser
1278
1279 [handler_hand1]
1280 class=StreamHandler
1281 level=NOTSET
1282 formatter=form1
1283 args=(sys.stdout,)
1284
1285 [formatter_form1]
1286 format=%(levelname)s ++ %(message)s
1287 datefmt=
1288 """
1289
Christian Heimes180510d2008-03-03 19:15:45 +00001290 # config2 has a subtle configuration error that should be reported
1291 config2 = config1.replace("sys.stdout", "sys.stbout")
1292
1293 # config3 has a less subtle configuration error
1294 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1295
1296 # config4 specifies a custom formatter class to be loaded
1297 config4 = """
1298 [loggers]
1299 keys=root
1300
1301 [handlers]
1302 keys=hand1
1303
1304 [formatters]
1305 keys=form1
1306
1307 [logger_root]
1308 level=NOTSET
1309 handlers=hand1
1310
1311 [handler_hand1]
1312 class=StreamHandler
1313 level=NOTSET
1314 formatter=form1
1315 args=(sys.stdout,)
1316
1317 [formatter_form1]
1318 class=""" + __name__ + """.ExceptionFormatter
1319 format=%(levelname)s:%(name)s:%(message)s
1320 datefmt=
1321 """
1322
Georg Brandl3dbca812008-07-23 16:10:53 +00001323 # config5 specifies a custom handler class to be loaded
1324 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1325
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001326 # config6 uses ', ' delimiters in the handlers and formatters sections
1327 config6 = """
1328 [loggers]
1329 keys=root,parser
1330
1331 [handlers]
1332 keys=hand1, hand2
1333
1334 [formatters]
1335 keys=form1, form2
1336
1337 [logger_root]
1338 level=WARNING
1339 handlers=
1340
1341 [logger_parser]
1342 level=DEBUG
1343 handlers=hand1
1344 propagate=1
1345 qualname=compiler.parser
1346
1347 [handler_hand1]
1348 class=StreamHandler
1349 level=NOTSET
1350 formatter=form1
1351 args=(sys.stdout,)
1352
1353 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001354 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001355 level=NOTSET
1356 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001357 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001358
1359 [formatter_form1]
1360 format=%(levelname)s ++ %(message)s
1361 datefmt=
1362
1363 [formatter_form2]
1364 format=%(message)s
1365 datefmt=
1366 """
1367
Preston Landers6ea56d22017-08-02 15:44:28 -05001368 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001369 config7 = """
1370 [loggers]
1371 keys=root,parser,compiler
1372
1373 [handlers]
1374 keys=hand1
1375
1376 [formatters]
1377 keys=form1
1378
1379 [logger_root]
1380 level=WARNING
1381 handlers=hand1
1382
1383 [logger_compiler]
1384 level=DEBUG
1385 handlers=
1386 propagate=1
1387 qualname=compiler
1388
1389 [logger_parser]
1390 level=DEBUG
1391 handlers=
1392 propagate=1
1393 qualname=compiler.parser
1394
1395 [handler_hand1]
1396 class=StreamHandler
1397 level=NOTSET
1398 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001399 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001400
1401 [formatter_form1]
1402 format=%(levelname)s ++ %(message)s
1403 datefmt=
1404 """
1405
Xtreak087570a2018-07-02 14:27:46 +05301406 # config 8, check for resource warning
1407 config8 = r"""
1408 [loggers]
1409 keys=root
1410
1411 [handlers]
1412 keys=file
1413
1414 [formatters]
1415 keys=
1416
1417 [logger_root]
1418 level=DEBUG
1419 handlers=file
1420
1421 [handler_file]
1422 class=FileHandler
1423 level=DEBUG
1424 args=("{tempfile}",)
1425 """
1426
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001427 disable_test = """
1428 [loggers]
1429 keys=root
1430
1431 [handlers]
1432 keys=screen
1433
1434 [formatters]
1435 keys=
1436
1437 [logger_root]
1438 level=DEBUG
1439 handlers=screen
1440
1441 [handler_screen]
1442 level=DEBUG
1443 class=StreamHandler
1444 args=(sys.stdout,)
1445 formatter=
1446 """
1447
1448 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001449 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001450 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001451
1452 def test_config0_ok(self):
1453 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001454 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001455 self.apply_config(self.config0)
1456 logger = logging.getLogger()
1457 # Won't output anything
1458 logger.info(self.next_message())
1459 # Outputs a message
1460 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001461 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001462 ('ERROR', '2'),
1463 ], stream=output)
1464 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001465 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001466
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001467 def test_config0_using_cp_ok(self):
1468 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001469 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001470 file = io.StringIO(textwrap.dedent(self.config0))
1471 cp = configparser.ConfigParser()
1472 cp.read_file(file)
1473 logging.config.fileConfig(cp)
1474 logger = logging.getLogger()
1475 # Won't output anything
1476 logger.info(self.next_message())
1477 # Outputs a message
1478 logger.error(self.next_message())
1479 self.assert_log_lines([
1480 ('ERROR', '2'),
1481 ], stream=output)
1482 # Original logger output is empty.
1483 self.assert_log_lines([])
1484
Georg Brandl3dbca812008-07-23 16:10:53 +00001485 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001486 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001487 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001488 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001489 logger = logging.getLogger("compiler.parser")
1490 # Both will output a message
1491 logger.info(self.next_message())
1492 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001493 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001494 ('INFO', '1'),
1495 ('ERROR', '2'),
1496 ], stream=output)
1497 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001498 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001499
1500 def test_config2_failure(self):
1501 # A simple config file which overrides the default settings.
1502 self.assertRaises(Exception, self.apply_config, self.config2)
1503
1504 def test_config3_failure(self):
1505 # A simple config file which overrides the default settings.
1506 self.assertRaises(Exception, self.apply_config, self.config3)
1507
1508 def test_config4_ok(self):
1509 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001510 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001511 self.apply_config(self.config4)
1512 logger = logging.getLogger()
1513 try:
1514 raise RuntimeError()
1515 except RuntimeError:
1516 logging.exception("just testing")
1517 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001518 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001519 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1520 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001521 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001522
Georg Brandl3dbca812008-07-23 16:10:53 +00001523 def test_config5_ok(self):
1524 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001525
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001526 def test_config6_ok(self):
1527 self.test_config1_ok(config=self.config6)
1528
Vinay Sajip3f84b072011-03-07 17:49:33 +00001529 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001530 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001531 self.apply_config(self.config1a)
1532 logger = logging.getLogger("compiler.parser")
1533 # See issue #11424. compiler-hyphenated sorts
1534 # between compiler and compiler.xyz and this
1535 # was preventing compiler.xyz from being included
1536 # in the child loggers of compiler because of an
1537 # overzealous loop termination condition.
1538 hyphenated = logging.getLogger('compiler-hyphenated')
1539 # All will output a message
1540 logger.info(self.next_message())
1541 logger.error(self.next_message())
1542 hyphenated.critical(self.next_message())
1543 self.assert_log_lines([
1544 ('INFO', '1'),
1545 ('ERROR', '2'),
1546 ('CRITICAL', '3'),
1547 ], stream=output)
1548 # Original logger output is empty.
1549 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001550 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001551 self.apply_config(self.config7)
1552 logger = logging.getLogger("compiler.parser")
1553 self.assertFalse(logger.disabled)
1554 # Both will output a message
1555 logger.info(self.next_message())
1556 logger.error(self.next_message())
1557 logger = logging.getLogger("compiler.lexer")
1558 # Both will output a message
1559 logger.info(self.next_message())
1560 logger.error(self.next_message())
1561 # Will not appear
1562 hyphenated.critical(self.next_message())
1563 self.assert_log_lines([
1564 ('INFO', '4'),
1565 ('ERROR', '5'),
1566 ('INFO', '6'),
1567 ('ERROR', '7'),
1568 ], stream=output)
1569 # Original logger output is empty.
1570 self.assert_log_lines([])
1571
Xtreak087570a2018-07-02 14:27:46 +05301572 def test_config8_ok(self):
1573
1574 def cleanup(h1, fn):
1575 h1.close()
1576 os.remove(fn)
1577
1578 with self.check_no_resource_warning():
1579 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1580 os.close(fd)
1581
1582 # Replace single backslash with double backslash in windows
1583 # to avoid unicode error during string formatting
1584 if os.name == "nt":
1585 fn = fn.replace("\\", "\\\\")
1586
1587 config8 = self.config8.format(tempfile=fn)
1588
1589 self.apply_config(config8)
1590 self.apply_config(config8)
1591
1592 handler = logging.root.handlers[0]
1593 self.addCleanup(cleanup, handler, fn)
1594
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001595 def test_logger_disabling(self):
1596 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001597 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001598 self.assertFalse(logger.disabled)
1599 self.apply_config(self.disable_test)
1600 self.assertTrue(logger.disabled)
1601 self.apply_config(self.disable_test, disable_existing_loggers=False)
1602 self.assertFalse(logger.disabled)
1603
Lucas Cimonb15100f2019-10-31 09:06:25 +01001604 def test_config_set_handler_names(self):
1605 test_config = """
1606 [loggers]
1607 keys=root
1608
1609 [handlers]
1610 keys=hand1
1611
1612 [formatters]
1613 keys=form1
1614
1615 [logger_root]
1616 handlers=hand1
1617
1618 [handler_hand1]
1619 class=StreamHandler
1620 formatter=form1
1621
1622 [formatter_form1]
1623 format=%(levelname)s ++ %(message)s
1624 """
1625 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001626 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001627
Łukasz Langa214f18e2018-06-08 04:02:48 -07001628 def test_defaults_do_no_interpolation(self):
1629 """bpo-33802 defaults should not get interpolated"""
1630 ini = textwrap.dedent("""
1631 [formatters]
1632 keys=default
1633
1634 [formatter_default]
1635
1636 [handlers]
1637 keys=console
1638
1639 [handler_console]
1640 class=logging.StreamHandler
1641 args=tuple()
1642
1643 [loggers]
1644 keys=root
1645
1646 [logger_root]
1647 formatter=default
1648 handlers=console
1649 """).strip()
1650 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1651 try:
1652 os.write(fd, ini.encode('ascii'))
1653 os.close(fd)
1654 logging.config.fileConfig(
1655 fn,
1656 defaults=dict(
1657 version=1,
1658 disable_existing_loggers=False,
1659 formatters={
1660 "generic": {
1661 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1662 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1663 "class": "logging.Formatter"
1664 },
1665 },
1666 )
1667 )
1668 finally:
1669 os.unlink(fn)
1670
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001671
Christian Heimes180510d2008-03-03 19:15:45 +00001672class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001673
Christian Heimes180510d2008-03-03 19:15:45 +00001674 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001675
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001676 server_class = TestTCPServer
1677 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001678
Christian Heimes180510d2008-03-03 19:15:45 +00001679 def setUp(self):
1680 """Set up a TCP server to receive log messages, and a SocketHandler
1681 pointing to that server's address and port."""
1682 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001683 # Issue #29177: deal with errors that happen during setup
1684 self.server = self.sock_hdlr = self.server_exception = None
1685 try:
1686 self.server = server = self.server_class(self.address,
1687 self.handle_socket, 0.01)
1688 server.start()
1689 # Uncomment next line to test error recovery in setUp()
1690 # raise OSError('dummy error raised')
1691 except OSError as e:
1692 self.server_exception = e
1693 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001694 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001695 hcls = logging.handlers.SocketHandler
1696 if isinstance(server.server_address, tuple):
1697 self.sock_hdlr = hcls('localhost', server.port)
1698 else:
1699 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001700 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001701 self.root_logger.removeHandler(self.root_logger.handlers[0])
1702 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001703 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001704
Christian Heimes180510d2008-03-03 19:15:45 +00001705 def tearDown(self):
1706 """Shutdown the TCP server."""
1707 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001708 if self.sock_hdlr:
1709 self.root_logger.removeHandler(self.sock_hdlr)
1710 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001711 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001712 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001713 finally:
1714 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001715
Vinay Sajip7367d082011-05-02 13:17:27 +01001716 def handle_socket(self, request):
1717 conn = request.connection
1718 while True:
1719 chunk = conn.recv(4)
1720 if len(chunk) < 4:
1721 break
1722 slen = struct.unpack(">L", chunk)[0]
1723 chunk = conn.recv(slen)
1724 while len(chunk) < slen:
1725 chunk = chunk + conn.recv(slen - len(chunk))
1726 obj = pickle.loads(chunk)
1727 record = logging.makeLogRecord(obj)
1728 self.log_output += record.msg + '\n'
1729 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001730
Christian Heimes180510d2008-03-03 19:15:45 +00001731 def test_output(self):
1732 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001733 if self.server_exception:
1734 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001735 logger = logging.getLogger("tcp")
1736 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001737 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001738 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001739 self.handled.acquire()
1740 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001741
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001742 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001743 if self.server_exception:
1744 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001745 # Avoid timing-related failures due to SocketHandler's own hard-wired
1746 # one-second timeout on socket.create_connection() (issue #16264).
1747 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001748 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001749 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001750 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001751 try:
1752 raise RuntimeError('Deliberate mistake')
1753 except RuntimeError:
1754 self.root_logger.exception('Never sent')
1755 self.root_logger.error('Never sent, either')
1756 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001757 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001758 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1759 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001760
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001761def _get_temp_domain_socket():
1762 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1763 os.close(fd)
1764 # just need a name - file can't be present, or we'll get an
1765 # 'address already in use' error.
1766 os.remove(fn)
1767 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001768
Victor Stinnerec5a8602014-06-02 14:41:51 +02001769@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001770class UnixSocketHandlerTest(SocketHandlerTest):
1771
1772 """Test for SocketHandler with unix sockets."""
1773
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001774 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001775 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001776
1777 def setUp(self):
1778 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001779 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001780 SocketHandlerTest.setUp(self)
1781
1782 def tearDown(self):
1783 SocketHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001784 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001785
Vinay Sajip7367d082011-05-02 13:17:27 +01001786class DatagramHandlerTest(BaseTest):
1787
1788 """Test for DatagramHandler."""
1789
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001790 server_class = TestUDPServer
1791 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001792
Vinay Sajip7367d082011-05-02 13:17:27 +01001793 def setUp(self):
1794 """Set up a UDP server to receive log messages, and a DatagramHandler
1795 pointing to that server's address and port."""
1796 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001797 # Issue #29177: deal with errors that happen during setup
1798 self.server = self.sock_hdlr = self.server_exception = None
1799 try:
1800 self.server = server = self.server_class(self.address,
1801 self.handle_datagram, 0.01)
1802 server.start()
1803 # Uncomment next line to test error recovery in setUp()
1804 # raise OSError('dummy error raised')
1805 except OSError as e:
1806 self.server_exception = e
1807 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001808 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001809 hcls = logging.handlers.DatagramHandler
1810 if isinstance(server.server_address, tuple):
1811 self.sock_hdlr = hcls('localhost', server.port)
1812 else:
1813 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001814 self.log_output = ''
1815 self.root_logger.removeHandler(self.root_logger.handlers[0])
1816 self.root_logger.addHandler(self.sock_hdlr)
1817 self.handled = threading.Event()
1818
1819 def tearDown(self):
1820 """Shutdown the UDP server."""
1821 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001822 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001823 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001824 if self.sock_hdlr:
1825 self.root_logger.removeHandler(self.sock_hdlr)
1826 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001827 finally:
1828 BaseTest.tearDown(self)
1829
1830 def handle_datagram(self, request):
1831 slen = struct.pack('>L', 0) # length of prefix
1832 packet = request.packet[len(slen):]
1833 obj = pickle.loads(packet)
1834 record = logging.makeLogRecord(obj)
1835 self.log_output += record.msg + '\n'
1836 self.handled.set()
1837
1838 def test_output(self):
1839 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001840 if self.server_exception:
1841 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001842 logger = logging.getLogger("udp")
1843 logger.error("spam")
1844 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001845 self.handled.clear()
1846 logger.error("eggs")
1847 self.handled.wait()
1848 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001849
Victor Stinnerec5a8602014-06-02 14:41:51 +02001850@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001851class UnixDatagramHandlerTest(DatagramHandlerTest):
1852
1853 """Test for DatagramHandler using Unix sockets."""
1854
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001855 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001856 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001857
1858 def setUp(self):
1859 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001860 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001861 DatagramHandlerTest.setUp(self)
1862
1863 def tearDown(self):
1864 DatagramHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001865 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001866
Vinay Sajip7367d082011-05-02 13:17:27 +01001867class SysLogHandlerTest(BaseTest):
1868
1869 """Test for SysLogHandler using UDP."""
1870
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001871 server_class = TestUDPServer
1872 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001873
Vinay Sajip7367d082011-05-02 13:17:27 +01001874 def setUp(self):
1875 """Set up a UDP server to receive log messages, and a SysLogHandler
1876 pointing to that server's address and port."""
1877 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001878 # Issue #29177: deal with errors that happen during setup
1879 self.server = self.sl_hdlr = self.server_exception = None
1880 try:
1881 self.server = server = self.server_class(self.address,
1882 self.handle_datagram, 0.01)
1883 server.start()
1884 # Uncomment next line to test error recovery in setUp()
1885 # raise OSError('dummy error raised')
1886 except OSError as e:
1887 self.server_exception = e
1888 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001889 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001890 hcls = logging.handlers.SysLogHandler
1891 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001892 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001893 else:
1894 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001895 self.log_output = ''
1896 self.root_logger.removeHandler(self.root_logger.handlers[0])
1897 self.root_logger.addHandler(self.sl_hdlr)
1898 self.handled = threading.Event()
1899
1900 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001901 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001902 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001903 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001904 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001905 if self.sl_hdlr:
1906 self.root_logger.removeHandler(self.sl_hdlr)
1907 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001908 finally:
1909 BaseTest.tearDown(self)
1910
1911 def handle_datagram(self, request):
1912 self.log_output = request.packet
1913 self.handled.set()
1914
1915 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001916 if self.server_exception:
1917 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001918 # The log message sent to the SysLogHandler is properly received.
1919 logger = logging.getLogger("slh")
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\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001923 self.handled.clear()
1924 self.sl_hdlr.append_nul = False
1925 logger.error("sp\xe4m")
1926 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001927 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001928 self.handled.clear()
1929 self.sl_hdlr.ident = "h\xe4m-"
1930 logger.error("sp\xe4m")
1931 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001932 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001933
Victor Stinnerec5a8602014-06-02 14:41:51 +02001934@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001935class UnixSysLogHandlerTest(SysLogHandlerTest):
1936
1937 """Test for SysLogHandler with Unix sockets."""
1938
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001939 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001940 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001941
1942 def setUp(self):
1943 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001944 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001945 SysLogHandlerTest.setUp(self)
1946
1947 def tearDown(self):
1948 SysLogHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001949 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001950
Serhiy Storchaka16994912020-04-25 10:06:29 +03001951@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001952 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001953class IPv6SysLogHandlerTest(SysLogHandlerTest):
1954
1955 """Test for SysLogHandler with IPv6 host."""
1956
1957 server_class = TestUDPServer
1958 address = ('::1', 0)
1959
1960 def setUp(self):
1961 self.server_class.address_family = socket.AF_INET6
1962 super(IPv6SysLogHandlerTest, self).setUp()
1963
1964 def tearDown(self):
1965 self.server_class.address_family = socket.AF_INET
1966 super(IPv6SysLogHandlerTest, self).tearDown()
1967
Vinay Sajip7367d082011-05-02 13:17:27 +01001968class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001969 """Test for HTTPHandler."""
1970
1971 def setUp(self):
1972 """Set up an HTTP server to receive log messages, and a HTTPHandler
1973 pointing to that server's address and port."""
1974 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001975 self.handled = threading.Event()
1976
Vinay Sajip7367d082011-05-02 13:17:27 +01001977 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001978 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001979 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001980 if self.command == 'POST':
1981 try:
1982 rlen = int(request.headers['Content-Length'])
1983 self.post_data = request.rfile.read(rlen)
1984 except:
1985 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001986 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001987 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001988 self.handled.set()
1989
1990 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001991 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001992 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001993 root_logger = self.root_logger
1994 root_logger.removeHandler(self.root_logger.handlers[0])
1995 for secure in (False, True):
1996 addr = ('localhost', 0)
1997 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001998 try:
1999 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01002000 except ImportError:
2001 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002002 else:
2003 here = os.path.dirname(__file__)
2004 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02002005 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002006 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06002007
2008 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01002009 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01002010 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06002011 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01002012 self.server = server = TestHTTPServer(addr, self.handle_request,
2013 0.01, sslctx=sslctx)
2014 server.start()
2015 server.ready.wait()
2016 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01002017 secure_client = secure and sslctx
2018 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06002019 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01002020 context=context,
2021 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01002022 self.log_data = None
2023 root_logger.addHandler(self.h_hdlr)
2024
2025 for method in ('GET', 'POST'):
2026 self.h_hdlr.method = method
2027 self.handled.clear()
2028 msg = "sp\xe4m"
2029 logger.error(msg)
2030 self.handled.wait()
2031 self.assertEqual(self.log_data.path, '/frob')
2032 self.assertEqual(self.command, method)
2033 if method == 'GET':
2034 d = parse_qs(self.log_data.query)
2035 else:
2036 d = parse_qs(self.post_data.decode('utf-8'))
2037 self.assertEqual(d['name'], ['http'])
2038 self.assertEqual(d['funcName'], ['test_output'])
2039 self.assertEqual(d['msg'], [msg])
2040
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002041 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002042 self.root_logger.removeHandler(self.h_hdlr)
2043 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002044
Christian Heimes180510d2008-03-03 19:15:45 +00002045class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002046
Christian Heimes180510d2008-03-03 19:15:45 +00002047 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002048
Christian Heimes180510d2008-03-03 19:15:45 +00002049 def setUp(self):
2050 """Create a dict to remember potentially destroyed objects."""
2051 BaseTest.setUp(self)
2052 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002053
Christian Heimes180510d2008-03-03 19:15:45 +00002054 def _watch_for_survival(self, *args):
2055 """Watch the given objects for survival, by creating weakrefs to
2056 them."""
2057 for obj in args:
2058 key = id(obj), repr(obj)
2059 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002060
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002061 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002062 """Assert that all objects watched for survival have survived."""
2063 # Trigger cycle breaking.
2064 gc.collect()
2065 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002066 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002067 if ref() is None:
2068 dead.append(repr_)
2069 if dead:
2070 self.fail("%d objects should have survived "
2071 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002072
Christian Heimes180510d2008-03-03 19:15:45 +00002073 def test_persistent_loggers(self):
2074 # Logger objects are persistent and retain their configuration, even
2075 # if visible references are destroyed.
2076 self.root_logger.setLevel(logging.INFO)
2077 foo = logging.getLogger("foo")
2078 self._watch_for_survival(foo)
2079 foo.setLevel(logging.DEBUG)
2080 self.root_logger.debug(self.next_message())
2081 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002082 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002083 ('foo', 'DEBUG', '2'),
2084 ])
2085 del foo
2086 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002087 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002088 # foo has retained its settings.
2089 bar = logging.getLogger("foo")
2090 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002091 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002092 ('foo', 'DEBUG', '2'),
2093 ('foo', 'DEBUG', '3'),
2094 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002095
Benjamin Petersonf91df042009-02-13 02:50:59 +00002096
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002097class EncodingTest(BaseTest):
2098 def test_encoding_plain_file(self):
2099 # In Python 2.x, a plain file object is treated as having no encoding.
2100 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002101 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2102 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002103 # the non-ascii data we write to the log.
2104 data = "foo\x80"
2105 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002106 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002107 log.addHandler(handler)
2108 try:
2109 # write non-ascii data to the log.
2110 log.warning(data)
2111 finally:
2112 log.removeHandler(handler)
2113 handler.close()
2114 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002115 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002116 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002117 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002118 finally:
2119 f.close()
2120 finally:
2121 if os.path.isfile(fn):
2122 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002123
Benjamin Petersonf91df042009-02-13 02:50:59 +00002124 def test_encoding_cyrillic_unicode(self):
2125 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002126 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002127 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002128 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002129 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002130 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002131 stream = io.BytesIO()
2132 writer = writer_class(stream, 'strict')
2133 handler = logging.StreamHandler(writer)
2134 log.addHandler(handler)
2135 try:
2136 log.warning(message)
2137 finally:
2138 log.removeHandler(handler)
2139 handler.close()
2140 # check we wrote exactly those bytes, ignoring trailing \n etc
2141 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002142 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002143 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2144
2145
Georg Brandlf9734072008-12-07 15:30:06 +00002146class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002147
Georg Brandlf9734072008-12-07 15:30:06 +00002148 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002149 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002150 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002151 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002152 warnings.filterwarnings("always", category=UserWarning)
2153 stream = io.StringIO()
2154 h = logging.StreamHandler(stream)
2155 logger = logging.getLogger("py.warnings")
2156 logger.addHandler(h)
2157 warnings.warn("I'm warning you...")
2158 logger.removeHandler(h)
2159 s = stream.getvalue()
2160 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002161 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002162
Mike53f7a7c2017-12-14 14:04:53 +03002163 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002164 a_file = io.StringIO()
2165 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2166 a_file, "Dummy line")
2167 s = a_file.getvalue()
2168 a_file.close()
2169 self.assertEqual(s,
2170 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2171
2172 def test_warnings_no_handlers(self):
2173 with warnings.catch_warnings():
2174 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002175 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002176
2177 # confirm our assumption: no loggers are set
2178 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002179 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002180
2181 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002182 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002183 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002184
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002185
2186def formatFunc(format, datefmt=None):
2187 return logging.Formatter(format, datefmt)
2188
BNMetrics18fb1fb2018-10-15 19:41:36 +01002189class myCustomFormatter:
2190 def __init__(self, fmt, datefmt=None):
2191 pass
2192
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002193def handlerFunc():
2194 return logging.StreamHandler()
2195
2196class CustomHandler(logging.StreamHandler):
2197 pass
2198
2199class ConfigDictTest(BaseTest):
2200
2201 """Reading logging config from a dictionary."""
2202
Xtreak087570a2018-07-02 14:27:46 +05302203 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002204 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002205
2206 # config0 is a standard configuration.
2207 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002208 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002209 'formatters': {
2210 'form1' : {
2211 'format' : '%(levelname)s ++ %(message)s',
2212 },
2213 },
2214 'handlers' : {
2215 'hand1' : {
2216 'class' : 'logging.StreamHandler',
2217 'formatter' : 'form1',
2218 'level' : 'NOTSET',
2219 'stream' : 'ext://sys.stdout',
2220 },
2221 },
2222 'root' : {
2223 'level' : 'WARNING',
2224 'handlers' : ['hand1'],
2225 },
2226 }
2227
2228 # config1 adds a little to the standard configuration.
2229 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002230 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002231 'formatters': {
2232 'form1' : {
2233 'format' : '%(levelname)s ++ %(message)s',
2234 },
2235 },
2236 'handlers' : {
2237 'hand1' : {
2238 'class' : 'logging.StreamHandler',
2239 'formatter' : 'form1',
2240 'level' : 'NOTSET',
2241 'stream' : 'ext://sys.stdout',
2242 },
2243 },
2244 'loggers' : {
2245 'compiler.parser' : {
2246 'level' : 'DEBUG',
2247 'handlers' : ['hand1'],
2248 },
2249 },
2250 'root' : {
2251 'level' : 'WARNING',
2252 },
2253 }
2254
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002255 # config1a moves the handler to the root. Used with config8a
2256 config1a = {
2257 'version': 1,
2258 'formatters': {
2259 'form1' : {
2260 'format' : '%(levelname)s ++ %(message)s',
2261 },
2262 },
2263 'handlers' : {
2264 'hand1' : {
2265 'class' : 'logging.StreamHandler',
2266 'formatter' : 'form1',
2267 'level' : 'NOTSET',
2268 'stream' : 'ext://sys.stdout',
2269 },
2270 },
2271 'loggers' : {
2272 'compiler.parser' : {
2273 'level' : 'DEBUG',
2274 },
2275 },
2276 'root' : {
2277 'level' : 'WARNING',
2278 'handlers' : ['hand1'],
2279 },
2280 }
2281
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002282 # config2 has a subtle configuration error that should be reported
2283 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002284 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002285 'formatters': {
2286 'form1' : {
2287 'format' : '%(levelname)s ++ %(message)s',
2288 },
2289 },
2290 'handlers' : {
2291 'hand1' : {
2292 'class' : 'logging.StreamHandler',
2293 'formatter' : 'form1',
2294 'level' : 'NOTSET',
2295 'stream' : 'ext://sys.stdbout',
2296 },
2297 },
2298 'loggers' : {
2299 'compiler.parser' : {
2300 'level' : 'DEBUG',
2301 'handlers' : ['hand1'],
2302 },
2303 },
2304 'root' : {
2305 'level' : 'WARNING',
2306 },
2307 }
2308
Mike53f7a7c2017-12-14 14:04:53 +03002309 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002310 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002311 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002312 'formatters': {
2313 'form1' : {
2314 'format' : '%(levelname)s ++ %(message)s',
2315 },
2316 },
2317 'handlers' : {
2318 'hand1' : {
2319 'class' : 'logging.StreamHandler',
2320 'formatter' : 'form1',
2321 'level' : 'NTOSET',
2322 'stream' : 'ext://sys.stdout',
2323 },
2324 },
2325 'loggers' : {
2326 'compiler.parser' : {
2327 'level' : 'DEBUG',
2328 'handlers' : ['hand1'],
2329 },
2330 },
2331 'root' : {
2332 'level' : 'WARNING',
2333 },
2334 }
2335
2336
Mike53f7a7c2017-12-14 14:04:53 +03002337 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002338 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002339 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002340 'formatters': {
2341 'form1' : {
2342 'format' : '%(levelname)s ++ %(message)s',
2343 },
2344 },
2345 'handlers' : {
2346 'hand1' : {
2347 'class' : 'logging.StreamHandler',
2348 'formatter' : 'form1',
2349 'level' : 'NOTSET',
2350 'stream' : 'ext://sys.stdout',
2351 },
2352 },
2353 'loggers' : {
2354 'compiler.parser' : {
2355 'level' : 'DEBUG',
2356 'handlers' : ['hand1'],
2357 },
2358 },
2359 'root' : {
2360 'level' : 'WRANING',
2361 },
2362 }
2363
2364 # config3 has a less subtle configuration error
2365 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002366 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002367 'formatters': {
2368 'form1' : {
2369 'format' : '%(levelname)s ++ %(message)s',
2370 },
2371 },
2372 'handlers' : {
2373 'hand1' : {
2374 'class' : 'logging.StreamHandler',
2375 'formatter' : 'misspelled_name',
2376 'level' : 'NOTSET',
2377 'stream' : 'ext://sys.stdout',
2378 },
2379 },
2380 'loggers' : {
2381 'compiler.parser' : {
2382 'level' : 'DEBUG',
2383 'handlers' : ['hand1'],
2384 },
2385 },
2386 'root' : {
2387 'level' : 'WARNING',
2388 },
2389 }
2390
2391 # config4 specifies a custom formatter class to be loaded
2392 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002393 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002394 'formatters': {
2395 'form1' : {
2396 '()' : __name__ + '.ExceptionFormatter',
2397 'format' : '%(levelname)s:%(name)s:%(message)s',
2398 },
2399 },
2400 'handlers' : {
2401 'hand1' : {
2402 'class' : 'logging.StreamHandler',
2403 'formatter' : 'form1',
2404 'level' : 'NOTSET',
2405 'stream' : 'ext://sys.stdout',
2406 },
2407 },
2408 'root' : {
2409 'level' : 'NOTSET',
2410 'handlers' : ['hand1'],
2411 },
2412 }
2413
2414 # As config4 but using an actual callable rather than a string
2415 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002416 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002417 'formatters': {
2418 'form1' : {
2419 '()' : ExceptionFormatter,
2420 'format' : '%(levelname)s:%(name)s:%(message)s',
2421 },
2422 'form2' : {
2423 '()' : __name__ + '.formatFunc',
2424 'format' : '%(levelname)s:%(name)s:%(message)s',
2425 },
2426 'form3' : {
2427 '()' : formatFunc,
2428 'format' : '%(levelname)s:%(name)s:%(message)s',
2429 },
2430 },
2431 'handlers' : {
2432 'hand1' : {
2433 'class' : 'logging.StreamHandler',
2434 'formatter' : 'form1',
2435 'level' : 'NOTSET',
2436 'stream' : 'ext://sys.stdout',
2437 },
2438 'hand2' : {
2439 '()' : handlerFunc,
2440 },
2441 },
2442 'root' : {
2443 'level' : 'NOTSET',
2444 'handlers' : ['hand1'],
2445 },
2446 }
2447
2448 # config5 specifies a custom handler class to be loaded
2449 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002450 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002451 'formatters': {
2452 'form1' : {
2453 'format' : '%(levelname)s ++ %(message)s',
2454 },
2455 },
2456 'handlers' : {
2457 'hand1' : {
2458 'class' : __name__ + '.CustomHandler',
2459 'formatter' : 'form1',
2460 'level' : 'NOTSET',
2461 'stream' : 'ext://sys.stdout',
2462 },
2463 },
2464 'loggers' : {
2465 'compiler.parser' : {
2466 'level' : 'DEBUG',
2467 'handlers' : ['hand1'],
2468 },
2469 },
2470 'root' : {
2471 'level' : 'WARNING',
2472 },
2473 }
2474
2475 # config6 specifies a custom handler class to be loaded
2476 # but has bad arguments
2477 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002478 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002479 'formatters': {
2480 'form1' : {
2481 'format' : '%(levelname)s ++ %(message)s',
2482 },
2483 },
2484 'handlers' : {
2485 'hand1' : {
2486 'class' : __name__ + '.CustomHandler',
2487 'formatter' : 'form1',
2488 'level' : 'NOTSET',
2489 'stream' : 'ext://sys.stdout',
2490 '9' : 'invalid parameter name',
2491 },
2492 },
2493 'loggers' : {
2494 'compiler.parser' : {
2495 'level' : 'DEBUG',
2496 'handlers' : ['hand1'],
2497 },
2498 },
2499 'root' : {
2500 'level' : 'WARNING',
2501 },
2502 }
2503
Mike53f7a7c2017-12-14 14:04:53 +03002504 # config 7 does not define compiler.parser but defines compiler.lexer
2505 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002506 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002507 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002508 'formatters': {
2509 'form1' : {
2510 'format' : '%(levelname)s ++ %(message)s',
2511 },
2512 },
2513 'handlers' : {
2514 'hand1' : {
2515 'class' : 'logging.StreamHandler',
2516 'formatter' : 'form1',
2517 'level' : 'NOTSET',
2518 'stream' : 'ext://sys.stdout',
2519 },
2520 },
2521 'loggers' : {
2522 'compiler.lexer' : {
2523 'level' : 'DEBUG',
2524 'handlers' : ['hand1'],
2525 },
2526 },
2527 'root' : {
2528 'level' : 'WARNING',
2529 },
2530 }
2531
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002532 # config8 defines both compiler and compiler.lexer
2533 # so compiler.parser should not be disabled (since
2534 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002535 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002536 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002537 'disable_existing_loggers' : False,
2538 'formatters': {
2539 'form1' : {
2540 'format' : '%(levelname)s ++ %(message)s',
2541 },
2542 },
2543 'handlers' : {
2544 'hand1' : {
2545 'class' : 'logging.StreamHandler',
2546 'formatter' : 'form1',
2547 'level' : 'NOTSET',
2548 'stream' : 'ext://sys.stdout',
2549 },
2550 },
2551 'loggers' : {
2552 'compiler' : {
2553 'level' : 'DEBUG',
2554 'handlers' : ['hand1'],
2555 },
2556 'compiler.lexer' : {
2557 },
2558 },
2559 'root' : {
2560 'level' : 'WARNING',
2561 },
2562 }
2563
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002564 # config8a disables existing loggers
2565 config8a = {
2566 'version': 1,
2567 'disable_existing_loggers' : True,
2568 'formatters': {
2569 'form1' : {
2570 'format' : '%(levelname)s ++ %(message)s',
2571 },
2572 },
2573 'handlers' : {
2574 'hand1' : {
2575 'class' : 'logging.StreamHandler',
2576 'formatter' : 'form1',
2577 'level' : 'NOTSET',
2578 'stream' : 'ext://sys.stdout',
2579 },
2580 },
2581 'loggers' : {
2582 'compiler' : {
2583 'level' : 'DEBUG',
2584 'handlers' : ['hand1'],
2585 },
2586 'compiler.lexer' : {
2587 },
2588 },
2589 'root' : {
2590 'level' : 'WARNING',
2591 },
2592 }
2593
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002594 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002595 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002596 'formatters': {
2597 'form1' : {
2598 'format' : '%(levelname)s ++ %(message)s',
2599 },
2600 },
2601 'handlers' : {
2602 'hand1' : {
2603 'class' : 'logging.StreamHandler',
2604 'formatter' : 'form1',
2605 'level' : 'WARNING',
2606 'stream' : 'ext://sys.stdout',
2607 },
2608 },
2609 'loggers' : {
2610 'compiler.parser' : {
2611 'level' : 'WARNING',
2612 'handlers' : ['hand1'],
2613 },
2614 },
2615 'root' : {
2616 'level' : 'NOTSET',
2617 },
2618 }
2619
2620 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002621 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002622 'incremental' : True,
2623 'handlers' : {
2624 'hand1' : {
2625 'level' : 'WARNING',
2626 },
2627 },
2628 'loggers' : {
2629 'compiler.parser' : {
2630 'level' : 'INFO',
2631 },
2632 },
2633 }
2634
2635 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002636 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002637 'incremental' : True,
2638 'handlers' : {
2639 'hand1' : {
2640 'level' : 'INFO',
2641 },
2642 },
2643 'loggers' : {
2644 'compiler.parser' : {
2645 'level' : 'INFO',
2646 },
2647 },
2648 }
2649
Mike53f7a7c2017-12-14 14:04:53 +03002650 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002651 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002652 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002653 'formatters': {
2654 'form1' : {
2655 'format' : '%(levelname)s ++ %(message)s',
2656 },
2657 },
2658 'filters' : {
2659 'filt1' : {
2660 'name' : 'compiler.parser',
2661 },
2662 },
2663 'handlers' : {
2664 'hand1' : {
2665 'class' : 'logging.StreamHandler',
2666 'formatter' : 'form1',
2667 'level' : 'NOTSET',
2668 'stream' : 'ext://sys.stdout',
2669 'filters' : ['filt1'],
2670 },
2671 },
2672 'loggers' : {
2673 'compiler.parser' : {
2674 'level' : 'DEBUG',
2675 'filters' : ['filt1'],
2676 },
2677 },
2678 'root' : {
2679 'level' : 'WARNING',
2680 'handlers' : ['hand1'],
2681 },
2682 }
2683
Mike53f7a7c2017-12-14 14:04:53 +03002684 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002685 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002686 'version': 1,
2687 'true_formatters': {
2688 'form1' : {
2689 'format' : '%(levelname)s ++ %(message)s',
2690 },
2691 },
2692 'handler_configs': {
2693 'hand1' : {
2694 'class' : 'logging.StreamHandler',
2695 'formatter' : 'form1',
2696 'level' : 'NOTSET',
2697 'stream' : 'ext://sys.stdout',
2698 },
2699 },
2700 'formatters' : 'cfg://true_formatters',
2701 'handlers' : {
2702 'hand1' : 'cfg://handler_configs[hand1]',
2703 },
2704 'loggers' : {
2705 'compiler.parser' : {
2706 'level' : 'DEBUG',
2707 'handlers' : ['hand1'],
2708 },
2709 },
2710 'root' : {
2711 'level' : 'WARNING',
2712 },
2713 }
2714
Mike53f7a7c2017-12-14 14:04:53 +03002715 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002716 config12 = {
2717 'true_formatters': {
2718 'form1' : {
2719 'format' : '%(levelname)s ++ %(message)s',
2720 },
2721 },
2722 'handler_configs': {
2723 'hand1' : {
2724 'class' : 'logging.StreamHandler',
2725 'formatter' : 'form1',
2726 'level' : 'NOTSET',
2727 'stream' : 'ext://sys.stdout',
2728 },
2729 },
2730 'formatters' : 'cfg://true_formatters',
2731 'handlers' : {
2732 'hand1' : 'cfg://handler_configs[hand1]',
2733 },
2734 'loggers' : {
2735 'compiler.parser' : {
2736 'level' : 'DEBUG',
2737 'handlers' : ['hand1'],
2738 },
2739 },
2740 'root' : {
2741 'level' : 'WARNING',
2742 },
2743 }
2744
Mike53f7a7c2017-12-14 14:04:53 +03002745 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002746 config13 = {
2747 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002748 'true_formatters': {
2749 'form1' : {
2750 'format' : '%(levelname)s ++ %(message)s',
2751 },
2752 },
2753 'handler_configs': {
2754 'hand1' : {
2755 'class' : 'logging.StreamHandler',
2756 'formatter' : 'form1',
2757 'level' : 'NOTSET',
2758 'stream' : 'ext://sys.stdout',
2759 },
2760 },
2761 'formatters' : 'cfg://true_formatters',
2762 'handlers' : {
2763 'hand1' : 'cfg://handler_configs[hand1]',
2764 },
2765 'loggers' : {
2766 'compiler.parser' : {
2767 'level' : 'DEBUG',
2768 'handlers' : ['hand1'],
2769 },
2770 },
2771 'root' : {
2772 'level' : 'WARNING',
2773 },
2774 }
2775
Vinay Sajip8d270232012-11-15 14:20:18 +00002776 # As config0, but with properties
2777 config14 = {
2778 'version': 1,
2779 'formatters': {
2780 'form1' : {
2781 'format' : '%(levelname)s ++ %(message)s',
2782 },
2783 },
2784 'handlers' : {
2785 'hand1' : {
2786 'class' : 'logging.StreamHandler',
2787 'formatter' : 'form1',
2788 'level' : 'NOTSET',
2789 'stream' : 'ext://sys.stdout',
2790 '.': {
2791 'foo': 'bar',
2792 'terminator': '!\n',
2793 }
2794 },
2795 },
2796 'root' : {
2797 'level' : 'WARNING',
2798 'handlers' : ['hand1'],
2799 },
2800 }
2801
Vinay Sajip3f885b52013-03-22 15:19:54 +00002802 out_of_order = {
2803 "version": 1,
2804 "formatters": {
2805 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002806 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2807 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002808 }
2809 },
2810 "handlers": {
2811 "fileGlobal": {
2812 "class": "logging.StreamHandler",
2813 "level": "DEBUG",
2814 "formatter": "mySimpleFormatter"
2815 },
2816 "bufferGlobal": {
2817 "class": "logging.handlers.MemoryHandler",
2818 "capacity": 5,
2819 "formatter": "mySimpleFormatter",
2820 "target": "fileGlobal",
2821 "level": "DEBUG"
2822 }
2823 },
2824 "loggers": {
2825 "mymodule": {
2826 "level": "DEBUG",
2827 "handlers": ["bufferGlobal"],
2828 "propagate": "true"
2829 }
2830 }
2831 }
2832
BNMetrics18fb1fb2018-10-15 19:41:36 +01002833 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2834 custom_formatter_class_validate = {
2835 'version': 1,
2836 'formatters': {
2837 'form1': {
2838 '()': __name__ + '.ExceptionFormatter',
2839 'format': '%(levelname)s:%(name)s:%(message)s',
2840 'validate': False,
2841 },
2842 },
2843 'handlers' : {
2844 'hand1' : {
2845 'class': 'logging.StreamHandler',
2846 'formatter': 'form1',
2847 'level': 'NOTSET',
2848 'stream': 'ext://sys.stdout',
2849 },
2850 },
2851 "loggers": {
2852 "my_test_logger_custom_formatter": {
2853 "level": "DEBUG",
2854 "handlers": ["hand1"],
2855 "propagate": "true"
2856 }
2857 }
2858 }
2859
2860 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2861 custom_formatter_class_validate2 = {
2862 'version': 1,
2863 'formatters': {
2864 'form1': {
2865 'class': __name__ + '.ExceptionFormatter',
2866 'format': '%(levelname)s:%(name)s:%(message)s',
2867 'validate': False,
2868 },
2869 },
2870 'handlers' : {
2871 'hand1' : {
2872 'class': 'logging.StreamHandler',
2873 'formatter': 'form1',
2874 'level': 'NOTSET',
2875 'stream': 'ext://sys.stdout',
2876 },
2877 },
2878 "loggers": {
2879 "my_test_logger_custom_formatter": {
2880 "level": "DEBUG",
2881 "handlers": ["hand1"],
2882 "propagate": "true"
2883 }
2884 }
2885 }
2886
2887 # Configuration with custom class that is not inherited from logging.Formatter
2888 custom_formatter_class_validate3 = {
2889 'version': 1,
2890 'formatters': {
2891 'form1': {
2892 'class': __name__ + '.myCustomFormatter',
2893 'format': '%(levelname)s:%(name)s:%(message)s',
2894 'validate': False,
2895 },
2896 },
2897 'handlers' : {
2898 'hand1' : {
2899 'class': 'logging.StreamHandler',
2900 'formatter': 'form1',
2901 'level': 'NOTSET',
2902 'stream': 'ext://sys.stdout',
2903 },
2904 },
2905 "loggers": {
2906 "my_test_logger_custom_formatter": {
2907 "level": "DEBUG",
2908 "handlers": ["hand1"],
2909 "propagate": "true"
2910 }
2911 }
2912 }
2913
2914 # Configuration with custom function and 'validate' set to False
2915 custom_formatter_with_function = {
2916 'version': 1,
2917 'formatters': {
2918 'form1': {
2919 '()': formatFunc,
2920 'format': '%(levelname)s:%(name)s:%(message)s',
2921 'validate': False,
2922 },
2923 },
2924 'handlers' : {
2925 'hand1' : {
2926 'class': 'logging.StreamHandler',
2927 'formatter': 'form1',
2928 'level': 'NOTSET',
2929 'stream': 'ext://sys.stdout',
2930 },
2931 },
2932 "loggers": {
2933 "my_test_logger_custom_formatter": {
2934 "level": "DEBUG",
2935 "handlers": ["hand1"],
2936 "propagate": "true"
2937 }
2938 }
2939 }
2940
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002941 def apply_config(self, conf):
2942 logging.config.dictConfig(conf)
2943
2944 def test_config0_ok(self):
2945 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002946 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002947 self.apply_config(self.config0)
2948 logger = logging.getLogger()
2949 # Won't output anything
2950 logger.info(self.next_message())
2951 # Outputs a message
2952 logger.error(self.next_message())
2953 self.assert_log_lines([
2954 ('ERROR', '2'),
2955 ], stream=output)
2956 # Original logger output is empty.
2957 self.assert_log_lines([])
2958
2959 def test_config1_ok(self, config=config1):
2960 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002961 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002962 self.apply_config(config)
2963 logger = logging.getLogger("compiler.parser")
2964 # Both will output a message
2965 logger.info(self.next_message())
2966 logger.error(self.next_message())
2967 self.assert_log_lines([
2968 ('INFO', '1'),
2969 ('ERROR', '2'),
2970 ], stream=output)
2971 # Original logger output is empty.
2972 self.assert_log_lines([])
2973
2974 def test_config2_failure(self):
2975 # A simple config which overrides the default settings.
2976 self.assertRaises(Exception, self.apply_config, self.config2)
2977
2978 def test_config2a_failure(self):
2979 # A simple config which overrides the default settings.
2980 self.assertRaises(Exception, self.apply_config, self.config2a)
2981
2982 def test_config2b_failure(self):
2983 # A simple config which overrides the default settings.
2984 self.assertRaises(Exception, self.apply_config, self.config2b)
2985
2986 def test_config3_failure(self):
2987 # A simple config which overrides the default settings.
2988 self.assertRaises(Exception, self.apply_config, self.config3)
2989
2990 def test_config4_ok(self):
2991 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002992 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002993 self.apply_config(self.config4)
2994 #logger = logging.getLogger()
2995 try:
2996 raise RuntimeError()
2997 except RuntimeError:
2998 logging.exception("just testing")
2999 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003000 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003001 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3002 # Original logger output is empty
3003 self.assert_log_lines([])
3004
3005 def test_config4a_ok(self):
3006 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003007 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003008 self.apply_config(self.config4a)
3009 #logger = logging.getLogger()
3010 try:
3011 raise RuntimeError()
3012 except RuntimeError:
3013 logging.exception("just testing")
3014 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003015 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003016 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3017 # Original logger output is empty
3018 self.assert_log_lines([])
3019
3020 def test_config5_ok(self):
3021 self.test_config1_ok(config=self.config5)
3022
3023 def test_config6_failure(self):
3024 self.assertRaises(Exception, self.apply_config, self.config6)
3025
3026 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003027 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003028 self.apply_config(self.config1)
3029 logger = logging.getLogger("compiler.parser")
3030 # Both will output a message
3031 logger.info(self.next_message())
3032 logger.error(self.next_message())
3033 self.assert_log_lines([
3034 ('INFO', '1'),
3035 ('ERROR', '2'),
3036 ], stream=output)
3037 # Original logger output is empty.
3038 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003039 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003040 self.apply_config(self.config7)
3041 logger = logging.getLogger("compiler.parser")
3042 self.assertTrue(logger.disabled)
3043 logger = logging.getLogger("compiler.lexer")
3044 # Both will output a message
3045 logger.info(self.next_message())
3046 logger.error(self.next_message())
3047 self.assert_log_lines([
3048 ('INFO', '3'),
3049 ('ERROR', '4'),
3050 ], stream=output)
3051 # Original logger output is empty.
3052 self.assert_log_lines([])
3053
Mike53f7a7c2017-12-14 14:04:53 +03003054 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003055 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003056 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003057 self.apply_config(self.config1)
3058 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003059 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003060 logger.info(self.next_message())
3061 logger.error(self.next_message())
3062 self.assert_log_lines([
3063 ('INFO', '1'),
3064 ('ERROR', '2'),
3065 ], stream=output)
3066 # Original logger output is empty.
3067 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003068 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003069 self.apply_config(self.config8)
3070 logger = logging.getLogger("compiler.parser")
3071 self.assertFalse(logger.disabled)
3072 # Both will output a message
3073 logger.info(self.next_message())
3074 logger.error(self.next_message())
3075 logger = logging.getLogger("compiler.lexer")
3076 # Both will output a message
3077 logger.info(self.next_message())
3078 logger.error(self.next_message())
3079 self.assert_log_lines([
3080 ('INFO', '3'),
3081 ('ERROR', '4'),
3082 ('INFO', '5'),
3083 ('ERROR', '6'),
3084 ], stream=output)
3085 # Original logger output is empty.
3086 self.assert_log_lines([])
3087
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003088 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003089 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003090 self.apply_config(self.config1a)
3091 logger = logging.getLogger("compiler.parser")
3092 # See issue #11424. compiler-hyphenated sorts
3093 # between compiler and compiler.xyz and this
3094 # was preventing compiler.xyz from being included
3095 # in the child loggers of compiler because of an
3096 # overzealous loop termination condition.
3097 hyphenated = logging.getLogger('compiler-hyphenated')
3098 # All will output a message
3099 logger.info(self.next_message())
3100 logger.error(self.next_message())
3101 hyphenated.critical(self.next_message())
3102 self.assert_log_lines([
3103 ('INFO', '1'),
3104 ('ERROR', '2'),
3105 ('CRITICAL', '3'),
3106 ], stream=output)
3107 # Original logger output is empty.
3108 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003109 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003110 self.apply_config(self.config8a)
3111 logger = logging.getLogger("compiler.parser")
3112 self.assertFalse(logger.disabled)
3113 # Both will output a message
3114 logger.info(self.next_message())
3115 logger.error(self.next_message())
3116 logger = logging.getLogger("compiler.lexer")
3117 # Both will output a message
3118 logger.info(self.next_message())
3119 logger.error(self.next_message())
3120 # Will not appear
3121 hyphenated.critical(self.next_message())
3122 self.assert_log_lines([
3123 ('INFO', '4'),
3124 ('ERROR', '5'),
3125 ('INFO', '6'),
3126 ('ERROR', '7'),
3127 ], stream=output)
3128 # Original logger output is empty.
3129 self.assert_log_lines([])
3130
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003131 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003132 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003133 self.apply_config(self.config9)
3134 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003135 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003136 logger.info(self.next_message())
3137 self.assert_log_lines([], stream=output)
3138 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003139 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003140 logger.info(self.next_message())
3141 self.assert_log_lines([], stream=output)
3142 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003143 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003144 logger.info(self.next_message())
3145 self.assert_log_lines([
3146 ('INFO', '3'),
3147 ], stream=output)
3148
3149 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003150 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003151 self.apply_config(self.config10)
3152 logger = logging.getLogger("compiler.parser")
3153 logger.warning(self.next_message())
3154 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003155 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003156 logger.warning(self.next_message())
3157 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003158 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003159 logger.warning(self.next_message())
3160 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003161 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003162 logger.error(self.next_message())
3163 self.assert_log_lines([
3164 ('WARNING', '1'),
3165 ('ERROR', '4'),
3166 ], stream=output)
3167
3168 def test_config11_ok(self):
3169 self.test_config1_ok(self.config11)
3170
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003171 def test_config12_failure(self):
3172 self.assertRaises(Exception, self.apply_config, self.config12)
3173
3174 def test_config13_failure(self):
3175 self.assertRaises(Exception, self.apply_config, self.config13)
3176
Vinay Sajip8d270232012-11-15 14:20:18 +00003177 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003178 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003179 self.apply_config(self.config14)
3180 h = logging._handlers['hand1']
3181 self.assertEqual(h.foo, 'bar')
3182 self.assertEqual(h.terminator, '!\n')
3183 logging.warning('Exclamation')
3184 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3185
Xtreak087570a2018-07-02 14:27:46 +05303186 def test_config15_ok(self):
3187
3188 def cleanup(h1, fn):
3189 h1.close()
3190 os.remove(fn)
3191
3192 with self.check_no_resource_warning():
3193 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3194 os.close(fd)
3195
3196 config = {
3197 "version": 1,
3198 "handlers": {
3199 "file": {
3200 "class": "logging.FileHandler",
3201 "filename": fn
3202 }
3203 },
3204 "root": {
3205 "handlers": ["file"]
3206 }
3207 }
3208
3209 self.apply_config(config)
3210 self.apply_config(config)
3211
3212 handler = logging.root.handlers[0]
3213 self.addCleanup(cleanup, handler, fn)
3214
Vinay Sajip4ded5512012-10-02 15:56:16 +01003215 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003216 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003217 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003218 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003219 t.start()
3220 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003221 # Now get the port allocated
3222 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003223 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003224 try:
3225 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3226 sock.settimeout(2.0)
3227 sock.connect(('localhost', port))
3228
3229 slen = struct.pack('>L', len(text))
3230 s = slen + text
3231 sentsofar = 0
3232 left = len(s)
3233 while left > 0:
3234 sent = sock.send(s[sentsofar:])
3235 sentsofar += sent
3236 left -= sent
3237 sock.close()
3238 finally:
3239 t.ready.wait(2.0)
3240 logging.config.stopListening()
Victor Stinnerbbc8b792019-12-10 20:41:23 +01003241 support.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003242
3243 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003244 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003245 self.setup_via_listener(json.dumps(self.config10))
3246 logger = logging.getLogger("compiler.parser")
3247 logger.warning(self.next_message())
3248 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003249 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003250 logger.warning(self.next_message())
3251 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003252 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003253 logger.warning(self.next_message())
3254 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003255 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003256 logger.error(self.next_message())
3257 self.assert_log_lines([
3258 ('WARNING', '1'),
3259 ('ERROR', '4'),
3260 ], stream=output)
3261
3262 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003263 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003264 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3265 logger = logging.getLogger("compiler.parser")
3266 # Both will output a message
3267 logger.info(self.next_message())
3268 logger.error(self.next_message())
3269 self.assert_log_lines([
3270 ('INFO', '1'),
3271 ('ERROR', '2'),
3272 ], stream=output)
3273 # Original logger output is empty.
3274 self.assert_log_lines([])
3275
Vinay Sajip4ded5512012-10-02 15:56:16 +01003276 def test_listen_verify(self):
3277
3278 def verify_fail(stuff):
3279 return None
3280
3281 def verify_reverse(stuff):
3282 return stuff[::-1]
3283
3284 logger = logging.getLogger("compiler.parser")
3285 to_send = textwrap.dedent(ConfigFileTest.config1)
3286 # First, specify a verification function that will fail.
3287 # We expect to see no output, since our configuration
3288 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003289 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003290 self.setup_via_listener(to_send, verify_fail)
3291 # Both will output a message
3292 logger.info(self.next_message())
3293 logger.error(self.next_message())
3294 self.assert_log_lines([], stream=output)
3295 # Original logger output has the stuff we logged.
3296 self.assert_log_lines([
3297 ('INFO', '1'),
3298 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003299 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003300
3301 # Now, perform no verification. Our configuration
3302 # should take effect.
3303
Vinay Sajip1feedb42014-05-31 08:19:12 +01003304 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003305 self.setup_via_listener(to_send) # no verify callable specified
3306 logger = logging.getLogger("compiler.parser")
3307 # Both will output a message
3308 logger.info(self.next_message())
3309 logger.error(self.next_message())
3310 self.assert_log_lines([
3311 ('INFO', '3'),
3312 ('ERROR', '4'),
3313 ], stream=output)
3314 # Original logger output still has the stuff we logged before.
3315 self.assert_log_lines([
3316 ('INFO', '1'),
3317 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003318 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003319
3320 # Now, perform verification which transforms the bytes.
3321
Vinay Sajip1feedb42014-05-31 08:19:12 +01003322 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003323 self.setup_via_listener(to_send[::-1], verify_reverse)
3324 logger = logging.getLogger("compiler.parser")
3325 # Both will output a message
3326 logger.info(self.next_message())
3327 logger.error(self.next_message())
3328 self.assert_log_lines([
3329 ('INFO', '5'),
3330 ('ERROR', '6'),
3331 ], stream=output)
3332 # Original logger output still has the stuff we logged before.
3333 self.assert_log_lines([
3334 ('INFO', '1'),
3335 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003336 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003337
Vinay Sajip3f885b52013-03-22 15:19:54 +00003338 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003339 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3340
3341 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003342 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003343 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3344
3345 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003346 handler = logging.getLogger('mymodule').handlers[0]
3347 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003348 self.assertIsInstance(handler.formatter._style,
3349 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003350
BNMetrics18fb1fb2018-10-15 19:41:36 +01003351 def test_custom_formatter_class_with_validate(self):
3352 self.apply_config(self.custom_formatter_class_validate)
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(self):
3357 self.apply_config(self.custom_formatter_class_validate2)
3358 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3359 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3360
3361 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3362 config = self.custom_formatter_class_validate.copy()
3363 config['formatters']['form1']['style'] = "$"
3364
3365 # Exception should not be raise as we have configured 'validate' to False
3366 self.apply_config(config)
3367 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3368 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3369
3370 def test_custom_formatter_class_with_validate3(self):
3371 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3372
3373 def test_custom_formatter_function_with_validate(self):
3374 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3375
Vinay Sajip373baef2011-04-26 20:05:24 +01003376 def test_baseconfig(self):
3377 d = {
3378 'atuple': (1, 2, 3),
3379 'alist': ['a', 'b', 'c'],
3380 'adict': {'d': 'e', 'f': 3 },
3381 'nest1': ('g', ('h', 'i'), 'j'),
3382 'nest2': ['k', ['l', 'm'], 'n'],
3383 'nest3': ['o', 'cfg://alist', 'p'],
3384 }
3385 bc = logging.config.BaseConfigurator(d)
3386 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3387 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3388 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3389 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3390 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3391 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3392 v = bc.convert('cfg://nest3')
3393 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3394 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3395 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3396 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003397
Vinay Sajip46abfc12020-01-01 19:32:11 +00003398 def test_namedtuple(self):
3399 # see bpo-39142
3400 from collections import namedtuple
3401
3402 class MyHandler(logging.StreamHandler):
3403 def __init__(self, resource, *args, **kwargs):
3404 super().__init__(*args, **kwargs)
3405 self.resource: namedtuple = resource
3406
3407 def emit(self, record):
3408 record.msg += f' {self.resource.type}'
3409 return super().emit(record)
3410
3411 Resource = namedtuple('Resource', ['type', 'labels'])
3412 resource = Resource(type='my_type', labels=['a'])
3413
3414 config = {
3415 'version': 1,
3416 'handlers': {
3417 'myhandler': {
3418 '()': MyHandler,
3419 'resource': resource
3420 }
3421 },
3422 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3423 }
3424 with support.captured_stderr() as stderr:
3425 self.apply_config(config)
3426 logging.info('some log')
3427 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3428
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003429class ManagerTest(BaseTest):
3430 def test_manager_loggerclass(self):
3431 logged = []
3432
3433 class MyLogger(logging.Logger):
3434 def _log(self, level, msg, args, exc_info=None, extra=None):
3435 logged.append(msg)
3436
3437 man = logging.Manager(None)
3438 self.assertRaises(TypeError, man.setLoggerClass, int)
3439 man.setLoggerClass(MyLogger)
3440 logger = man.getLogger('test')
3441 logger.warning('should appear in logged')
3442 logging.warning('should not appear in logged')
3443
3444 self.assertEqual(logged, ['should appear in logged'])
3445
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003446 def test_set_log_record_factory(self):
3447 man = logging.Manager(None)
3448 expected = object()
3449 man.setLogRecordFactory(expected)
3450 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003451
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003452class ChildLoggerTest(BaseTest):
3453 def test_child_loggers(self):
3454 r = logging.getLogger()
3455 l1 = logging.getLogger('abc')
3456 l2 = logging.getLogger('def.ghi')
3457 c1 = r.getChild('xyz')
3458 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003459 self.assertIs(c1, logging.getLogger('xyz'))
3460 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003461 c1 = l1.getChild('def')
3462 c2 = c1.getChild('ghi')
3463 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003464 self.assertIs(c1, logging.getLogger('abc.def'))
3465 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3466 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003467
3468
Vinay Sajip6fac8172010-10-19 20:44:14 +00003469class DerivedLogRecord(logging.LogRecord):
3470 pass
3471
Vinay Sajip61561522010-12-03 11:50:38 +00003472class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003473
3474 def setUp(self):
3475 class CheckingFilter(logging.Filter):
3476 def __init__(self, cls):
3477 self.cls = cls
3478
3479 def filter(self, record):
3480 t = type(record)
3481 if t is not self.cls:
3482 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3483 self.cls)
3484 raise TypeError(msg)
3485 return True
3486
3487 BaseTest.setUp(self)
3488 self.filter = CheckingFilter(DerivedLogRecord)
3489 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003490 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003491
3492 def tearDown(self):
3493 self.root_logger.removeFilter(self.filter)
3494 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003495 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003496
3497 def test_logrecord_class(self):
3498 self.assertRaises(TypeError, self.root_logger.warning,
3499 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003500 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003501 self.root_logger.error(self.next_message())
3502 self.assert_log_lines([
3503 ('root', 'ERROR', '2'),
3504 ])
3505
3506
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003507class QueueHandlerTest(BaseTest):
3508 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003509 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003510
3511 def setUp(self):
3512 BaseTest.setUp(self)
3513 self.queue = queue.Queue(-1)
3514 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003515 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003516 self.que_logger = logging.getLogger('que')
3517 self.que_logger.propagate = False
3518 self.que_logger.setLevel(logging.WARNING)
3519 self.que_logger.addHandler(self.que_hdlr)
3520
3521 def tearDown(self):
3522 self.que_hdlr.close()
3523 BaseTest.tearDown(self)
3524
3525 def test_queue_handler(self):
3526 self.que_logger.debug(self.next_message())
3527 self.assertRaises(queue.Empty, self.queue.get_nowait)
3528 self.que_logger.info(self.next_message())
3529 self.assertRaises(queue.Empty, self.queue.get_nowait)
3530 msg = self.next_message()
3531 self.que_logger.warning(msg)
3532 data = self.queue.get_nowait()
3533 self.assertTrue(isinstance(data, logging.LogRecord))
3534 self.assertEqual(data.name, self.que_logger.name)
3535 self.assertEqual((data.msg, data.args), (msg, None))
3536
favlladfe3442017-08-01 20:12:26 +02003537 def test_formatting(self):
3538 msg = self.next_message()
3539 levelname = logging.getLevelName(logging.WARNING)
3540 log_format_str = '{name} -> {levelname}: {message}'
3541 formatted_msg = log_format_str.format(name=self.name,
3542 levelname=levelname, message=msg)
3543 formatter = logging.Formatter(self.log_format)
3544 self.que_hdlr.setFormatter(formatter)
3545 self.que_logger.warning(msg)
3546 log_record = self.queue.get_nowait()
3547 self.assertEqual(formatted_msg, log_record.msg)
3548 self.assertEqual(formatted_msg, log_record.message)
3549
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003550 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3551 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003552 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003553 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003554 listener = logging.handlers.QueueListener(self.queue, handler)
3555 listener.start()
3556 try:
3557 self.que_logger.warning(self.next_message())
3558 self.que_logger.error(self.next_message())
3559 self.que_logger.critical(self.next_message())
3560 finally:
3561 listener.stop()
3562 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3563 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3564 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003565 handler.close()
3566
3567 # Now test with respect_handler_level set
3568
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003569 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003570 handler.setLevel(logging.CRITICAL)
3571 listener = logging.handlers.QueueListener(self.queue, handler,
3572 respect_handler_level=True)
3573 listener.start()
3574 try:
3575 self.que_logger.warning(self.next_message())
3576 self.que_logger.error(self.next_message())
3577 self.que_logger.critical(self.next_message())
3578 finally:
3579 listener.stop()
3580 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3581 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3582 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003583 handler.close()
3584
3585 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3586 'logging.handlers.QueueListener required for this test')
3587 def test_queue_listener_with_StreamHandler(self):
3588 # Test that traceback only appends once (bpo-34334).
3589 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3590 listener.start()
3591 try:
3592 1 / 0
3593 except ZeroDivisionError as e:
3594 exc = e
3595 self.que_logger.exception(self.next_message(), exc_info=exc)
3596 listener.stop()
3597 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003598
Xtreak2dad9602019-04-07 13:21:27 +05303599 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3600 'logging.handlers.QueueListener required for this test')
3601 def test_queue_listener_with_multiple_handlers(self):
3602 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3603 self.que_hdlr.setFormatter(self.root_formatter)
3604 self.que_logger.addHandler(self.root_hdlr)
3605
3606 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3607 listener.start()
3608 self.que_logger.error("error")
3609 listener.stop()
3610 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3611
Vinay Sajipd61910c2016-09-08 01:13:39 +01003612if hasattr(logging.handlers, 'QueueListener'):
3613 import multiprocessing
3614 from unittest.mock import patch
3615
3616 class QueueListenerTest(BaseTest):
3617 """
3618 Tests based on patch submitted for issue #27930. Ensure that
3619 QueueListener handles all log messages.
3620 """
3621
3622 repeat = 20
3623
3624 @staticmethod
3625 def setup_and_log(log_queue, ident):
3626 """
3627 Creates a logger with a QueueHandler that logs to a queue read by a
3628 QueueListener. Starts the listener, logs five messages, and stops
3629 the listener.
3630 """
3631 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3632 logger.setLevel(logging.DEBUG)
3633 handler = logging.handlers.QueueHandler(log_queue)
3634 logger.addHandler(handler)
3635 listener = logging.handlers.QueueListener(log_queue)
3636 listener.start()
3637
3638 logger.info('one')
3639 logger.info('two')
3640 logger.info('three')
3641 logger.info('four')
3642 logger.info('five')
3643
3644 listener.stop()
3645 logger.removeHandler(handler)
3646 handler.close()
3647
3648 @patch.object(logging.handlers.QueueListener, 'handle')
3649 def test_handle_called_with_queue_queue(self, mock_handle):
3650 for i in range(self.repeat):
3651 log_queue = queue.Queue()
3652 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3653 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3654 'correct number of handled log messages')
3655
3656 @patch.object(logging.handlers.QueueListener, 'handle')
3657 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerb1e73612020-06-18 17:19:59 +02003658 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003659 # when the mp.synchronize module cannot be imported.
Victor Stinnerb1e73612020-06-18 17:19:59 +02003660 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003661 for i in range(self.repeat):
3662 log_queue = multiprocessing.Queue()
3663 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003664 log_queue.close()
3665 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003666 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3667 'correct number of handled log messages')
3668
3669 @staticmethod
3670 def get_all_from_queue(log_queue):
3671 try:
3672 while True:
3673 yield log_queue.get_nowait()
3674 except queue.Empty:
3675 return []
3676
3677 def test_no_messages_in_queue_after_stop(self):
3678 """
3679 Five messages are logged then the QueueListener is stopped. This
3680 test then gets everything off the queue. Failure of this test
3681 indicates that messages were not registered on the queue until
3682 _after_ the QueueListener stopped.
3683 """
Victor Stinnerb1e73612020-06-18 17:19:59 +02003684 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003685 # when the mp.synchronize module cannot be imported.
Victor Stinnerb1e73612020-06-18 17:19:59 +02003686 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003687 for i in range(self.repeat):
3688 queue = multiprocessing.Queue()
3689 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3690 # time.sleep(1)
3691 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003692 queue.close()
3693 queue.join_thread()
3694
Vinay Sajipd61910c2016-09-08 01:13:39 +01003695 expected = [[], [logging.handlers.QueueListener._sentinel]]
3696 self.assertIn(items, expected,
3697 'Found unexpected messages in queue: %s' % (
3698 [m.msg if isinstance(m, logging.LogRecord)
3699 else m for m in items]))
3700
Bar Harel6b282e12019-06-01 12:19:09 +03003701 def test_calls_task_done_after_stop(self):
3702 # Issue 36813: Make sure queue.join does not deadlock.
3703 log_queue = queue.Queue()
3704 listener = logging.handlers.QueueListener(log_queue)
3705 listener.start()
3706 listener.stop()
3707 with self.assertRaises(ValueError):
3708 # Make sure all tasks are done and .join won't block.
3709 log_queue.task_done()
3710
Vinay Sajipe723e962011-04-15 22:27:17 +01003711
Vinay Sajip37eb3382011-04-26 20:26:41 +01003712ZERO = datetime.timedelta(0)
3713
3714class UTC(datetime.tzinfo):
3715 def utcoffset(self, dt):
3716 return ZERO
3717
3718 dst = utcoffset
3719
3720 def tzname(self, dt):
3721 return 'UTC'
3722
3723utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003724
Vinay Sajipa39c5712010-10-25 13:57:39 +00003725class FormatterTest(unittest.TestCase):
3726 def setUp(self):
3727 self.common = {
3728 'name': 'formatter.test',
3729 'level': logging.DEBUG,
3730 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3731 'lineno': 42,
3732 'exc_info': None,
3733 'func': None,
3734 'msg': 'Message with %d %s',
3735 'args': (2, 'placeholders'),
3736 }
3737 self.variants = {
3738 }
3739
3740 def get_record(self, name=None):
3741 result = dict(self.common)
3742 if name is not None:
3743 result.update(self.variants[name])
3744 return logging.makeLogRecord(result)
3745
BNMetrics18fb1fb2018-10-15 19:41:36 +01003746 def assert_error_message(self, exception, message, *args, **kwargs):
3747 try:
3748 self.assertRaises(exception, *args, **kwargs)
3749 except exception as e:
3750 self.assertEqual(message, e.message)
3751
Vinay Sajipa39c5712010-10-25 13:57:39 +00003752 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003753 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003754 r = self.get_record()
3755 f = logging.Formatter('${%(message)s}')
3756 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3757 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003758 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003759 self.assertFalse(f.usesTime())
3760 f = logging.Formatter('%(asctime)s')
3761 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003762 f = logging.Formatter('%(asctime)-15s')
3763 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003764 f = logging.Formatter('%(asctime)#15s')
3765 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003766
3767 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003768 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003769 r = self.get_record()
3770 f = logging.Formatter('$%{message}%$', style='{')
3771 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3772 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003773 self.assertRaises(ValueError, f.format, r)
3774 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003775 self.assertFalse(f.usesTime())
3776 f = logging.Formatter('{asctime}', style='{')
3777 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003778 f = logging.Formatter('{asctime!s:15}', style='{')
3779 self.assertTrue(f.usesTime())
3780 f = logging.Formatter('{asctime:15}', style='{')
3781 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003782
3783 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003784 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003785 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003786 f = logging.Formatter('${message}', style='$')
3787 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003788 f = logging.Formatter('$message', style='$')
3789 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3790 f = logging.Formatter('$$%${message}%$$', style='$')
3791 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3792 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003793 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003794 self.assertFalse(f.usesTime())
3795 f = logging.Formatter('${asctime}', style='$')
3796 self.assertTrue(f.usesTime())
3797 f = logging.Formatter('$asctime', style='$')
3798 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003799 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003800 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003801 f = logging.Formatter('${asctime}--', style='$')
3802 self.assertTrue(f.usesTime())
3803
3804 def test_format_validate(self):
3805 # Check correct formatting
3806 # Percentage style
3807 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3808 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3809 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3810 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3811 f = logging.Formatter("%(process)#+027.23X")
3812 self.assertEqual(f._fmt, "%(process)#+027.23X")
3813 f = logging.Formatter("%(foo)#.*g")
3814 self.assertEqual(f._fmt, "%(foo)#.*g")
3815
3816 # StrFormat Style
3817 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3818 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3819 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3820 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3821 f = logging.Formatter("{customfield!s:#<30}", style="{")
3822 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3823 f = logging.Formatter("{message!r}", style="{")
3824 self.assertEqual(f._fmt, "{message!r}")
3825 f = logging.Formatter("{message!s}", style="{")
3826 self.assertEqual(f._fmt, "{message!s}")
3827 f = logging.Formatter("{message!a}", style="{")
3828 self.assertEqual(f._fmt, "{message!a}")
3829 f = logging.Formatter("{process!r:4.2}", style="{")
3830 self.assertEqual(f._fmt, "{process!r:4.2}")
3831 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3832 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3833 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3834 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3835 f = logging.Formatter("{foo:12.{p}}", style="{")
3836 self.assertEqual(f._fmt, "{foo:12.{p}}")
3837 f = logging.Formatter("{foo:{w}.6}", style="{")
3838 self.assertEqual(f._fmt, "{foo:{w}.6}")
3839 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3840 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3841 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3842 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3843 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3844 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3845
3846 # Dollar style
3847 f = logging.Formatter("${asctime} - $message", style="$")
3848 self.assertEqual(f._fmt, "${asctime} - $message")
3849 f = logging.Formatter("$bar $$", style="$")
3850 self.assertEqual(f._fmt, "$bar $$")
3851 f = logging.Formatter("$bar $$$$", style="$")
3852 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3853
3854 # Testing when ValueError being raised from incorrect format
3855 # Percentage Style
3856 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3857 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3858 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3859 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3860 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3861 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3862 self.assertRaises(ValueError, logging.Formatter, '${message}')
3863 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3864 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3865
3866 # StrFormat Style
3867 # Testing failure for '-' in field name
3868 self.assert_error_message(
3869 ValueError,
3870 "invalid field name/expression: 'name-thing'",
3871 logging.Formatter, "{name-thing}", style="{"
3872 )
3873 # Testing failure for style mismatch
3874 self.assert_error_message(
3875 ValueError,
3876 "invalid format: no fields",
3877 logging.Formatter, '%(asctime)s', style='{'
3878 )
3879 # Testing failure for invalid conversion
3880 self.assert_error_message(
3881 ValueError,
3882 "invalid conversion: 'Z'"
3883 )
3884 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3885 self.assert_error_message(
3886 ValueError,
3887 "invalid format: expected ':' after conversion specifier",
3888 logging.Formatter, '{asctime!aa:15}', style='{'
3889 )
3890 # Testing failure for invalid spec
3891 self.assert_error_message(
3892 ValueError,
3893 "bad specifier: '.2ff'",
3894 logging.Formatter, '{process:.2ff}', style='{'
3895 )
3896 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3897 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3898 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3899 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3900 # Testing failure for mismatch braces
3901 self.assert_error_message(
3902 ValueError,
3903 "invalid format: unmatched '{' in format spec",
3904 logging.Formatter, '{process', style='{'
3905 )
3906 self.assert_error_message(
3907 ValueError,
3908 "invalid format: unmatched '{' in format spec",
3909 logging.Formatter, 'process}', style='{'
3910 )
3911 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3912 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3913 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3914 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3915 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3916 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3917 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3918 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3919 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3920
3921 # Dollar style
3922 # Testing failure for mismatch bare $
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, 'bar $', style='$'
3932 )
3933 self.assert_error_message(
3934 ValueError,
3935 "invalid format: bare \'$\' not allowed",
3936 logging.Formatter, 'foo $.', style='$'
3937 )
3938 # Testing failure for mismatch style
3939 self.assert_error_message(
3940 ValueError,
3941 "invalid format: no fields",
3942 logging.Formatter, '{asctime}', style='$'
3943 )
3944 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3945
3946 # Testing failure for incorrect fields
3947 self.assert_error_message(
3948 ValueError,
3949 "invalid format: no fields",
3950 logging.Formatter, 'foo', style='$'
3951 )
3952 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003953
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003954 def test_invalid_style(self):
3955 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3956
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003957 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003958 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003959 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3960 # We use None to indicate we want the local timezone
3961 # We're essentially converting a UTC time to local time
3962 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003963 r.msecs = 123
3964 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003965 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003966 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3967 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003968 f.format(r)
3969 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
3970
Mariusz Felisiak06a35542020-04-17 18:02:47 +02003971 def test_default_msec_format_none(self):
3972 class NoMsecFormatter(logging.Formatter):
3973 default_msec_format = None
3974 default_time_format = '%d/%m/%Y %H:%M:%S'
3975
3976 r = self.get_record()
3977 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
3978 r.created = time.mktime(dt.astimezone(None).timetuple())
3979 f = NoMsecFormatter()
3980 f.converter = time.gmtime
3981 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
3982
3983
Vinay Sajip985ef872011-04-26 19:34:04 +01003984class TestBufferingFormatter(logging.BufferingFormatter):
3985 def formatHeader(self, records):
3986 return '[(%d)' % len(records)
3987
3988 def formatFooter(self, records):
3989 return '(%d)]' % len(records)
3990
3991class BufferingFormatterTest(unittest.TestCase):
3992 def setUp(self):
3993 self.records = [
3994 logging.makeLogRecord({'msg': 'one'}),
3995 logging.makeLogRecord({'msg': 'two'}),
3996 ]
3997
3998 def test_default(self):
3999 f = logging.BufferingFormatter()
4000 self.assertEqual('', f.format([]))
4001 self.assertEqual('onetwo', f.format(self.records))
4002
4003 def test_custom(self):
4004 f = TestBufferingFormatter()
4005 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
4006 lf = logging.Formatter('<%(message)s>')
4007 f = TestBufferingFormatter(lf)
4008 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004009
4010class ExceptionTest(BaseTest):
4011 def test_formatting(self):
4012 r = self.root_logger
4013 h = RecordingHandler()
4014 r.addHandler(h)
4015 try:
4016 raise RuntimeError('deliberate mistake')
4017 except:
4018 logging.exception('failed', stack_info=True)
4019 r.removeHandler(h)
4020 h.close()
4021 r = h.records[0]
4022 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
4023 'call last):\n'))
4024 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4025 'deliberate mistake'))
4026 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4027 'call last):\n'))
4028 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4029 'stack_info=True)'))
4030
4031
Vinay Sajip5a27d402010-12-10 11:42:57 +00004032class LastResortTest(BaseTest):
4033 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004034 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004035 root = self.root_logger
4036 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004037 old_lastresort = logging.lastResort
4038 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004039
Vinay Sajip5a27d402010-12-10 11:42:57 +00004040 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004041 with support.captured_stderr() as stderr:
4042 root.debug('This should not appear')
4043 self.assertEqual(stderr.getvalue(), '')
4044 root.warning('Final chance!')
4045 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4046
4047 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004048 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004049 with support.captured_stderr() as stderr:
4050 root.warning('Final chance!')
4051 msg = 'No handlers could be found for logger "root"\n'
4052 self.assertEqual(stderr.getvalue(), msg)
4053
Vinay Sajip5a27d402010-12-10 11:42:57 +00004054 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004055 with support.captured_stderr() as stderr:
4056 root.warning('Final chance!')
4057 self.assertEqual(stderr.getvalue(), '')
4058
4059 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004060 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004061 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004062 with support.captured_stderr() as stderr:
4063 root.warning('Final chance!')
4064 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004065 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004066 root.addHandler(self.root_hdlr)
4067 logging.lastResort = old_lastresort
4068 logging.raiseExceptions = old_raise_exceptions
4069
4070
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004071class FakeHandler:
4072
4073 def __init__(self, identifier, called):
4074 for method in ('acquire', 'flush', 'close', 'release'):
4075 setattr(self, method, self.record_call(identifier, method, called))
4076
4077 def record_call(self, identifier, method_name, called):
4078 def inner():
4079 called.append('{} - {}'.format(identifier, method_name))
4080 return inner
4081
4082
4083class RecordingHandler(logging.NullHandler):
4084
4085 def __init__(self, *args, **kwargs):
4086 super(RecordingHandler, self).__init__(*args, **kwargs)
4087 self.records = []
4088
4089 def handle(self, record):
4090 """Keep track of all the emitted records."""
4091 self.records.append(record)
4092
4093
4094class ShutdownTest(BaseTest):
4095
Vinay Sajip5e66b162011-04-20 15:41:14 +01004096 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004097
4098 def setUp(self):
4099 super(ShutdownTest, self).setUp()
4100 self.called = []
4101
4102 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004103 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004104
4105 def raise_error(self, error):
4106 def inner():
4107 raise error()
4108 return inner
4109
4110 def test_no_failure(self):
4111 # create some fake handlers
4112 handler0 = FakeHandler(0, self.called)
4113 handler1 = FakeHandler(1, self.called)
4114 handler2 = FakeHandler(2, self.called)
4115
4116 # create live weakref to those handlers
4117 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4118
4119 logging.shutdown(handlerList=list(handlers))
4120
4121 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4122 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4123 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4124 self.assertEqual(expected, self.called)
4125
4126 def _test_with_failure_in_method(self, method, error):
4127 handler = FakeHandler(0, self.called)
4128 setattr(handler, method, self.raise_error(error))
4129 handlers = [logging.weakref.ref(handler)]
4130
4131 logging.shutdown(handlerList=list(handlers))
4132
4133 self.assertEqual('0 - release', self.called[-1])
4134
4135 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004136 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004137
4138 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004139 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004140
4141 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004142 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004143
4144 def test_with_valueerror_in_acquire(self):
4145 self._test_with_failure_in_method('acquire', ValueError)
4146
4147 def test_with_valueerror_in_flush(self):
4148 self._test_with_failure_in_method('flush', ValueError)
4149
4150 def test_with_valueerror_in_close(self):
4151 self._test_with_failure_in_method('close', ValueError)
4152
4153 def test_with_other_error_in_acquire_without_raise(self):
4154 logging.raiseExceptions = False
4155 self._test_with_failure_in_method('acquire', IndexError)
4156
4157 def test_with_other_error_in_flush_without_raise(self):
4158 logging.raiseExceptions = False
4159 self._test_with_failure_in_method('flush', IndexError)
4160
4161 def test_with_other_error_in_close_without_raise(self):
4162 logging.raiseExceptions = False
4163 self._test_with_failure_in_method('close', IndexError)
4164
4165 def test_with_other_error_in_acquire_with_raise(self):
4166 logging.raiseExceptions = True
4167 self.assertRaises(IndexError, self._test_with_failure_in_method,
4168 'acquire', IndexError)
4169
4170 def test_with_other_error_in_flush_with_raise(self):
4171 logging.raiseExceptions = True
4172 self.assertRaises(IndexError, self._test_with_failure_in_method,
4173 'flush', IndexError)
4174
4175 def test_with_other_error_in_close_with_raise(self):
4176 logging.raiseExceptions = True
4177 self.assertRaises(IndexError, self._test_with_failure_in_method,
4178 'close', IndexError)
4179
4180
4181class ModuleLevelMiscTest(BaseTest):
4182
Vinay Sajip5e66b162011-04-20 15:41:14 +01004183 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004184
4185 def test_disable(self):
4186 old_disable = logging.root.manager.disable
4187 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004188 self.assertEqual(old_disable, 0)
4189 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004190
4191 logging.disable(83)
4192 self.assertEqual(logging.root.manager.disable, 83)
4193
Vinay Sajipd489ac92016-12-31 11:40:11 +00004194 # test the default value introduced in 3.7
4195 # (Issue #28524)
4196 logging.disable()
4197 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4198
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004199 def _test_log(self, method, level=None):
4200 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004201 support.patch(self, logging, 'basicConfig',
4202 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004203
4204 recording = RecordingHandler()
4205 logging.root.addHandler(recording)
4206
4207 log_method = getattr(logging, method)
4208 if level is not None:
4209 log_method(level, "test me: %r", recording)
4210 else:
4211 log_method("test me: %r", recording)
4212
4213 self.assertEqual(len(recording.records), 1)
4214 record = recording.records[0]
4215 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4216
4217 expected_level = level if level is not None else getattr(logging, method.upper())
4218 self.assertEqual(record.levelno, expected_level)
4219
4220 # basicConfig was not called!
4221 self.assertEqual(called, [])
4222
4223 def test_log(self):
4224 self._test_log('log', logging.ERROR)
4225
4226 def test_debug(self):
4227 self._test_log('debug')
4228
4229 def test_info(self):
4230 self._test_log('info')
4231
4232 def test_warning(self):
4233 self._test_log('warning')
4234
4235 def test_error(self):
4236 self._test_log('error')
4237
4238 def test_critical(self):
4239 self._test_log('critical')
4240
4241 def test_set_logger_class(self):
4242 self.assertRaises(TypeError, logging.setLoggerClass, object)
4243
4244 class MyLogger(logging.Logger):
4245 pass
4246
4247 logging.setLoggerClass(MyLogger)
4248 self.assertEqual(logging.getLoggerClass(), MyLogger)
4249
4250 logging.setLoggerClass(logging.Logger)
4251 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4252
Vinay Sajip01500012019-06-19 11:46:53 +01004253 def test_subclass_logger_cache(self):
4254 # bpo-37258
4255 message = []
4256
4257 class MyLogger(logging.getLoggerClass()):
4258 def __init__(self, name='MyLogger', level=logging.NOTSET):
4259 super().__init__(name, level)
4260 message.append('initialized')
4261
4262 logging.setLoggerClass(MyLogger)
4263 logger = logging.getLogger('just_some_logger')
4264 self.assertEqual(message, ['initialized'])
4265 stream = io.StringIO()
4266 h = logging.StreamHandler(stream)
4267 logger.addHandler(h)
4268 try:
4269 logger.setLevel(logging.DEBUG)
4270 logger.debug("hello")
4271 self.assertEqual(stream.getvalue().strip(), "hello")
4272
4273 stream.truncate(0)
4274 stream.seek(0)
4275
4276 logger.setLevel(logging.INFO)
4277 logger.debug("hello")
4278 self.assertEqual(stream.getvalue(), "")
4279 finally:
4280 logger.removeHandler(h)
4281 h.close()
4282 logging.setLoggerClass(logging.Logger)
4283
Antoine Pitrou712cb732013-12-21 15:51:54 +01004284 def test_logging_at_shutdown(self):
4285 # Issue #20037
4286 code = """if 1:
4287 import logging
4288
4289 class A:
4290 def __del__(self):
4291 try:
4292 raise ValueError("some error")
4293 except Exception:
4294 logging.exception("exception in __del__")
4295
4296 a = A()"""
4297 rc, out, err = assert_python_ok("-c", code)
4298 err = err.decode()
4299 self.assertIn("exception in __del__", err)
4300 self.assertIn("ValueError: some error", err)
4301
Rémi Lapeyre65f64b192019-03-15 07:53:34 +01004302 def test_recursion_error(self):
4303 # Issue 36272
4304 code = """if 1:
4305 import logging
4306
4307 def rec():
4308 logging.error("foo")
4309 rec()
4310
4311 rec()"""
4312 rc, out, err = assert_python_failure("-c", code)
4313 err = err.decode()
4314 self.assertNotIn("Cannot recover from stack overflow.", err)
4315 self.assertEqual(rc, 1)
4316
Antoine Pitrou712cb732013-12-21 15:51:54 +01004317
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004318class LogRecordTest(BaseTest):
4319 def test_str_rep(self):
4320 r = logging.makeLogRecord({})
4321 s = str(r)
4322 self.assertTrue(s.startswith('<LogRecord: '))
4323 self.assertTrue(s.endswith('>'))
4324
4325 def test_dict_arg(self):
4326 h = RecordingHandler()
4327 r = logging.getLogger()
4328 r.addHandler(h)
4329 d = {'less' : 'more' }
4330 logging.warning('less is %(less)s', d)
4331 self.assertIs(h.records[0].args, d)
4332 self.assertEqual(h.records[0].message, 'less is more')
4333 r.removeHandler(h)
4334 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004335
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004336 def test_multiprocessing(self):
4337 r = logging.makeLogRecord({})
4338 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004339 try:
4340 import multiprocessing as mp
4341 r = logging.makeLogRecord({})
4342 self.assertEqual(r.processName, mp.current_process().name)
4343 except ImportError:
4344 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004345
4346 def test_optional(self):
4347 r = logging.makeLogRecord({})
4348 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004349 NOT_NONE(r.thread)
4350 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004351 NOT_NONE(r.process)
4352 NOT_NONE(r.processName)
4353 log_threads = logging.logThreads
4354 log_processes = logging.logProcesses
4355 log_multiprocessing = logging.logMultiprocessing
4356 try:
4357 logging.logThreads = False
4358 logging.logProcesses = False
4359 logging.logMultiprocessing = False
4360 r = logging.makeLogRecord({})
4361 NONE = self.assertIsNone
4362 NONE(r.thread)
4363 NONE(r.threadName)
4364 NONE(r.process)
4365 NONE(r.processName)
4366 finally:
4367 logging.logThreads = log_threads
4368 logging.logProcesses = log_processes
4369 logging.logMultiprocessing = log_multiprocessing
4370
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004371class BasicConfigTest(unittest.TestCase):
4372
Vinay Sajip95bf5042011-04-20 11:50:56 +01004373 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004374
4375 def setUp(self):
4376 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004377 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004378 self.saved_handlers = logging._handlers.copy()
4379 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004380 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004381 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004382 logging.root.handlers = []
4383
4384 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004385 for h in logging.root.handlers[:]:
4386 logging.root.removeHandler(h)
4387 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004388 super(BasicConfigTest, self).tearDown()
4389
Vinay Sajip3def7e02011-04-20 10:58:06 +01004390 def cleanup(self):
4391 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004392 logging._handlers.clear()
4393 logging._handlers.update(self.saved_handlers)
4394 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004395 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004396
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004397 def test_no_kwargs(self):
4398 logging.basicConfig()
4399
4400 # handler defaults to a StreamHandler to sys.stderr
4401 self.assertEqual(len(logging.root.handlers), 1)
4402 handler = logging.root.handlers[0]
4403 self.assertIsInstance(handler, logging.StreamHandler)
4404 self.assertEqual(handler.stream, sys.stderr)
4405
4406 formatter = handler.formatter
4407 # format defaults to logging.BASIC_FORMAT
4408 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4409 # datefmt defaults to None
4410 self.assertIsNone(formatter.datefmt)
4411 # style defaults to %
4412 self.assertIsInstance(formatter._style, logging.PercentStyle)
4413
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004414 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004415 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004416
Vinay Sajip1fd12022014-01-13 21:59:56 +00004417 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004418 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004419 logging.basicConfig(stream=sys.stdout, style="{")
4420 logging.error("Log an error")
4421 sys.stdout.seek(0)
4422 self.assertEqual(output.getvalue().strip(),
4423 "ERROR:root:Log an error")
4424
4425 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004426 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004427 logging.basicConfig(stream=sys.stdout, style="$")
4428 logging.error("Log an error")
4429 sys.stdout.seek(0)
4430 self.assertEqual(output.getvalue().strip(),
4431 "ERROR:root:Log an error")
4432
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004433 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004434
4435 def cleanup(h1, h2, fn):
4436 h1.close()
4437 h2.close()
4438 os.remove(fn)
4439
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004440 logging.basicConfig(filename='test.log')
4441
4442 self.assertEqual(len(logging.root.handlers), 1)
4443 handler = logging.root.handlers[0]
4444 self.assertIsInstance(handler, logging.FileHandler)
4445
4446 expected = logging.FileHandler('test.log', 'a')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004447 self.assertEqual(handler.stream.mode, expected.stream.mode)
4448 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004449 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004450
4451 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004452
4453 def cleanup(h1, h2, fn):
4454 h1.close()
4455 h2.close()
4456 os.remove(fn)
4457
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004458 logging.basicConfig(filename='test.log', filemode='wb')
4459
4460 handler = logging.root.handlers[0]
4461 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004462 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004463 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004464
4465 def test_stream(self):
4466 stream = io.StringIO()
4467 self.addCleanup(stream.close)
4468 logging.basicConfig(stream=stream)
4469
4470 self.assertEqual(len(logging.root.handlers), 1)
4471 handler = logging.root.handlers[0]
4472 self.assertIsInstance(handler, logging.StreamHandler)
4473 self.assertEqual(handler.stream, stream)
4474
4475 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004476 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004477
4478 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004479 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004480
4481 def test_datefmt(self):
4482 logging.basicConfig(datefmt='bar')
4483
4484 formatter = logging.root.handlers[0].formatter
4485 self.assertEqual(formatter.datefmt, 'bar')
4486
4487 def test_style(self):
4488 logging.basicConfig(style='$')
4489
4490 formatter = logging.root.handlers[0].formatter
4491 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4492
4493 def test_level(self):
4494 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004495 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004496
4497 logging.basicConfig(level=57)
4498 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004499 # Test that second call has no effect
4500 logging.basicConfig(level=58)
4501 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004502
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004503 def test_incompatible(self):
4504 assertRaises = self.assertRaises
4505 handlers = [logging.StreamHandler()]
4506 stream = sys.stderr
4507 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004508 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004509 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004510 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004511 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004512 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004513 # Issue 23207: test for invalid kwargs
4514 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4515 # Should pop both filename and filemode even if filename is None
4516 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004517
4518 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004519 handlers = [
4520 logging.StreamHandler(),
4521 logging.StreamHandler(sys.stdout),
4522 logging.StreamHandler(),
4523 ]
4524 f = logging.Formatter()
4525 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004526 logging.basicConfig(handlers=handlers)
4527 self.assertIs(handlers[0], logging.root.handlers[0])
4528 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004529 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004530 self.assertIsNotNone(handlers[0].formatter)
4531 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004532 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004533 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4534
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004535 def test_force(self):
4536 old_string_io = io.StringIO()
4537 new_string_io = io.StringIO()
4538 old_handlers = [logging.StreamHandler(old_string_io)]
4539 new_handlers = [logging.StreamHandler(new_string_io)]
4540 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4541 logging.warning('warn')
4542 logging.info('info')
4543 logging.debug('debug')
4544 self.assertEqual(len(logging.root.handlers), 1)
4545 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4546 force=True)
4547 logging.warning('warn')
4548 logging.info('info')
4549 logging.debug('debug')
4550 self.assertEqual(len(logging.root.handlers), 1)
4551 self.assertEqual(old_string_io.getvalue().strip(),
4552 'WARNING:root:warn')
4553 self.assertEqual(new_string_io.getvalue().strip(),
4554 'WARNING:root:warn\nINFO:root:info')
4555
Vinay Sajipca7b5042019-06-17 17:40:52 +01004556 def test_encoding(self):
4557 try:
4558 encoding = 'utf-8'
4559 logging.basicConfig(filename='test.log', encoding=encoding,
4560 errors='strict',
4561 format='%(message)s', level=logging.DEBUG)
4562
4563 self.assertEqual(len(logging.root.handlers), 1)
4564 handler = logging.root.handlers[0]
4565 self.assertIsInstance(handler, logging.FileHandler)
4566 self.assertEqual(handler.encoding, encoding)
4567 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4568 finally:
4569 handler.close()
4570 with open('test.log', encoding='utf-8') as f:
4571 data = f.read().strip()
4572 os.remove('test.log')
4573 self.assertEqual(data,
4574 'The Øresund Bridge joins Copenhagen to Malmö')
4575
4576 def test_encoding_errors(self):
4577 try:
4578 encoding = 'ascii'
4579 logging.basicConfig(filename='test.log', encoding=encoding,
4580 errors='ignore',
4581 format='%(message)s', level=logging.DEBUG)
4582
4583 self.assertEqual(len(logging.root.handlers), 1)
4584 handler = logging.root.handlers[0]
4585 self.assertIsInstance(handler, logging.FileHandler)
4586 self.assertEqual(handler.encoding, encoding)
4587 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4588 finally:
4589 handler.close()
4590 with open('test.log', encoding='utf-8') as f:
4591 data = f.read().strip()
4592 os.remove('test.log')
4593 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4594
4595 def test_encoding_errors_default(self):
4596 try:
4597 encoding = 'ascii'
4598 logging.basicConfig(filename='test.log', encoding=encoding,
4599 format='%(message)s', level=logging.DEBUG)
4600
4601 self.assertEqual(len(logging.root.handlers), 1)
4602 handler = logging.root.handlers[0]
4603 self.assertIsInstance(handler, logging.FileHandler)
4604 self.assertEqual(handler.encoding, encoding)
4605 self.assertEqual(handler.errors, 'backslashreplace')
4606 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4607 finally:
4608 handler.close()
4609 with open('test.log', encoding='utf-8') as f:
4610 data = f.read().strip()
4611 os.remove('test.log')
4612 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4613 r'Bridge joins Copenhagen to Malm\xf6')
4614
4615 def test_encoding_errors_none(self):
4616 # Specifying None should behave as 'strict'
4617 try:
4618 encoding = 'ascii'
4619 logging.basicConfig(filename='test.log', encoding=encoding,
4620 errors=None,
4621 format='%(message)s', level=logging.DEBUG)
4622
4623 self.assertEqual(len(logging.root.handlers), 1)
4624 handler = logging.root.handlers[0]
4625 self.assertIsInstance(handler, logging.FileHandler)
4626 self.assertEqual(handler.encoding, encoding)
4627 self.assertIsNone(handler.errors)
4628
4629 message = []
4630
4631 def dummy_handle_error(record):
4632 _, v, _ = sys.exc_info()
4633 message.append(str(v))
4634
4635 handler.handleError = dummy_handle_error
4636 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4637 self.assertTrue(message)
4638 self.assertIn("'ascii' codec can't encode "
4639 "character '\\xd8' in position 4:", message[0])
4640 finally:
4641 handler.close()
4642 with open('test.log', encoding='utf-8') as f:
4643 data = f.read().strip()
4644 os.remove('test.log')
4645 # didn't write anything due to the encoding error
4646 self.assertEqual(data, r'')
4647
4648
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004649 def _test_log(self, method, level=None):
4650 # logging.root has no handlers so basicConfig should be called
4651 called = []
4652
4653 old_basic_config = logging.basicConfig
4654 def my_basic_config(*a, **kw):
4655 old_basic_config()
4656 old_level = logging.root.level
4657 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004658 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004659 called.append((a, kw))
4660
Vinay Sajip1feedb42014-05-31 08:19:12 +01004661 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004662
4663 log_method = getattr(logging, method)
4664 if level is not None:
4665 log_method(level, "test me")
4666 else:
4667 log_method("test me")
4668
4669 # basicConfig was called with no arguments
4670 self.assertEqual(called, [((), {})])
4671
4672 def test_log(self):
4673 self._test_log('log', logging.WARNING)
4674
4675 def test_debug(self):
4676 self._test_log('debug')
4677
4678 def test_info(self):
4679 self._test_log('info')
4680
4681 def test_warning(self):
4682 self._test_log('warning')
4683
4684 def test_error(self):
4685 self._test_log('error')
4686
4687 def test_critical(self):
4688 self._test_log('critical')
4689
4690
4691class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004692 def setUp(self):
4693 super(LoggerAdapterTest, self).setUp()
4694 old_handler_list = logging._handlerList[:]
4695
4696 self.recording = RecordingHandler()
4697 self.logger = logging.root
4698 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004699 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004700 self.addCleanup(self.recording.close)
4701
4702 def cleanup():
4703 logging._handlerList[:] = old_handler_list
4704
4705 self.addCleanup(cleanup)
4706 self.addCleanup(logging.shutdown)
4707 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4708
4709 def test_exception(self):
4710 msg = 'testing exception: %r'
4711 exc = None
4712 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004713 1 / 0
4714 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004715 exc = e
4716 self.adapter.exception(msg, self.recording)
4717
4718 self.assertEqual(len(self.recording.records), 1)
4719 record = self.recording.records[0]
4720 self.assertEqual(record.levelno, logging.ERROR)
4721 self.assertEqual(record.msg, msg)
4722 self.assertEqual(record.args, (self.recording,))
4723 self.assertEqual(record.exc_info,
4724 (exc.__class__, exc, exc.__traceback__))
4725
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004726 def test_exception_excinfo(self):
4727 try:
4728 1 / 0
4729 except ZeroDivisionError as e:
4730 exc = e
4731
4732 self.adapter.exception('exc_info test', exc_info=exc)
4733
4734 self.assertEqual(len(self.recording.records), 1)
4735 record = self.recording.records[0]
4736 self.assertEqual(record.exc_info,
4737 (exc.__class__, exc, exc.__traceback__))
4738
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004739 def test_critical(self):
4740 msg = 'critical test! %r'
4741 self.adapter.critical(msg, self.recording)
4742
4743 self.assertEqual(len(self.recording.records), 1)
4744 record = self.recording.records[0]
4745 self.assertEqual(record.levelno, logging.CRITICAL)
4746 self.assertEqual(record.msg, msg)
4747 self.assertEqual(record.args, (self.recording,))
4748
4749 def test_is_enabled_for(self):
4750 old_disable = self.adapter.logger.manager.disable
4751 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004752 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4753 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004754 self.assertFalse(self.adapter.isEnabledFor(32))
4755
4756 def test_has_handlers(self):
4757 self.assertTrue(self.adapter.hasHandlers())
4758
4759 for handler in self.logger.handlers:
4760 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004761
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004762 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004763 self.assertFalse(self.adapter.hasHandlers())
4764
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004765 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004766 class Adapter(logging.LoggerAdapter):
4767 prefix = 'Adapter'
4768
4769 def process(self, msg, kwargs):
4770 return f"{self.prefix} {msg}", kwargs
4771
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004772 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004773 adapter = Adapter(logger=self.logger, extra=None)
4774 adapter_adapter = Adapter(logger=adapter, extra=None)
4775 adapter_adapter.prefix = 'AdapterAdapter'
4776 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004777 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004778 self.assertEqual(len(self.recording.records), 1)
4779 record = self.recording.records[0]
4780 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004781 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004782 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004783 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004784 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004785 self.assertIs(self.logger.manager, orig_manager)
4786 temp_manager = object()
4787 try:
4788 adapter_adapter.manager = temp_manager
4789 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004790 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004791 self.assertIs(self.logger.manager, temp_manager)
4792 finally:
4793 adapter_adapter.manager = orig_manager
4794 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004795 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004796 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004797
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004798
4799class LoggerTest(BaseTest):
4800
4801 def setUp(self):
4802 super(LoggerTest, self).setUp()
4803 self.recording = RecordingHandler()
4804 self.logger = logging.Logger(name='blah')
4805 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004806 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004807 self.addCleanup(self.recording.close)
4808 self.addCleanup(logging.shutdown)
4809
4810 def test_set_invalid_level(self):
4811 self.assertRaises(TypeError, self.logger.setLevel, object())
4812
4813 def test_exception(self):
4814 msg = 'testing exception: %r'
4815 exc = None
4816 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004817 1 / 0
4818 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004819 exc = e
4820 self.logger.exception(msg, self.recording)
4821
4822 self.assertEqual(len(self.recording.records), 1)
4823 record = self.recording.records[0]
4824 self.assertEqual(record.levelno, logging.ERROR)
4825 self.assertEqual(record.msg, msg)
4826 self.assertEqual(record.args, (self.recording,))
4827 self.assertEqual(record.exc_info,
4828 (exc.__class__, exc, exc.__traceback__))
4829
4830 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004831 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004832 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004833
4834 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004835 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004836 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004837
4838 def test_find_caller_with_stack_info(self):
4839 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004840 support.patch(self, logging.traceback, 'print_stack',
4841 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004842
4843 self.logger.findCaller(stack_info=True)
4844
4845 self.assertEqual(len(called), 1)
4846 self.assertEqual('Stack (most recent call last):\n', called[0])
4847
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004848 def test_find_caller_with_stacklevel(self):
4849 the_level = 1
4850
4851 def innermost():
4852 self.logger.warning('test', stacklevel=the_level)
4853
4854 def inner():
4855 innermost()
4856
4857 def outer():
4858 inner()
4859
4860 records = self.recording.records
4861 outer()
4862 self.assertEqual(records[-1].funcName, 'innermost')
4863 lineno = records[-1].lineno
4864 the_level += 1
4865 outer()
4866 self.assertEqual(records[-1].funcName, 'inner')
4867 self.assertGreater(records[-1].lineno, lineno)
4868 lineno = records[-1].lineno
4869 the_level += 1
4870 outer()
4871 self.assertEqual(records[-1].funcName, 'outer')
4872 self.assertGreater(records[-1].lineno, lineno)
4873 lineno = records[-1].lineno
4874 the_level += 1
4875 outer()
4876 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
4877 self.assertGreater(records[-1].lineno, lineno)
4878
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004879 def test_make_record_with_extra_overwrite(self):
4880 name = 'my record'
4881 level = 13
4882 fn = lno = msg = args = exc_info = func = sinfo = None
4883 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
4884 exc_info, func, sinfo)
4885
4886 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
4887 extra = {key: 'some value'}
4888 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
4889 fn, lno, msg, args, exc_info,
4890 extra=extra, sinfo=sinfo)
4891
4892 def test_make_record_with_extra_no_overwrite(self):
4893 name = 'my record'
4894 level = 13
4895 fn = lno = msg = args = exc_info = func = sinfo = None
4896 extra = {'valid_key': 'some value'}
4897 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
4898 exc_info, extra=extra, sinfo=sinfo)
4899 self.assertIn('valid_key', result.__dict__)
4900
4901 def test_has_handlers(self):
4902 self.assertTrue(self.logger.hasHandlers())
4903
4904 for handler in self.logger.handlers:
4905 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004906 self.assertFalse(self.logger.hasHandlers())
4907
4908 def test_has_handlers_no_propagate(self):
4909 child_logger = logging.getLogger('blah.child')
4910 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004911 self.assertFalse(child_logger.hasHandlers())
4912
4913 def test_is_enabled_for(self):
4914 old_disable = self.logger.manager.disable
4915 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004916 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004917 self.assertFalse(self.logger.isEnabledFor(22))
4918
Timo Furrer6e3ca642018-06-01 09:29:46 +02004919 def test_is_enabled_for_disabled_logger(self):
4920 old_disabled = self.logger.disabled
4921 old_disable = self.logger.manager.disable
4922
4923 self.logger.disabled = True
4924 self.logger.manager.disable = 21
4925
4926 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
4927 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
4928
4929 self.assertFalse(self.logger.isEnabledFor(22))
4930
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004931 def test_root_logger_aliases(self):
4932 root = logging.getLogger()
4933 self.assertIs(root, logging.root)
4934 self.assertIs(root, logging.getLogger(None))
4935 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01004936 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004937 self.assertIs(root, logging.getLogger('foo').root)
4938 self.assertIs(root, logging.getLogger('foo.bar').root)
4939 self.assertIs(root, logging.getLogger('foo').parent)
4940
4941 self.assertIsNot(root, logging.getLogger('\0'))
4942 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
4943
4944 def test_invalid_names(self):
4945 self.assertRaises(TypeError, logging.getLogger, any)
4946 self.assertRaises(TypeError, logging.getLogger, b'foo')
4947
Vinay Sajip6260d9f2017-06-06 16:34:29 +01004948 def test_pickling(self):
4949 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
4950 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
4951 logger = logging.getLogger(name)
4952 s = pickle.dumps(logger, proto)
4953 unpickled = pickle.loads(s)
4954 self.assertIs(unpickled, logger)
4955
Avram Lubkin78c18a92017-07-30 05:36:33 -04004956 def test_caching(self):
4957 root = self.root_logger
4958 logger1 = logging.getLogger("abc")
4959 logger2 = logging.getLogger("abc.def")
4960
4961 # Set root logger level and ensure cache is empty
4962 root.setLevel(logging.ERROR)
4963 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
4964 self.assertEqual(logger2._cache, {})
4965
4966 # Ensure cache is populated and calls are consistent
4967 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4968 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
4969 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
4970 self.assertEqual(root._cache, {})
4971 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4972
4973 # Ensure root cache gets populated
4974 self.assertEqual(root._cache, {})
4975 self.assertTrue(root.isEnabledFor(logging.ERROR))
4976 self.assertEqual(root._cache, {logging.ERROR: True})
4977
4978 # Set parent logger level and ensure caches are emptied
4979 logger1.setLevel(logging.CRITICAL)
4980 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4981 self.assertEqual(logger2._cache, {})
4982
4983 # Ensure logger2 uses parent logger's effective level
4984 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4985
4986 # Set level to NOTSET and ensure caches are empty
4987 logger2.setLevel(logging.NOTSET)
4988 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4989 self.assertEqual(logger2._cache, {})
4990 self.assertEqual(logger1._cache, {})
4991 self.assertEqual(root._cache, {})
4992
4993 # Verify logger2 follows parent and not root
4994 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4995 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
4996 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
4997 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
4998 self.assertTrue(root.isEnabledFor(logging.ERROR))
4999
5000 # Disable logging in manager and ensure caches are clear
5001 logging.disable()
5002 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5003 self.assertEqual(logger2._cache, {})
5004 self.assertEqual(logger1._cache, {})
5005 self.assertEqual(root._cache, {})
5006
5007 # Ensure no loggers are enabled
5008 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5009 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5010 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5011
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005012
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005013class BaseFileTest(BaseTest):
5014 "Base class for handler tests that write log files"
5015
5016 def setUp(self):
5017 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005018 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5019 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005020 self.rmfiles = []
5021
5022 def tearDown(self):
5023 for fn in self.rmfiles:
5024 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005025 if os.path.exists(self.fn):
5026 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005027 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005028
5029 def assertLogFile(self, filename):
5030 "Assert a log file is there and register it for deletion"
5031 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005032 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005033 self.rmfiles.append(filename)
5034
5035
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005036class FileHandlerTest(BaseFileTest):
5037 def test_delay(self):
5038 os.unlink(self.fn)
5039 fh = logging.FileHandler(self.fn, delay=True)
5040 self.assertIsNone(fh.stream)
5041 self.assertFalse(os.path.exists(self.fn))
5042 fh.handle(logging.makeLogRecord({}))
5043 self.assertIsNotNone(fh.stream)
5044 self.assertTrue(os.path.exists(self.fn))
5045 fh.close()
5046
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005047class RotatingFileHandlerTest(BaseFileTest):
5048 def next_rec(self):
5049 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5050 self.next_message(), None, None, None)
5051
5052 def test_should_not_rollover(self):
5053 # If maxbytes is zero rollover never occurs
5054 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
5055 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005056 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005057
5058 def test_should_rollover(self):
5059 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
5060 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005061 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005062
5063 def test_file_created(self):
5064 # checks that the file is created and assumes it was created
5065 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005066 rh = logging.handlers.RotatingFileHandler(self.fn)
5067 rh.emit(self.next_rec())
5068 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005069 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005070
5071 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005072 def namer(name):
5073 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005074 rh = logging.handlers.RotatingFileHandler(
5075 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005076 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005077 rh.emit(self.next_rec())
5078 self.assertLogFile(self.fn)
5079 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005080 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005081 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005082 self.assertLogFile(namer(self.fn + ".2"))
5083 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5084 rh.close()
5085
l0rb519cb872019-11-06 22:21:40 +01005086 def test_namer_rotator_inheritance(self):
5087 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5088 def namer(self, name):
5089 return name + ".test"
5090
5091 def rotator(self, source, dest):
5092 if os.path.exists(source):
5093 os.rename(source, dest + ".rotated")
5094
5095 rh = HandlerWithNamerAndRotator(
5096 self.fn, backupCount=2, maxBytes=1)
5097 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5098 rh.emit(self.next_rec())
5099 self.assertLogFile(self.fn)
5100 rh.emit(self.next_rec())
5101 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5102 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5103 rh.close()
5104
Hai Shia3ec3ad2020-05-19 06:02:57 +08005105 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005106 def test_rotator(self):
5107 def namer(name):
5108 return name + ".gz"
5109
5110 def rotator(source, dest):
5111 with open(source, "rb") as sf:
5112 data = sf.read()
5113 compressed = zlib.compress(data, 9)
5114 with open(dest, "wb") as df:
5115 df.write(compressed)
5116 os.remove(source)
5117
5118 rh = logging.handlers.RotatingFileHandler(
5119 self.fn, backupCount=2, maxBytes=1)
5120 rh.rotator = rotator
5121 rh.namer = namer
5122 m1 = self.next_rec()
5123 rh.emit(m1)
5124 self.assertLogFile(self.fn)
5125 m2 = self.next_rec()
5126 rh.emit(m2)
5127 fn = namer(self.fn + ".1")
5128 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005129 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005130 with open(fn, "rb") as f:
5131 compressed = f.read()
5132 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005133 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005134 rh.emit(self.next_rec())
5135 fn = namer(self.fn + ".2")
5136 self.assertLogFile(fn)
5137 with open(fn, "rb") as f:
5138 compressed = f.read()
5139 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005140 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005141 rh.emit(self.next_rec())
5142 fn = namer(self.fn + ".2")
5143 with open(fn, "rb") as f:
5144 compressed = f.read()
5145 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005146 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005147 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005148 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005149
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005150class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005151 # other test methods added below
5152 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01005153 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
5154 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005155 fmt = logging.Formatter('%(asctime)s %(message)s')
5156 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005157 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005158 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005159 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005160 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005161 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005162 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005163 fh.close()
5164 # At this point, we should have a recent rotated file which we
5165 # can test for the existence of. However, in practice, on some
5166 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005167 # in time to go to look for the log file. So, we go back a fair
5168 # bit, and stop as soon as we see a rotated file. In theory this
5169 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005170 found = False
5171 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005172 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005173 for secs in range(GO_BACK):
5174 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005175 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5176 found = os.path.exists(fn)
5177 if found:
5178 self.rmfiles.append(fn)
5179 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005180 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5181 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005182 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005183 dn, fn = os.path.split(self.fn)
5184 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005185 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5186 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005187 for f in files:
5188 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005189 path = os.path.join(dn, f)
5190 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005191 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005192 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005193
Vinay Sajip0372e102011-05-05 12:59:14 +01005194 def test_invalid(self):
5195 assertRaises = self.assertRaises
5196 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005197 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005198 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005199 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005200 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005201 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005202
Vinay Sajipa7130792013-04-12 17:04:23 +01005203 def test_compute_rollover_daily_attime(self):
5204 currentTime = 0
5205 atTime = datetime.time(12, 0, 0)
5206 rh = logging.handlers.TimedRotatingFileHandler(
5207 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
5208 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005209 try:
5210 actual = rh.computeRollover(currentTime)
5211 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005212
Vinay Sajipd86ac962013-04-14 12:20:46 +01005213 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5214 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5215 finally:
5216 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005217
Vinay Sajip10e8c492013-05-18 10:19:54 -07005218 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005219 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005220 currentTime = int(time.time())
5221 today = currentTime - currentTime % 86400
5222
Vinay Sajipa7130792013-04-12 17:04:23 +01005223 atTime = datetime.time(12, 0, 0)
5224
Vinay Sajip10e8c492013-05-18 10:19:54 -07005225 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005226 for day in range(7):
5227 rh = logging.handlers.TimedRotatingFileHandler(
5228 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
5229 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005230 try:
5231 if wday > day:
5232 # The rollover day has already passed this week, so we
5233 # go over into next week
5234 expected = (7 - wday + day)
5235 else:
5236 expected = (day - wday)
5237 # At this point expected is in days from now, convert to seconds
5238 expected *= 24 * 60 * 60
5239 # Add in the rollover time
5240 expected += 12 * 60 * 60
5241 # Add in adjustment for today
5242 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005243 actual = rh.computeRollover(today)
5244 if actual != expected:
5245 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005246 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005247 self.assertEqual(actual, expected)
5248 if day == wday:
5249 # goes into following week
5250 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005251 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005252 if actual != expected:
5253 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005254 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005255 self.assertEqual(actual, expected)
5256 finally:
5257 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005258
5259
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005260def secs(**kw):
5261 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5262
5263for when, exp in (('S', 1),
5264 ('M', 60),
5265 ('H', 60 * 60),
5266 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005267 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005268 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005269 ('W0', secs(days=4, hours=24)),
5270 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005271 def test_compute_rollover(self, when=when, exp=exp):
5272 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00005273 self.fn, when=when, interval=1, backupCount=0, utc=True)
5274 currentTime = 0.0
5275 actual = rh.computeRollover(currentTime)
5276 if exp != actual:
5277 # Failures occur on some systems for MIDNIGHT and W0.
5278 # Print detailed calculation for MIDNIGHT so we can try to see
5279 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005280 if when == 'MIDNIGHT':
5281 try:
5282 if rh.utc:
5283 t = time.gmtime(currentTime)
5284 else:
5285 t = time.localtime(currentTime)
5286 currentHour = t[3]
5287 currentMinute = t[4]
5288 currentSecond = t[5]
5289 # r is the number of seconds left between now and midnight
5290 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5291 currentMinute) * 60 +
5292 currentSecond)
5293 result = currentTime + r
5294 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5295 print('currentHour: %s' % currentHour, file=sys.stderr)
5296 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5297 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5298 print('r: %s' % r, file=sys.stderr)
5299 print('result: %s' % result, file=sys.stderr)
5300 except Exception:
5301 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5302 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005303 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005304 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5305
Vinay Sajip60ccd822011-05-09 17:32:09 +01005306
Vinay Sajip223349c2015-10-01 20:37:54 +01005307@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005308class NTEventLogHandlerTest(BaseTest):
5309 def test_basic(self):
5310 logtype = 'Application'
5311 elh = win32evtlog.OpenEventLog(None, logtype)
5312 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005313
5314 try:
5315 h = logging.handlers.NTEventLogHandler('test_logging')
5316 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005317 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005318 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005319 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005320
Vinay Sajip60ccd822011-05-09 17:32:09 +01005321 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5322 h.handle(r)
5323 h.close()
5324 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005325 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005326 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5327 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5328 found = False
5329 GO_BACK = 100
5330 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5331 for e in events:
5332 if e.SourceName != 'test_logging':
5333 continue
5334 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5335 if msg != 'Test Log Message\r\n':
5336 continue
5337 found = True
5338 break
5339 msg = 'Record not found in event log, went back %d records' % GO_BACK
5340 self.assertTrue(found, msg=msg)
5341
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005342
5343class MiscTestCase(unittest.TestCase):
5344 def test__all__(self):
5345 blacklist = {'logThreads', 'logMultiprocessing',
5346 'logProcesses', 'currentframe',
5347 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5348 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger',
Martin Panter94332cb2016-10-20 05:10:44 +00005349 'root', 'threading'}
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005350 support.check__all__(self, logging, blacklist=blacklist)
5351
5352
Christian Heimes180510d2008-03-03 19:15:45 +00005353# Set the locale to the platform-dependent default. I have no idea
5354# why the test does this, but in any case we save the current locale
5355# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005356@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005357def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005358 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005359 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5360 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5361 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5362 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5363 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5364 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5365 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5366 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005367 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005368 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005369 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005370 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005371 ]
5372 if hasattr(logging.handlers, 'QueueListener'):
5373 tests.append(QueueListenerTest)
5374 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005375
Christian Heimes180510d2008-03-03 19:15:45 +00005376if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005377 test_main()