blob: 9a114451913e88fa60e1938827c06b9e22bd3637 [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 Lapeyre65f64b12019-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
Christian Heimes180510d2008-03-03 19:15:45 +00001160
1161class ExceptionFormatter(logging.Formatter):
1162 """A special exception formatter."""
1163 def formatException(self, ei):
1164 return "Got a [%s]" % ei[0].__name__
1165
1166
1167class ConfigFileTest(BaseTest):
1168
1169 """Reading logging config from a .ini-style config file."""
1170
Xtreak087570a2018-07-02 14:27:46 +05301171 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001172 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001173
1174 # config0 is a standard configuration.
1175 config0 = """
1176 [loggers]
1177 keys=root
1178
1179 [handlers]
1180 keys=hand1
1181
1182 [formatters]
1183 keys=form1
1184
1185 [logger_root]
1186 level=WARNING
1187 handlers=hand1
1188
1189 [handler_hand1]
1190 class=StreamHandler
1191 level=NOTSET
1192 formatter=form1
1193 args=(sys.stdout,)
1194
1195 [formatter_form1]
1196 format=%(levelname)s ++ %(message)s
1197 datefmt=
1198 """
1199
1200 # config1 adds a little to the standard configuration.
1201 config1 = """
1202 [loggers]
1203 keys=root,parser
1204
1205 [handlers]
1206 keys=hand1
1207
1208 [formatters]
1209 keys=form1
1210
1211 [logger_root]
1212 level=WARNING
1213 handlers=
1214
1215 [logger_parser]
1216 level=DEBUG
1217 handlers=hand1
1218 propagate=1
1219 qualname=compiler.parser
1220
1221 [handler_hand1]
1222 class=StreamHandler
1223 level=NOTSET
1224 formatter=form1
1225 args=(sys.stdout,)
1226
1227 [formatter_form1]
1228 format=%(levelname)s ++ %(message)s
1229 datefmt=
1230 """
1231
Vinay Sajip3f84b072011-03-07 17:49:33 +00001232 # config1a moves the handler to the root.
1233 config1a = """
1234 [loggers]
1235 keys=root,parser
1236
1237 [handlers]
1238 keys=hand1
1239
1240 [formatters]
1241 keys=form1
1242
1243 [logger_root]
1244 level=WARNING
1245 handlers=hand1
1246
1247 [logger_parser]
1248 level=DEBUG
1249 handlers=
1250 propagate=1
1251 qualname=compiler.parser
1252
1253 [handler_hand1]
1254 class=StreamHandler
1255 level=NOTSET
1256 formatter=form1
1257 args=(sys.stdout,)
1258
1259 [formatter_form1]
1260 format=%(levelname)s ++ %(message)s
1261 datefmt=
1262 """
1263
Christian Heimes180510d2008-03-03 19:15:45 +00001264 # config2 has a subtle configuration error that should be reported
1265 config2 = config1.replace("sys.stdout", "sys.stbout")
1266
1267 # config3 has a less subtle configuration error
1268 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1269
1270 # config4 specifies a custom formatter class to be loaded
1271 config4 = """
1272 [loggers]
1273 keys=root
1274
1275 [handlers]
1276 keys=hand1
1277
1278 [formatters]
1279 keys=form1
1280
1281 [logger_root]
1282 level=NOTSET
1283 handlers=hand1
1284
1285 [handler_hand1]
1286 class=StreamHandler
1287 level=NOTSET
1288 formatter=form1
1289 args=(sys.stdout,)
1290
1291 [formatter_form1]
1292 class=""" + __name__ + """.ExceptionFormatter
1293 format=%(levelname)s:%(name)s:%(message)s
1294 datefmt=
1295 """
1296
Georg Brandl3dbca812008-07-23 16:10:53 +00001297 # config5 specifies a custom handler class to be loaded
1298 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1299
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001300 # config6 uses ', ' delimiters in the handlers and formatters sections
1301 config6 = """
1302 [loggers]
1303 keys=root,parser
1304
1305 [handlers]
1306 keys=hand1, hand2
1307
1308 [formatters]
1309 keys=form1, form2
1310
1311 [logger_root]
1312 level=WARNING
1313 handlers=
1314
1315 [logger_parser]
1316 level=DEBUG
1317 handlers=hand1
1318 propagate=1
1319 qualname=compiler.parser
1320
1321 [handler_hand1]
1322 class=StreamHandler
1323 level=NOTSET
1324 formatter=form1
1325 args=(sys.stdout,)
1326
1327 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001328 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001329 level=NOTSET
1330 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001331 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001332
1333 [formatter_form1]
1334 format=%(levelname)s ++ %(message)s
1335 datefmt=
1336
1337 [formatter_form2]
1338 format=%(message)s
1339 datefmt=
1340 """
1341
Preston Landers6ea56d22017-08-02 15:44:28 -05001342 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001343 config7 = """
1344 [loggers]
1345 keys=root,parser,compiler
1346
1347 [handlers]
1348 keys=hand1
1349
1350 [formatters]
1351 keys=form1
1352
1353 [logger_root]
1354 level=WARNING
1355 handlers=hand1
1356
1357 [logger_compiler]
1358 level=DEBUG
1359 handlers=
1360 propagate=1
1361 qualname=compiler
1362
1363 [logger_parser]
1364 level=DEBUG
1365 handlers=
1366 propagate=1
1367 qualname=compiler.parser
1368
1369 [handler_hand1]
1370 class=StreamHandler
1371 level=NOTSET
1372 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001373 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001374
1375 [formatter_form1]
1376 format=%(levelname)s ++ %(message)s
1377 datefmt=
1378 """
1379
Xtreak087570a2018-07-02 14:27:46 +05301380 # config 8, check for resource warning
1381 config8 = r"""
1382 [loggers]
1383 keys=root
1384
1385 [handlers]
1386 keys=file
1387
1388 [formatters]
1389 keys=
1390
1391 [logger_root]
1392 level=DEBUG
1393 handlers=file
1394
1395 [handler_file]
1396 class=FileHandler
1397 level=DEBUG
1398 args=("{tempfile}",)
1399 """
1400
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001401 disable_test = """
1402 [loggers]
1403 keys=root
1404
1405 [handlers]
1406 keys=screen
1407
1408 [formatters]
1409 keys=
1410
1411 [logger_root]
1412 level=DEBUG
1413 handlers=screen
1414
1415 [handler_screen]
1416 level=DEBUG
1417 class=StreamHandler
1418 args=(sys.stdout,)
1419 formatter=
1420 """
1421
1422 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001423 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001424 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001425
1426 def test_config0_ok(self):
1427 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001428 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001429 self.apply_config(self.config0)
1430 logger = logging.getLogger()
1431 # Won't output anything
1432 logger.info(self.next_message())
1433 # Outputs a message
1434 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001435 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001436 ('ERROR', '2'),
1437 ], stream=output)
1438 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001439 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001440
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001441 def test_config0_using_cp_ok(self):
1442 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001443 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001444 file = io.StringIO(textwrap.dedent(self.config0))
1445 cp = configparser.ConfigParser()
1446 cp.read_file(file)
1447 logging.config.fileConfig(cp)
1448 logger = logging.getLogger()
1449 # Won't output anything
1450 logger.info(self.next_message())
1451 # Outputs a message
1452 logger.error(self.next_message())
1453 self.assert_log_lines([
1454 ('ERROR', '2'),
1455 ], stream=output)
1456 # Original logger output is empty.
1457 self.assert_log_lines([])
1458
Georg Brandl3dbca812008-07-23 16:10:53 +00001459 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001460 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001461 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001462 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001463 logger = logging.getLogger("compiler.parser")
1464 # Both will output a message
1465 logger.info(self.next_message())
1466 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001467 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001468 ('INFO', '1'),
1469 ('ERROR', '2'),
1470 ], stream=output)
1471 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001472 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001473
1474 def test_config2_failure(self):
1475 # A simple config file which overrides the default settings.
1476 self.assertRaises(Exception, self.apply_config, self.config2)
1477
1478 def test_config3_failure(self):
1479 # A simple config file which overrides the default settings.
1480 self.assertRaises(Exception, self.apply_config, self.config3)
1481
1482 def test_config4_ok(self):
1483 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001484 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001485 self.apply_config(self.config4)
1486 logger = logging.getLogger()
1487 try:
1488 raise RuntimeError()
1489 except RuntimeError:
1490 logging.exception("just testing")
1491 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001492 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001493 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1494 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001495 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001496
Georg Brandl3dbca812008-07-23 16:10:53 +00001497 def test_config5_ok(self):
1498 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001499
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001500 def test_config6_ok(self):
1501 self.test_config1_ok(config=self.config6)
1502
Vinay Sajip3f84b072011-03-07 17:49:33 +00001503 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001504 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001505 self.apply_config(self.config1a)
1506 logger = logging.getLogger("compiler.parser")
1507 # See issue #11424. compiler-hyphenated sorts
1508 # between compiler and compiler.xyz and this
1509 # was preventing compiler.xyz from being included
1510 # in the child loggers of compiler because of an
1511 # overzealous loop termination condition.
1512 hyphenated = logging.getLogger('compiler-hyphenated')
1513 # All will output a message
1514 logger.info(self.next_message())
1515 logger.error(self.next_message())
1516 hyphenated.critical(self.next_message())
1517 self.assert_log_lines([
1518 ('INFO', '1'),
1519 ('ERROR', '2'),
1520 ('CRITICAL', '3'),
1521 ], stream=output)
1522 # Original logger output is empty.
1523 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001524 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001525 self.apply_config(self.config7)
1526 logger = logging.getLogger("compiler.parser")
1527 self.assertFalse(logger.disabled)
1528 # Both will output a message
1529 logger.info(self.next_message())
1530 logger.error(self.next_message())
1531 logger = logging.getLogger("compiler.lexer")
1532 # Both will output a message
1533 logger.info(self.next_message())
1534 logger.error(self.next_message())
1535 # Will not appear
1536 hyphenated.critical(self.next_message())
1537 self.assert_log_lines([
1538 ('INFO', '4'),
1539 ('ERROR', '5'),
1540 ('INFO', '6'),
1541 ('ERROR', '7'),
1542 ], stream=output)
1543 # Original logger output is empty.
1544 self.assert_log_lines([])
1545
Xtreak087570a2018-07-02 14:27:46 +05301546 def test_config8_ok(self):
1547
1548 def cleanup(h1, fn):
1549 h1.close()
1550 os.remove(fn)
1551
1552 with self.check_no_resource_warning():
1553 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1554 os.close(fd)
1555
1556 # Replace single backslash with double backslash in windows
1557 # to avoid unicode error during string formatting
1558 if os.name == "nt":
1559 fn = fn.replace("\\", "\\\\")
1560
1561 config8 = self.config8.format(tempfile=fn)
1562
1563 self.apply_config(config8)
1564 self.apply_config(config8)
1565
1566 handler = logging.root.handlers[0]
1567 self.addCleanup(cleanup, handler, fn)
1568
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001569 def test_logger_disabling(self):
1570 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001571 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001572 self.assertFalse(logger.disabled)
1573 self.apply_config(self.disable_test)
1574 self.assertTrue(logger.disabled)
1575 self.apply_config(self.disable_test, disable_existing_loggers=False)
1576 self.assertFalse(logger.disabled)
1577
Lucas Cimonb15100f2019-10-31 09:06:25 +01001578 def test_config_set_handler_names(self):
1579 test_config = """
1580 [loggers]
1581 keys=root
1582
1583 [handlers]
1584 keys=hand1
1585
1586 [formatters]
1587 keys=form1
1588
1589 [logger_root]
1590 handlers=hand1
1591
1592 [handler_hand1]
1593 class=StreamHandler
1594 formatter=form1
1595
1596 [formatter_form1]
1597 format=%(levelname)s ++ %(message)s
1598 """
1599 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001600 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001601
Łukasz Langa214f18e2018-06-08 04:02:48 -07001602 def test_defaults_do_no_interpolation(self):
1603 """bpo-33802 defaults should not get interpolated"""
1604 ini = textwrap.dedent("""
1605 [formatters]
1606 keys=default
1607
1608 [formatter_default]
1609
1610 [handlers]
1611 keys=console
1612
1613 [handler_console]
1614 class=logging.StreamHandler
1615 args=tuple()
1616
1617 [loggers]
1618 keys=root
1619
1620 [logger_root]
1621 formatter=default
1622 handlers=console
1623 """).strip()
1624 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1625 try:
1626 os.write(fd, ini.encode('ascii'))
1627 os.close(fd)
1628 logging.config.fileConfig(
1629 fn,
1630 defaults=dict(
1631 version=1,
1632 disable_existing_loggers=False,
1633 formatters={
1634 "generic": {
1635 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1636 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1637 "class": "logging.Formatter"
1638 },
1639 },
1640 )
1641 )
1642 finally:
1643 os.unlink(fn)
1644
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001645
Christian Heimes180510d2008-03-03 19:15:45 +00001646class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001647
Christian Heimes180510d2008-03-03 19:15:45 +00001648 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001649
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001650 server_class = TestTCPServer
1651 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001652
Christian Heimes180510d2008-03-03 19:15:45 +00001653 def setUp(self):
1654 """Set up a TCP server to receive log messages, and a SocketHandler
1655 pointing to that server's address and port."""
1656 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001657 # Issue #29177: deal with errors that happen during setup
1658 self.server = self.sock_hdlr = self.server_exception = None
1659 try:
1660 self.server = server = self.server_class(self.address,
1661 self.handle_socket, 0.01)
1662 server.start()
1663 # Uncomment next line to test error recovery in setUp()
1664 # raise OSError('dummy error raised')
1665 except OSError as e:
1666 self.server_exception = e
1667 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001668 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001669 hcls = logging.handlers.SocketHandler
1670 if isinstance(server.server_address, tuple):
1671 self.sock_hdlr = hcls('localhost', server.port)
1672 else:
1673 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001674 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001675 self.root_logger.removeHandler(self.root_logger.handlers[0])
1676 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001677 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001678
Christian Heimes180510d2008-03-03 19:15:45 +00001679 def tearDown(self):
1680 """Shutdown the TCP server."""
1681 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001682 if self.sock_hdlr:
1683 self.root_logger.removeHandler(self.sock_hdlr)
1684 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001685 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001686 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001687 finally:
1688 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001689
Vinay Sajip7367d082011-05-02 13:17:27 +01001690 def handle_socket(self, request):
1691 conn = request.connection
1692 while True:
1693 chunk = conn.recv(4)
1694 if len(chunk) < 4:
1695 break
1696 slen = struct.unpack(">L", chunk)[0]
1697 chunk = conn.recv(slen)
1698 while len(chunk) < slen:
1699 chunk = chunk + conn.recv(slen - len(chunk))
1700 obj = pickle.loads(chunk)
1701 record = logging.makeLogRecord(obj)
1702 self.log_output += record.msg + '\n'
1703 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001704
Christian Heimes180510d2008-03-03 19:15:45 +00001705 def test_output(self):
1706 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001707 if self.server_exception:
1708 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001709 logger = logging.getLogger("tcp")
1710 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001711 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001712 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001713 self.handled.acquire()
1714 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001715
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001716 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001717 if self.server_exception:
1718 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001719 # Avoid timing-related failures due to SocketHandler's own hard-wired
1720 # one-second timeout on socket.create_connection() (issue #16264).
1721 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001722 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001723 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001724 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001725 try:
1726 raise RuntimeError('Deliberate mistake')
1727 except RuntimeError:
1728 self.root_logger.exception('Never sent')
1729 self.root_logger.error('Never sent, either')
1730 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001731 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001732 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1733 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001734
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001735def _get_temp_domain_socket():
1736 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1737 os.close(fd)
1738 # just need a name - file can't be present, or we'll get an
1739 # 'address already in use' error.
1740 os.remove(fn)
1741 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001742
Victor Stinnerec5a8602014-06-02 14:41:51 +02001743@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001744class UnixSocketHandlerTest(SocketHandlerTest):
1745
1746 """Test for SocketHandler with unix sockets."""
1747
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001748 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001749 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001750
1751 def setUp(self):
1752 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001753 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001754 SocketHandlerTest.setUp(self)
1755
1756 def tearDown(self):
1757 SocketHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001758 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001759
Vinay Sajip7367d082011-05-02 13:17:27 +01001760class DatagramHandlerTest(BaseTest):
1761
1762 """Test for DatagramHandler."""
1763
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001764 server_class = TestUDPServer
1765 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001766
Vinay Sajip7367d082011-05-02 13:17:27 +01001767 def setUp(self):
1768 """Set up a UDP server to receive log messages, and a DatagramHandler
1769 pointing to that server's address and port."""
1770 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001771 # Issue #29177: deal with errors that happen during setup
1772 self.server = self.sock_hdlr = self.server_exception = None
1773 try:
1774 self.server = server = self.server_class(self.address,
1775 self.handle_datagram, 0.01)
1776 server.start()
1777 # Uncomment next line to test error recovery in setUp()
1778 # raise OSError('dummy error raised')
1779 except OSError as e:
1780 self.server_exception = e
1781 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001782 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001783 hcls = logging.handlers.DatagramHandler
1784 if isinstance(server.server_address, tuple):
1785 self.sock_hdlr = hcls('localhost', server.port)
1786 else:
1787 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001788 self.log_output = ''
1789 self.root_logger.removeHandler(self.root_logger.handlers[0])
1790 self.root_logger.addHandler(self.sock_hdlr)
1791 self.handled = threading.Event()
1792
1793 def tearDown(self):
1794 """Shutdown the UDP server."""
1795 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001796 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001797 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001798 if self.sock_hdlr:
1799 self.root_logger.removeHandler(self.sock_hdlr)
1800 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001801 finally:
1802 BaseTest.tearDown(self)
1803
1804 def handle_datagram(self, request):
1805 slen = struct.pack('>L', 0) # length of prefix
1806 packet = request.packet[len(slen):]
1807 obj = pickle.loads(packet)
1808 record = logging.makeLogRecord(obj)
1809 self.log_output += record.msg + '\n'
1810 self.handled.set()
1811
1812 def test_output(self):
1813 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001814 if self.server_exception:
1815 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001816 logger = logging.getLogger("udp")
1817 logger.error("spam")
1818 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001819 self.handled.clear()
1820 logger.error("eggs")
1821 self.handled.wait()
1822 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001823
Victor Stinnerec5a8602014-06-02 14:41:51 +02001824@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001825class UnixDatagramHandlerTest(DatagramHandlerTest):
1826
1827 """Test for DatagramHandler using Unix sockets."""
1828
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001829 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001830 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001831
1832 def setUp(self):
1833 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001834 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001835 DatagramHandlerTest.setUp(self)
1836
1837 def tearDown(self):
1838 DatagramHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001839 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001840
Vinay Sajip7367d082011-05-02 13:17:27 +01001841class SysLogHandlerTest(BaseTest):
1842
1843 """Test for SysLogHandler using UDP."""
1844
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001845 server_class = TestUDPServer
1846 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001847
Vinay Sajip7367d082011-05-02 13:17:27 +01001848 def setUp(self):
1849 """Set up a UDP server to receive log messages, and a SysLogHandler
1850 pointing to that server's address and port."""
1851 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001852 # Issue #29177: deal with errors that happen during setup
1853 self.server = self.sl_hdlr = self.server_exception = None
1854 try:
1855 self.server = server = self.server_class(self.address,
1856 self.handle_datagram, 0.01)
1857 server.start()
1858 # Uncomment next line to test error recovery in setUp()
1859 # raise OSError('dummy error raised')
1860 except OSError as e:
1861 self.server_exception = e
1862 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001863 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001864 hcls = logging.handlers.SysLogHandler
1865 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001866 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001867 else:
1868 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001869 self.log_output = ''
1870 self.root_logger.removeHandler(self.root_logger.handlers[0])
1871 self.root_logger.addHandler(self.sl_hdlr)
1872 self.handled = threading.Event()
1873
1874 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001875 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001876 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001877 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001878 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001879 if self.sl_hdlr:
1880 self.root_logger.removeHandler(self.sl_hdlr)
1881 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001882 finally:
1883 BaseTest.tearDown(self)
1884
1885 def handle_datagram(self, request):
1886 self.log_output = request.packet
1887 self.handled.set()
1888
1889 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001890 if self.server_exception:
1891 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001892 # The log message sent to the SysLogHandler is properly received.
1893 logger = logging.getLogger("slh")
1894 logger.error("sp\xe4m")
1895 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001896 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001897 self.handled.clear()
1898 self.sl_hdlr.append_nul = False
1899 logger.error("sp\xe4m")
1900 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001901 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001902 self.handled.clear()
1903 self.sl_hdlr.ident = "h\xe4m-"
1904 logger.error("sp\xe4m")
1905 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001906 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001907
Victor Stinnerec5a8602014-06-02 14:41:51 +02001908@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001909class UnixSysLogHandlerTest(SysLogHandlerTest):
1910
1911 """Test for SysLogHandler with Unix sockets."""
1912
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001913 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001914 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001915
1916 def setUp(self):
1917 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001918 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001919 SysLogHandlerTest.setUp(self)
1920
1921 def tearDown(self):
1922 SysLogHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001923 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001924
Serhiy Storchaka16994912020-04-25 10:06:29 +03001925@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001926 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001927class IPv6SysLogHandlerTest(SysLogHandlerTest):
1928
1929 """Test for SysLogHandler with IPv6 host."""
1930
1931 server_class = TestUDPServer
1932 address = ('::1', 0)
1933
1934 def setUp(self):
1935 self.server_class.address_family = socket.AF_INET6
1936 super(IPv6SysLogHandlerTest, self).setUp()
1937
1938 def tearDown(self):
1939 self.server_class.address_family = socket.AF_INET
1940 super(IPv6SysLogHandlerTest, self).tearDown()
1941
Vinay Sajip7367d082011-05-02 13:17:27 +01001942class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001943 """Test for HTTPHandler."""
1944
1945 def setUp(self):
1946 """Set up an HTTP server to receive log messages, and a HTTPHandler
1947 pointing to that server's address and port."""
1948 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001949 self.handled = threading.Event()
1950
Vinay Sajip7367d082011-05-02 13:17:27 +01001951 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001952 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001953 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001954 if self.command == 'POST':
1955 try:
1956 rlen = int(request.headers['Content-Length'])
1957 self.post_data = request.rfile.read(rlen)
1958 except:
1959 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001960 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001961 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001962 self.handled.set()
1963
1964 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001965 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001966 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001967 root_logger = self.root_logger
1968 root_logger.removeHandler(self.root_logger.handlers[0])
1969 for secure in (False, True):
1970 addr = ('localhost', 0)
1971 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001972 try:
1973 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01001974 except ImportError:
1975 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001976 else:
1977 here = os.path.dirname(__file__)
1978 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02001979 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001980 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06001981
1982 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01001983 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001984 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06001985 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01001986 self.server = server = TestHTTPServer(addr, self.handle_request,
1987 0.01, sslctx=sslctx)
1988 server.start()
1989 server.ready.wait()
1990 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001991 secure_client = secure and sslctx
1992 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06001993 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01001994 context=context,
1995 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01001996 self.log_data = None
1997 root_logger.addHandler(self.h_hdlr)
1998
1999 for method in ('GET', 'POST'):
2000 self.h_hdlr.method = method
2001 self.handled.clear()
2002 msg = "sp\xe4m"
2003 logger.error(msg)
2004 self.handled.wait()
2005 self.assertEqual(self.log_data.path, '/frob')
2006 self.assertEqual(self.command, method)
2007 if method == 'GET':
2008 d = parse_qs(self.log_data.query)
2009 else:
2010 d = parse_qs(self.post_data.decode('utf-8'))
2011 self.assertEqual(d['name'], ['http'])
2012 self.assertEqual(d['funcName'], ['test_output'])
2013 self.assertEqual(d['msg'], [msg])
2014
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002015 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002016 self.root_logger.removeHandler(self.h_hdlr)
2017 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002018
Christian Heimes180510d2008-03-03 19:15:45 +00002019class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002020
Christian Heimes180510d2008-03-03 19:15:45 +00002021 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002022
Christian Heimes180510d2008-03-03 19:15:45 +00002023 def setUp(self):
2024 """Create a dict to remember potentially destroyed objects."""
2025 BaseTest.setUp(self)
2026 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002027
Christian Heimes180510d2008-03-03 19:15:45 +00002028 def _watch_for_survival(self, *args):
2029 """Watch the given objects for survival, by creating weakrefs to
2030 them."""
2031 for obj in args:
2032 key = id(obj), repr(obj)
2033 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002034
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002035 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002036 """Assert that all objects watched for survival have survived."""
2037 # Trigger cycle breaking.
2038 gc.collect()
2039 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002040 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002041 if ref() is None:
2042 dead.append(repr_)
2043 if dead:
2044 self.fail("%d objects should have survived "
2045 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002046
Christian Heimes180510d2008-03-03 19:15:45 +00002047 def test_persistent_loggers(self):
2048 # Logger objects are persistent and retain their configuration, even
2049 # if visible references are destroyed.
2050 self.root_logger.setLevel(logging.INFO)
2051 foo = logging.getLogger("foo")
2052 self._watch_for_survival(foo)
2053 foo.setLevel(logging.DEBUG)
2054 self.root_logger.debug(self.next_message())
2055 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002056 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002057 ('foo', 'DEBUG', '2'),
2058 ])
2059 del foo
2060 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002061 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002062 # foo has retained its settings.
2063 bar = logging.getLogger("foo")
2064 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002065 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002066 ('foo', 'DEBUG', '2'),
2067 ('foo', 'DEBUG', '3'),
2068 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002069
Benjamin Petersonf91df042009-02-13 02:50:59 +00002070
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002071class EncodingTest(BaseTest):
2072 def test_encoding_plain_file(self):
2073 # In Python 2.x, a plain file object is treated as having no encoding.
2074 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002075 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2076 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002077 # the non-ascii data we write to the log.
2078 data = "foo\x80"
2079 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002080 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002081 log.addHandler(handler)
2082 try:
2083 # write non-ascii data to the log.
2084 log.warning(data)
2085 finally:
2086 log.removeHandler(handler)
2087 handler.close()
2088 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002089 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002090 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002091 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002092 finally:
2093 f.close()
2094 finally:
2095 if os.path.isfile(fn):
2096 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002097
Benjamin Petersonf91df042009-02-13 02:50:59 +00002098 def test_encoding_cyrillic_unicode(self):
2099 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002100 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002101 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002102 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002103 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002104 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002105 stream = io.BytesIO()
2106 writer = writer_class(stream, 'strict')
2107 handler = logging.StreamHandler(writer)
2108 log.addHandler(handler)
2109 try:
2110 log.warning(message)
2111 finally:
2112 log.removeHandler(handler)
2113 handler.close()
2114 # check we wrote exactly those bytes, ignoring trailing \n etc
2115 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002116 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002117 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2118
2119
Georg Brandlf9734072008-12-07 15:30:06 +00002120class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002121
Georg Brandlf9734072008-12-07 15:30:06 +00002122 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002123 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002124 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002125 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002126 warnings.filterwarnings("always", category=UserWarning)
2127 stream = io.StringIO()
2128 h = logging.StreamHandler(stream)
2129 logger = logging.getLogger("py.warnings")
2130 logger.addHandler(h)
2131 warnings.warn("I'm warning you...")
2132 logger.removeHandler(h)
2133 s = stream.getvalue()
2134 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002135 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002136
Mike53f7a7c2017-12-14 14:04:53 +03002137 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002138 a_file = io.StringIO()
2139 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2140 a_file, "Dummy line")
2141 s = a_file.getvalue()
2142 a_file.close()
2143 self.assertEqual(s,
2144 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2145
2146 def test_warnings_no_handlers(self):
2147 with warnings.catch_warnings():
2148 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002149 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002150
2151 # confirm our assumption: no loggers are set
2152 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002153 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002154
2155 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002156 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002157 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002158
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002159
2160def formatFunc(format, datefmt=None):
2161 return logging.Formatter(format, datefmt)
2162
BNMetrics18fb1fb2018-10-15 19:41:36 +01002163class myCustomFormatter:
2164 def __init__(self, fmt, datefmt=None):
2165 pass
2166
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002167def handlerFunc():
2168 return logging.StreamHandler()
2169
2170class CustomHandler(logging.StreamHandler):
2171 pass
2172
2173class ConfigDictTest(BaseTest):
2174
2175 """Reading logging config from a dictionary."""
2176
Xtreak087570a2018-07-02 14:27:46 +05302177 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002178 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002179
2180 # config0 is a standard configuration.
2181 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002182 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002183 'formatters': {
2184 'form1' : {
2185 'format' : '%(levelname)s ++ %(message)s',
2186 },
2187 },
2188 'handlers' : {
2189 'hand1' : {
2190 'class' : 'logging.StreamHandler',
2191 'formatter' : 'form1',
2192 'level' : 'NOTSET',
2193 'stream' : 'ext://sys.stdout',
2194 },
2195 },
2196 'root' : {
2197 'level' : 'WARNING',
2198 'handlers' : ['hand1'],
2199 },
2200 }
2201
2202 # config1 adds a little to the standard configuration.
2203 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002204 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002205 'formatters': {
2206 'form1' : {
2207 'format' : '%(levelname)s ++ %(message)s',
2208 },
2209 },
2210 'handlers' : {
2211 'hand1' : {
2212 'class' : 'logging.StreamHandler',
2213 'formatter' : 'form1',
2214 'level' : 'NOTSET',
2215 'stream' : 'ext://sys.stdout',
2216 },
2217 },
2218 'loggers' : {
2219 'compiler.parser' : {
2220 'level' : 'DEBUG',
2221 'handlers' : ['hand1'],
2222 },
2223 },
2224 'root' : {
2225 'level' : 'WARNING',
2226 },
2227 }
2228
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002229 # config1a moves the handler to the root. Used with config8a
2230 config1a = {
2231 'version': 1,
2232 'formatters': {
2233 'form1' : {
2234 'format' : '%(levelname)s ++ %(message)s',
2235 },
2236 },
2237 'handlers' : {
2238 'hand1' : {
2239 'class' : 'logging.StreamHandler',
2240 'formatter' : 'form1',
2241 'level' : 'NOTSET',
2242 'stream' : 'ext://sys.stdout',
2243 },
2244 },
2245 'loggers' : {
2246 'compiler.parser' : {
2247 'level' : 'DEBUG',
2248 },
2249 },
2250 'root' : {
2251 'level' : 'WARNING',
2252 'handlers' : ['hand1'],
2253 },
2254 }
2255
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002256 # config2 has a subtle configuration error that should be reported
2257 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002258 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002259 'formatters': {
2260 'form1' : {
2261 'format' : '%(levelname)s ++ %(message)s',
2262 },
2263 },
2264 'handlers' : {
2265 'hand1' : {
2266 'class' : 'logging.StreamHandler',
2267 'formatter' : 'form1',
2268 'level' : 'NOTSET',
2269 'stream' : 'ext://sys.stdbout',
2270 },
2271 },
2272 'loggers' : {
2273 'compiler.parser' : {
2274 'level' : 'DEBUG',
2275 'handlers' : ['hand1'],
2276 },
2277 },
2278 'root' : {
2279 'level' : 'WARNING',
2280 },
2281 }
2282
Mike53f7a7c2017-12-14 14:04:53 +03002283 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002284 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002285 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002286 'formatters': {
2287 'form1' : {
2288 'format' : '%(levelname)s ++ %(message)s',
2289 },
2290 },
2291 'handlers' : {
2292 'hand1' : {
2293 'class' : 'logging.StreamHandler',
2294 'formatter' : 'form1',
2295 'level' : 'NTOSET',
2296 'stream' : 'ext://sys.stdout',
2297 },
2298 },
2299 'loggers' : {
2300 'compiler.parser' : {
2301 'level' : 'DEBUG',
2302 'handlers' : ['hand1'],
2303 },
2304 },
2305 'root' : {
2306 'level' : 'WARNING',
2307 },
2308 }
2309
2310
Mike53f7a7c2017-12-14 14:04:53 +03002311 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002312 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002313 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002314 'formatters': {
2315 'form1' : {
2316 'format' : '%(levelname)s ++ %(message)s',
2317 },
2318 },
2319 'handlers' : {
2320 'hand1' : {
2321 'class' : 'logging.StreamHandler',
2322 'formatter' : 'form1',
2323 'level' : 'NOTSET',
2324 'stream' : 'ext://sys.stdout',
2325 },
2326 },
2327 'loggers' : {
2328 'compiler.parser' : {
2329 'level' : 'DEBUG',
2330 'handlers' : ['hand1'],
2331 },
2332 },
2333 'root' : {
2334 'level' : 'WRANING',
2335 },
2336 }
2337
2338 # config3 has a less subtle configuration error
2339 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002340 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002341 'formatters': {
2342 'form1' : {
2343 'format' : '%(levelname)s ++ %(message)s',
2344 },
2345 },
2346 'handlers' : {
2347 'hand1' : {
2348 'class' : 'logging.StreamHandler',
2349 'formatter' : 'misspelled_name',
2350 'level' : 'NOTSET',
2351 'stream' : 'ext://sys.stdout',
2352 },
2353 },
2354 'loggers' : {
2355 'compiler.parser' : {
2356 'level' : 'DEBUG',
2357 'handlers' : ['hand1'],
2358 },
2359 },
2360 'root' : {
2361 'level' : 'WARNING',
2362 },
2363 }
2364
2365 # config4 specifies a custom formatter class to be loaded
2366 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002367 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002368 'formatters': {
2369 'form1' : {
2370 '()' : __name__ + '.ExceptionFormatter',
2371 'format' : '%(levelname)s:%(name)s:%(message)s',
2372 },
2373 },
2374 'handlers' : {
2375 'hand1' : {
2376 'class' : 'logging.StreamHandler',
2377 'formatter' : 'form1',
2378 'level' : 'NOTSET',
2379 'stream' : 'ext://sys.stdout',
2380 },
2381 },
2382 'root' : {
2383 'level' : 'NOTSET',
2384 'handlers' : ['hand1'],
2385 },
2386 }
2387
2388 # As config4 but using an actual callable rather than a string
2389 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002390 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002391 'formatters': {
2392 'form1' : {
2393 '()' : ExceptionFormatter,
2394 'format' : '%(levelname)s:%(name)s:%(message)s',
2395 },
2396 'form2' : {
2397 '()' : __name__ + '.formatFunc',
2398 'format' : '%(levelname)s:%(name)s:%(message)s',
2399 },
2400 'form3' : {
2401 '()' : formatFunc,
2402 'format' : '%(levelname)s:%(name)s:%(message)s',
2403 },
2404 },
2405 'handlers' : {
2406 'hand1' : {
2407 'class' : 'logging.StreamHandler',
2408 'formatter' : 'form1',
2409 'level' : 'NOTSET',
2410 'stream' : 'ext://sys.stdout',
2411 },
2412 'hand2' : {
2413 '()' : handlerFunc,
2414 },
2415 },
2416 'root' : {
2417 'level' : 'NOTSET',
2418 'handlers' : ['hand1'],
2419 },
2420 }
2421
2422 # config5 specifies a custom handler class to be loaded
2423 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002424 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002425 'formatters': {
2426 'form1' : {
2427 'format' : '%(levelname)s ++ %(message)s',
2428 },
2429 },
2430 'handlers' : {
2431 'hand1' : {
2432 'class' : __name__ + '.CustomHandler',
2433 'formatter' : 'form1',
2434 'level' : 'NOTSET',
2435 'stream' : 'ext://sys.stdout',
2436 },
2437 },
2438 'loggers' : {
2439 'compiler.parser' : {
2440 'level' : 'DEBUG',
2441 'handlers' : ['hand1'],
2442 },
2443 },
2444 'root' : {
2445 'level' : 'WARNING',
2446 },
2447 }
2448
2449 # config6 specifies a custom handler class to be loaded
2450 # but has bad arguments
2451 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002452 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002453 'formatters': {
2454 'form1' : {
2455 'format' : '%(levelname)s ++ %(message)s',
2456 },
2457 },
2458 'handlers' : {
2459 'hand1' : {
2460 'class' : __name__ + '.CustomHandler',
2461 'formatter' : 'form1',
2462 'level' : 'NOTSET',
2463 'stream' : 'ext://sys.stdout',
2464 '9' : 'invalid parameter name',
2465 },
2466 },
2467 'loggers' : {
2468 'compiler.parser' : {
2469 'level' : 'DEBUG',
2470 'handlers' : ['hand1'],
2471 },
2472 },
2473 'root' : {
2474 'level' : 'WARNING',
2475 },
2476 }
2477
Mike53f7a7c2017-12-14 14:04:53 +03002478 # config 7 does not define compiler.parser but defines compiler.lexer
2479 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002480 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002481 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002482 'formatters': {
2483 'form1' : {
2484 'format' : '%(levelname)s ++ %(message)s',
2485 },
2486 },
2487 'handlers' : {
2488 'hand1' : {
2489 'class' : 'logging.StreamHandler',
2490 'formatter' : 'form1',
2491 'level' : 'NOTSET',
2492 'stream' : 'ext://sys.stdout',
2493 },
2494 },
2495 'loggers' : {
2496 'compiler.lexer' : {
2497 'level' : 'DEBUG',
2498 'handlers' : ['hand1'],
2499 },
2500 },
2501 'root' : {
2502 'level' : 'WARNING',
2503 },
2504 }
2505
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002506 # config8 defines both compiler and compiler.lexer
2507 # so compiler.parser should not be disabled (since
2508 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002509 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002510 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002511 'disable_existing_loggers' : False,
2512 'formatters': {
2513 'form1' : {
2514 'format' : '%(levelname)s ++ %(message)s',
2515 },
2516 },
2517 'handlers' : {
2518 'hand1' : {
2519 'class' : 'logging.StreamHandler',
2520 'formatter' : 'form1',
2521 'level' : 'NOTSET',
2522 'stream' : 'ext://sys.stdout',
2523 },
2524 },
2525 'loggers' : {
2526 'compiler' : {
2527 'level' : 'DEBUG',
2528 'handlers' : ['hand1'],
2529 },
2530 'compiler.lexer' : {
2531 },
2532 },
2533 'root' : {
2534 'level' : 'WARNING',
2535 },
2536 }
2537
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002538 # config8a disables existing loggers
2539 config8a = {
2540 'version': 1,
2541 'disable_existing_loggers' : True,
2542 'formatters': {
2543 'form1' : {
2544 'format' : '%(levelname)s ++ %(message)s',
2545 },
2546 },
2547 'handlers' : {
2548 'hand1' : {
2549 'class' : 'logging.StreamHandler',
2550 'formatter' : 'form1',
2551 'level' : 'NOTSET',
2552 'stream' : 'ext://sys.stdout',
2553 },
2554 },
2555 'loggers' : {
2556 'compiler' : {
2557 'level' : 'DEBUG',
2558 'handlers' : ['hand1'],
2559 },
2560 'compiler.lexer' : {
2561 },
2562 },
2563 'root' : {
2564 'level' : 'WARNING',
2565 },
2566 }
2567
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002568 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002569 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002570 'formatters': {
2571 'form1' : {
2572 'format' : '%(levelname)s ++ %(message)s',
2573 },
2574 },
2575 'handlers' : {
2576 'hand1' : {
2577 'class' : 'logging.StreamHandler',
2578 'formatter' : 'form1',
2579 'level' : 'WARNING',
2580 'stream' : 'ext://sys.stdout',
2581 },
2582 },
2583 'loggers' : {
2584 'compiler.parser' : {
2585 'level' : 'WARNING',
2586 'handlers' : ['hand1'],
2587 },
2588 },
2589 'root' : {
2590 'level' : 'NOTSET',
2591 },
2592 }
2593
2594 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002595 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002596 'incremental' : True,
2597 'handlers' : {
2598 'hand1' : {
2599 'level' : 'WARNING',
2600 },
2601 },
2602 'loggers' : {
2603 'compiler.parser' : {
2604 'level' : 'INFO',
2605 },
2606 },
2607 }
2608
2609 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002610 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002611 'incremental' : True,
2612 'handlers' : {
2613 'hand1' : {
2614 'level' : 'INFO',
2615 },
2616 },
2617 'loggers' : {
2618 'compiler.parser' : {
2619 'level' : 'INFO',
2620 },
2621 },
2622 }
2623
Mike53f7a7c2017-12-14 14:04:53 +03002624 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002625 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002626 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002627 'formatters': {
2628 'form1' : {
2629 'format' : '%(levelname)s ++ %(message)s',
2630 },
2631 },
2632 'filters' : {
2633 'filt1' : {
2634 'name' : 'compiler.parser',
2635 },
2636 },
2637 'handlers' : {
2638 'hand1' : {
2639 'class' : 'logging.StreamHandler',
2640 'formatter' : 'form1',
2641 'level' : 'NOTSET',
2642 'stream' : 'ext://sys.stdout',
2643 'filters' : ['filt1'],
2644 },
2645 },
2646 'loggers' : {
2647 'compiler.parser' : {
2648 'level' : 'DEBUG',
2649 'filters' : ['filt1'],
2650 },
2651 },
2652 'root' : {
2653 'level' : 'WARNING',
2654 'handlers' : ['hand1'],
2655 },
2656 }
2657
Mike53f7a7c2017-12-14 14:04:53 +03002658 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002659 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002660 'version': 1,
2661 'true_formatters': {
2662 'form1' : {
2663 'format' : '%(levelname)s ++ %(message)s',
2664 },
2665 },
2666 'handler_configs': {
2667 'hand1' : {
2668 'class' : 'logging.StreamHandler',
2669 'formatter' : 'form1',
2670 'level' : 'NOTSET',
2671 'stream' : 'ext://sys.stdout',
2672 },
2673 },
2674 'formatters' : 'cfg://true_formatters',
2675 'handlers' : {
2676 'hand1' : 'cfg://handler_configs[hand1]',
2677 },
2678 'loggers' : {
2679 'compiler.parser' : {
2680 'level' : 'DEBUG',
2681 'handlers' : ['hand1'],
2682 },
2683 },
2684 'root' : {
2685 'level' : 'WARNING',
2686 },
2687 }
2688
Mike53f7a7c2017-12-14 14:04:53 +03002689 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002690 config12 = {
2691 'true_formatters': {
2692 'form1' : {
2693 'format' : '%(levelname)s ++ %(message)s',
2694 },
2695 },
2696 'handler_configs': {
2697 'hand1' : {
2698 'class' : 'logging.StreamHandler',
2699 'formatter' : 'form1',
2700 'level' : 'NOTSET',
2701 'stream' : 'ext://sys.stdout',
2702 },
2703 },
2704 'formatters' : 'cfg://true_formatters',
2705 'handlers' : {
2706 'hand1' : 'cfg://handler_configs[hand1]',
2707 },
2708 'loggers' : {
2709 'compiler.parser' : {
2710 'level' : 'DEBUG',
2711 'handlers' : ['hand1'],
2712 },
2713 },
2714 'root' : {
2715 'level' : 'WARNING',
2716 },
2717 }
2718
Mike53f7a7c2017-12-14 14:04:53 +03002719 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002720 config13 = {
2721 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002722 'true_formatters': {
2723 'form1' : {
2724 'format' : '%(levelname)s ++ %(message)s',
2725 },
2726 },
2727 'handler_configs': {
2728 'hand1' : {
2729 'class' : 'logging.StreamHandler',
2730 'formatter' : 'form1',
2731 'level' : 'NOTSET',
2732 'stream' : 'ext://sys.stdout',
2733 },
2734 },
2735 'formatters' : 'cfg://true_formatters',
2736 'handlers' : {
2737 'hand1' : 'cfg://handler_configs[hand1]',
2738 },
2739 'loggers' : {
2740 'compiler.parser' : {
2741 'level' : 'DEBUG',
2742 'handlers' : ['hand1'],
2743 },
2744 },
2745 'root' : {
2746 'level' : 'WARNING',
2747 },
2748 }
2749
Vinay Sajip8d270232012-11-15 14:20:18 +00002750 # As config0, but with properties
2751 config14 = {
2752 'version': 1,
2753 'formatters': {
2754 'form1' : {
2755 'format' : '%(levelname)s ++ %(message)s',
2756 },
2757 },
2758 'handlers' : {
2759 'hand1' : {
2760 'class' : 'logging.StreamHandler',
2761 'formatter' : 'form1',
2762 'level' : 'NOTSET',
2763 'stream' : 'ext://sys.stdout',
2764 '.': {
2765 'foo': 'bar',
2766 'terminator': '!\n',
2767 }
2768 },
2769 },
2770 'root' : {
2771 'level' : 'WARNING',
2772 'handlers' : ['hand1'],
2773 },
2774 }
2775
Vinay Sajip3f885b52013-03-22 15:19:54 +00002776 out_of_order = {
2777 "version": 1,
2778 "formatters": {
2779 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002780 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2781 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002782 }
2783 },
2784 "handlers": {
2785 "fileGlobal": {
2786 "class": "logging.StreamHandler",
2787 "level": "DEBUG",
2788 "formatter": "mySimpleFormatter"
2789 },
2790 "bufferGlobal": {
2791 "class": "logging.handlers.MemoryHandler",
2792 "capacity": 5,
2793 "formatter": "mySimpleFormatter",
2794 "target": "fileGlobal",
2795 "level": "DEBUG"
2796 }
2797 },
2798 "loggers": {
2799 "mymodule": {
2800 "level": "DEBUG",
2801 "handlers": ["bufferGlobal"],
2802 "propagate": "true"
2803 }
2804 }
2805 }
2806
BNMetrics18fb1fb2018-10-15 19:41:36 +01002807 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2808 custom_formatter_class_validate = {
2809 'version': 1,
2810 'formatters': {
2811 'form1': {
2812 '()': __name__ + '.ExceptionFormatter',
2813 'format': '%(levelname)s:%(name)s:%(message)s',
2814 'validate': False,
2815 },
2816 },
2817 'handlers' : {
2818 'hand1' : {
2819 'class': 'logging.StreamHandler',
2820 'formatter': 'form1',
2821 'level': 'NOTSET',
2822 'stream': 'ext://sys.stdout',
2823 },
2824 },
2825 "loggers": {
2826 "my_test_logger_custom_formatter": {
2827 "level": "DEBUG",
2828 "handlers": ["hand1"],
2829 "propagate": "true"
2830 }
2831 }
2832 }
2833
2834 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2835 custom_formatter_class_validate2 = {
2836 'version': 1,
2837 'formatters': {
2838 'form1': {
2839 'class': __name__ + '.ExceptionFormatter',
2840 'format': '%(levelname)s:%(name)s:%(message)s',
2841 'validate': False,
2842 },
2843 },
2844 'handlers' : {
2845 'hand1' : {
2846 'class': 'logging.StreamHandler',
2847 'formatter': 'form1',
2848 'level': 'NOTSET',
2849 'stream': 'ext://sys.stdout',
2850 },
2851 },
2852 "loggers": {
2853 "my_test_logger_custom_formatter": {
2854 "level": "DEBUG",
2855 "handlers": ["hand1"],
2856 "propagate": "true"
2857 }
2858 }
2859 }
2860
2861 # Configuration with custom class that is not inherited from logging.Formatter
2862 custom_formatter_class_validate3 = {
2863 'version': 1,
2864 'formatters': {
2865 'form1': {
2866 'class': __name__ + '.myCustomFormatter',
2867 'format': '%(levelname)s:%(name)s:%(message)s',
2868 'validate': False,
2869 },
2870 },
2871 'handlers' : {
2872 'hand1' : {
2873 'class': 'logging.StreamHandler',
2874 'formatter': 'form1',
2875 'level': 'NOTSET',
2876 'stream': 'ext://sys.stdout',
2877 },
2878 },
2879 "loggers": {
2880 "my_test_logger_custom_formatter": {
2881 "level": "DEBUG",
2882 "handlers": ["hand1"],
2883 "propagate": "true"
2884 }
2885 }
2886 }
2887
2888 # Configuration with custom function and 'validate' set to False
2889 custom_formatter_with_function = {
2890 'version': 1,
2891 'formatters': {
2892 'form1': {
2893 '()': formatFunc,
2894 'format': '%(levelname)s:%(name)s:%(message)s',
2895 'validate': False,
2896 },
2897 },
2898 'handlers' : {
2899 'hand1' : {
2900 'class': 'logging.StreamHandler',
2901 'formatter': 'form1',
2902 'level': 'NOTSET',
2903 'stream': 'ext://sys.stdout',
2904 },
2905 },
2906 "loggers": {
2907 "my_test_logger_custom_formatter": {
2908 "level": "DEBUG",
2909 "handlers": ["hand1"],
2910 "propagate": "true"
2911 }
2912 }
2913 }
2914
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002915 def apply_config(self, conf):
2916 logging.config.dictConfig(conf)
2917
2918 def test_config0_ok(self):
2919 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002920 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002921 self.apply_config(self.config0)
2922 logger = logging.getLogger()
2923 # Won't output anything
2924 logger.info(self.next_message())
2925 # Outputs a message
2926 logger.error(self.next_message())
2927 self.assert_log_lines([
2928 ('ERROR', '2'),
2929 ], stream=output)
2930 # Original logger output is empty.
2931 self.assert_log_lines([])
2932
2933 def test_config1_ok(self, config=config1):
2934 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002935 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002936 self.apply_config(config)
2937 logger = logging.getLogger("compiler.parser")
2938 # Both will output a message
2939 logger.info(self.next_message())
2940 logger.error(self.next_message())
2941 self.assert_log_lines([
2942 ('INFO', '1'),
2943 ('ERROR', '2'),
2944 ], stream=output)
2945 # Original logger output is empty.
2946 self.assert_log_lines([])
2947
2948 def test_config2_failure(self):
2949 # A simple config which overrides the default settings.
2950 self.assertRaises(Exception, self.apply_config, self.config2)
2951
2952 def test_config2a_failure(self):
2953 # A simple config which overrides the default settings.
2954 self.assertRaises(Exception, self.apply_config, self.config2a)
2955
2956 def test_config2b_failure(self):
2957 # A simple config which overrides the default settings.
2958 self.assertRaises(Exception, self.apply_config, self.config2b)
2959
2960 def test_config3_failure(self):
2961 # A simple config which overrides the default settings.
2962 self.assertRaises(Exception, self.apply_config, self.config3)
2963
2964 def test_config4_ok(self):
2965 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002966 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002967 self.apply_config(self.config4)
2968 #logger = logging.getLogger()
2969 try:
2970 raise RuntimeError()
2971 except RuntimeError:
2972 logging.exception("just testing")
2973 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002974 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002975 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2976 # Original logger output is empty
2977 self.assert_log_lines([])
2978
2979 def test_config4a_ok(self):
2980 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002981 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002982 self.apply_config(self.config4a)
2983 #logger = logging.getLogger()
2984 try:
2985 raise RuntimeError()
2986 except RuntimeError:
2987 logging.exception("just testing")
2988 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002989 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002990 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2991 # Original logger output is empty
2992 self.assert_log_lines([])
2993
2994 def test_config5_ok(self):
2995 self.test_config1_ok(config=self.config5)
2996
2997 def test_config6_failure(self):
2998 self.assertRaises(Exception, self.apply_config, self.config6)
2999
3000 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003001 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003002 self.apply_config(self.config1)
3003 logger = logging.getLogger("compiler.parser")
3004 # Both will output a message
3005 logger.info(self.next_message())
3006 logger.error(self.next_message())
3007 self.assert_log_lines([
3008 ('INFO', '1'),
3009 ('ERROR', '2'),
3010 ], stream=output)
3011 # Original logger output is empty.
3012 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003013 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003014 self.apply_config(self.config7)
3015 logger = logging.getLogger("compiler.parser")
3016 self.assertTrue(logger.disabled)
3017 logger = logging.getLogger("compiler.lexer")
3018 # Both will output a message
3019 logger.info(self.next_message())
3020 logger.error(self.next_message())
3021 self.assert_log_lines([
3022 ('INFO', '3'),
3023 ('ERROR', '4'),
3024 ], stream=output)
3025 # Original logger output is empty.
3026 self.assert_log_lines([])
3027
Mike53f7a7c2017-12-14 14:04:53 +03003028 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003029 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003030 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003031 self.apply_config(self.config1)
3032 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003033 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003034 logger.info(self.next_message())
3035 logger.error(self.next_message())
3036 self.assert_log_lines([
3037 ('INFO', '1'),
3038 ('ERROR', '2'),
3039 ], stream=output)
3040 # Original logger output is empty.
3041 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003042 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003043 self.apply_config(self.config8)
3044 logger = logging.getLogger("compiler.parser")
3045 self.assertFalse(logger.disabled)
3046 # Both will output a message
3047 logger.info(self.next_message())
3048 logger.error(self.next_message())
3049 logger = logging.getLogger("compiler.lexer")
3050 # Both will output a message
3051 logger.info(self.next_message())
3052 logger.error(self.next_message())
3053 self.assert_log_lines([
3054 ('INFO', '3'),
3055 ('ERROR', '4'),
3056 ('INFO', '5'),
3057 ('ERROR', '6'),
3058 ], stream=output)
3059 # Original logger output is empty.
3060 self.assert_log_lines([])
3061
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003062 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003063 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003064 self.apply_config(self.config1a)
3065 logger = logging.getLogger("compiler.parser")
3066 # See issue #11424. compiler-hyphenated sorts
3067 # between compiler and compiler.xyz and this
3068 # was preventing compiler.xyz from being included
3069 # in the child loggers of compiler because of an
3070 # overzealous loop termination condition.
3071 hyphenated = logging.getLogger('compiler-hyphenated')
3072 # All will output a message
3073 logger.info(self.next_message())
3074 logger.error(self.next_message())
3075 hyphenated.critical(self.next_message())
3076 self.assert_log_lines([
3077 ('INFO', '1'),
3078 ('ERROR', '2'),
3079 ('CRITICAL', '3'),
3080 ], stream=output)
3081 # Original logger output is empty.
3082 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003083 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003084 self.apply_config(self.config8a)
3085 logger = logging.getLogger("compiler.parser")
3086 self.assertFalse(logger.disabled)
3087 # Both will output a message
3088 logger.info(self.next_message())
3089 logger.error(self.next_message())
3090 logger = logging.getLogger("compiler.lexer")
3091 # Both will output a message
3092 logger.info(self.next_message())
3093 logger.error(self.next_message())
3094 # Will not appear
3095 hyphenated.critical(self.next_message())
3096 self.assert_log_lines([
3097 ('INFO', '4'),
3098 ('ERROR', '5'),
3099 ('INFO', '6'),
3100 ('ERROR', '7'),
3101 ], stream=output)
3102 # Original logger output is empty.
3103 self.assert_log_lines([])
3104
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003105 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003106 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003107 self.apply_config(self.config9)
3108 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003109 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003110 logger.info(self.next_message())
3111 self.assert_log_lines([], stream=output)
3112 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003113 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003114 logger.info(self.next_message())
3115 self.assert_log_lines([], stream=output)
3116 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003117 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003118 logger.info(self.next_message())
3119 self.assert_log_lines([
3120 ('INFO', '3'),
3121 ], stream=output)
3122
3123 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003124 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003125 self.apply_config(self.config10)
3126 logger = logging.getLogger("compiler.parser")
3127 logger.warning(self.next_message())
3128 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003129 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003130 logger.warning(self.next_message())
3131 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003132 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003133 logger.warning(self.next_message())
3134 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003135 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003136 logger.error(self.next_message())
3137 self.assert_log_lines([
3138 ('WARNING', '1'),
3139 ('ERROR', '4'),
3140 ], stream=output)
3141
3142 def test_config11_ok(self):
3143 self.test_config1_ok(self.config11)
3144
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003145 def test_config12_failure(self):
3146 self.assertRaises(Exception, self.apply_config, self.config12)
3147
3148 def test_config13_failure(self):
3149 self.assertRaises(Exception, self.apply_config, self.config13)
3150
Vinay Sajip8d270232012-11-15 14:20:18 +00003151 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003152 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003153 self.apply_config(self.config14)
3154 h = logging._handlers['hand1']
3155 self.assertEqual(h.foo, 'bar')
3156 self.assertEqual(h.terminator, '!\n')
3157 logging.warning('Exclamation')
3158 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3159
Xtreak087570a2018-07-02 14:27:46 +05303160 def test_config15_ok(self):
3161
3162 def cleanup(h1, fn):
3163 h1.close()
3164 os.remove(fn)
3165
3166 with self.check_no_resource_warning():
3167 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3168 os.close(fd)
3169
3170 config = {
3171 "version": 1,
3172 "handlers": {
3173 "file": {
3174 "class": "logging.FileHandler",
3175 "filename": fn
3176 }
3177 },
3178 "root": {
3179 "handlers": ["file"]
3180 }
3181 }
3182
3183 self.apply_config(config)
3184 self.apply_config(config)
3185
3186 handler = logging.root.handlers[0]
3187 self.addCleanup(cleanup, handler, fn)
3188
Vinay Sajip4ded5512012-10-02 15:56:16 +01003189 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003190 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003191 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003192 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003193 t.start()
3194 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003195 # Now get the port allocated
3196 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003197 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003198 try:
3199 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3200 sock.settimeout(2.0)
3201 sock.connect(('localhost', port))
3202
3203 slen = struct.pack('>L', len(text))
3204 s = slen + text
3205 sentsofar = 0
3206 left = len(s)
3207 while left > 0:
3208 sent = sock.send(s[sentsofar:])
3209 sentsofar += sent
3210 left -= sent
3211 sock.close()
3212 finally:
3213 t.ready.wait(2.0)
3214 logging.config.stopListening()
Victor Stinnerbbc8b792019-12-10 20:41:23 +01003215 support.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003216
3217 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003218 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003219 self.setup_via_listener(json.dumps(self.config10))
3220 logger = logging.getLogger("compiler.parser")
3221 logger.warning(self.next_message())
3222 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003223 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003224 logger.warning(self.next_message())
3225 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003226 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003227 logger.warning(self.next_message())
3228 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003229 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003230 logger.error(self.next_message())
3231 self.assert_log_lines([
3232 ('WARNING', '1'),
3233 ('ERROR', '4'),
3234 ], stream=output)
3235
3236 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003237 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003238 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3239 logger = logging.getLogger("compiler.parser")
3240 # Both will output a message
3241 logger.info(self.next_message())
3242 logger.error(self.next_message())
3243 self.assert_log_lines([
3244 ('INFO', '1'),
3245 ('ERROR', '2'),
3246 ], stream=output)
3247 # Original logger output is empty.
3248 self.assert_log_lines([])
3249
Vinay Sajip4ded5512012-10-02 15:56:16 +01003250 def test_listen_verify(self):
3251
3252 def verify_fail(stuff):
3253 return None
3254
3255 def verify_reverse(stuff):
3256 return stuff[::-1]
3257
3258 logger = logging.getLogger("compiler.parser")
3259 to_send = textwrap.dedent(ConfigFileTest.config1)
3260 # First, specify a verification function that will fail.
3261 # We expect to see no output, since our configuration
3262 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003263 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003264 self.setup_via_listener(to_send, verify_fail)
3265 # Both will output a message
3266 logger.info(self.next_message())
3267 logger.error(self.next_message())
3268 self.assert_log_lines([], stream=output)
3269 # Original logger output has the stuff we logged.
3270 self.assert_log_lines([
3271 ('INFO', '1'),
3272 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003273 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003274
3275 # Now, perform no verification. Our configuration
3276 # should take effect.
3277
Vinay Sajip1feedb42014-05-31 08:19:12 +01003278 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003279 self.setup_via_listener(to_send) # no verify callable specified
3280 logger = logging.getLogger("compiler.parser")
3281 # Both will output a message
3282 logger.info(self.next_message())
3283 logger.error(self.next_message())
3284 self.assert_log_lines([
3285 ('INFO', '3'),
3286 ('ERROR', '4'),
3287 ], stream=output)
3288 # Original logger output still has the stuff we logged before.
3289 self.assert_log_lines([
3290 ('INFO', '1'),
3291 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003292 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003293
3294 # Now, perform verification which transforms the bytes.
3295
Vinay Sajip1feedb42014-05-31 08:19:12 +01003296 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003297 self.setup_via_listener(to_send[::-1], verify_reverse)
3298 logger = logging.getLogger("compiler.parser")
3299 # Both will output a message
3300 logger.info(self.next_message())
3301 logger.error(self.next_message())
3302 self.assert_log_lines([
3303 ('INFO', '5'),
3304 ('ERROR', '6'),
3305 ], stream=output)
3306 # Original logger output still has the stuff we logged before.
3307 self.assert_log_lines([
3308 ('INFO', '1'),
3309 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003310 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003311
Vinay Sajip3f885b52013-03-22 15:19:54 +00003312 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003313 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3314
3315 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003316 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003317 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3318
3319 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003320 handler = logging.getLogger('mymodule').handlers[0]
3321 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003322 self.assertIsInstance(handler.formatter._style,
3323 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003324
BNMetrics18fb1fb2018-10-15 19:41:36 +01003325 def test_custom_formatter_class_with_validate(self):
3326 self.apply_config(self.custom_formatter_class_validate)
3327 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3328 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3329
3330 def test_custom_formatter_class_with_validate2(self):
3331 self.apply_config(self.custom_formatter_class_validate2)
3332 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3333 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3334
3335 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3336 config = self.custom_formatter_class_validate.copy()
3337 config['formatters']['form1']['style'] = "$"
3338
3339 # Exception should not be raise as we have configured 'validate' to False
3340 self.apply_config(config)
3341 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3342 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3343
3344 def test_custom_formatter_class_with_validate3(self):
3345 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3346
3347 def test_custom_formatter_function_with_validate(self):
3348 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3349
Vinay Sajip373baef2011-04-26 20:05:24 +01003350 def test_baseconfig(self):
3351 d = {
3352 'atuple': (1, 2, 3),
3353 'alist': ['a', 'b', 'c'],
3354 'adict': {'d': 'e', 'f': 3 },
3355 'nest1': ('g', ('h', 'i'), 'j'),
3356 'nest2': ['k', ['l', 'm'], 'n'],
3357 'nest3': ['o', 'cfg://alist', 'p'],
3358 }
3359 bc = logging.config.BaseConfigurator(d)
3360 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3361 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3362 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3363 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3364 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3365 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3366 v = bc.convert('cfg://nest3')
3367 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3368 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3369 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3370 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003371
Vinay Sajip46abfc12020-01-01 19:32:11 +00003372 def test_namedtuple(self):
3373 # see bpo-39142
3374 from collections import namedtuple
3375
3376 class MyHandler(logging.StreamHandler):
3377 def __init__(self, resource, *args, **kwargs):
3378 super().__init__(*args, **kwargs)
3379 self.resource: namedtuple = resource
3380
3381 def emit(self, record):
3382 record.msg += f' {self.resource.type}'
3383 return super().emit(record)
3384
3385 Resource = namedtuple('Resource', ['type', 'labels'])
3386 resource = Resource(type='my_type', labels=['a'])
3387
3388 config = {
3389 'version': 1,
3390 'handlers': {
3391 'myhandler': {
3392 '()': MyHandler,
3393 'resource': resource
3394 }
3395 },
3396 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3397 }
3398 with support.captured_stderr() as stderr:
3399 self.apply_config(config)
3400 logging.info('some log')
3401 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3402
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003403class ManagerTest(BaseTest):
3404 def test_manager_loggerclass(self):
3405 logged = []
3406
3407 class MyLogger(logging.Logger):
3408 def _log(self, level, msg, args, exc_info=None, extra=None):
3409 logged.append(msg)
3410
3411 man = logging.Manager(None)
3412 self.assertRaises(TypeError, man.setLoggerClass, int)
3413 man.setLoggerClass(MyLogger)
3414 logger = man.getLogger('test')
3415 logger.warning('should appear in logged')
3416 logging.warning('should not appear in logged')
3417
3418 self.assertEqual(logged, ['should appear in logged'])
3419
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003420 def test_set_log_record_factory(self):
3421 man = logging.Manager(None)
3422 expected = object()
3423 man.setLogRecordFactory(expected)
3424 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003425
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003426class ChildLoggerTest(BaseTest):
3427 def test_child_loggers(self):
3428 r = logging.getLogger()
3429 l1 = logging.getLogger('abc')
3430 l2 = logging.getLogger('def.ghi')
3431 c1 = r.getChild('xyz')
3432 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003433 self.assertIs(c1, logging.getLogger('xyz'))
3434 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003435 c1 = l1.getChild('def')
3436 c2 = c1.getChild('ghi')
3437 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003438 self.assertIs(c1, logging.getLogger('abc.def'))
3439 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3440 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003441
3442
Vinay Sajip6fac8172010-10-19 20:44:14 +00003443class DerivedLogRecord(logging.LogRecord):
3444 pass
3445
Vinay Sajip61561522010-12-03 11:50:38 +00003446class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003447
3448 def setUp(self):
3449 class CheckingFilter(logging.Filter):
3450 def __init__(self, cls):
3451 self.cls = cls
3452
3453 def filter(self, record):
3454 t = type(record)
3455 if t is not self.cls:
3456 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3457 self.cls)
3458 raise TypeError(msg)
3459 return True
3460
3461 BaseTest.setUp(self)
3462 self.filter = CheckingFilter(DerivedLogRecord)
3463 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003464 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003465
3466 def tearDown(self):
3467 self.root_logger.removeFilter(self.filter)
3468 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003469 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003470
3471 def test_logrecord_class(self):
3472 self.assertRaises(TypeError, self.root_logger.warning,
3473 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003474 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003475 self.root_logger.error(self.next_message())
3476 self.assert_log_lines([
3477 ('root', 'ERROR', '2'),
3478 ])
3479
3480
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003481class QueueHandlerTest(BaseTest):
3482 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003483 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003484
3485 def setUp(self):
3486 BaseTest.setUp(self)
3487 self.queue = queue.Queue(-1)
3488 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003489 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003490 self.que_logger = logging.getLogger('que')
3491 self.que_logger.propagate = False
3492 self.que_logger.setLevel(logging.WARNING)
3493 self.que_logger.addHandler(self.que_hdlr)
3494
3495 def tearDown(self):
3496 self.que_hdlr.close()
3497 BaseTest.tearDown(self)
3498
3499 def test_queue_handler(self):
3500 self.que_logger.debug(self.next_message())
3501 self.assertRaises(queue.Empty, self.queue.get_nowait)
3502 self.que_logger.info(self.next_message())
3503 self.assertRaises(queue.Empty, self.queue.get_nowait)
3504 msg = self.next_message()
3505 self.que_logger.warning(msg)
3506 data = self.queue.get_nowait()
3507 self.assertTrue(isinstance(data, logging.LogRecord))
3508 self.assertEqual(data.name, self.que_logger.name)
3509 self.assertEqual((data.msg, data.args), (msg, None))
3510
favlladfe3442017-08-01 20:12:26 +02003511 def test_formatting(self):
3512 msg = self.next_message()
3513 levelname = logging.getLevelName(logging.WARNING)
3514 log_format_str = '{name} -> {levelname}: {message}'
3515 formatted_msg = log_format_str.format(name=self.name,
3516 levelname=levelname, message=msg)
3517 formatter = logging.Formatter(self.log_format)
3518 self.que_hdlr.setFormatter(formatter)
3519 self.que_logger.warning(msg)
3520 log_record = self.queue.get_nowait()
3521 self.assertEqual(formatted_msg, log_record.msg)
3522 self.assertEqual(formatted_msg, log_record.message)
3523
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003524 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3525 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003526 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003527 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003528 listener = logging.handlers.QueueListener(self.queue, handler)
3529 listener.start()
3530 try:
3531 self.que_logger.warning(self.next_message())
3532 self.que_logger.error(self.next_message())
3533 self.que_logger.critical(self.next_message())
3534 finally:
3535 listener.stop()
3536 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3537 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3538 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003539 handler.close()
3540
3541 # Now test with respect_handler_level set
3542
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003543 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003544 handler.setLevel(logging.CRITICAL)
3545 listener = logging.handlers.QueueListener(self.queue, handler,
3546 respect_handler_level=True)
3547 listener.start()
3548 try:
3549 self.que_logger.warning(self.next_message())
3550 self.que_logger.error(self.next_message())
3551 self.que_logger.critical(self.next_message())
3552 finally:
3553 listener.stop()
3554 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3555 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3556 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003557 handler.close()
3558
3559 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3560 'logging.handlers.QueueListener required for this test')
3561 def test_queue_listener_with_StreamHandler(self):
3562 # Test that traceback only appends once (bpo-34334).
3563 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3564 listener.start()
3565 try:
3566 1 / 0
3567 except ZeroDivisionError as e:
3568 exc = e
3569 self.que_logger.exception(self.next_message(), exc_info=exc)
3570 listener.stop()
3571 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003572
Xtreak2dad9602019-04-07 13:21:27 +05303573 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3574 'logging.handlers.QueueListener required for this test')
3575 def test_queue_listener_with_multiple_handlers(self):
3576 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3577 self.que_hdlr.setFormatter(self.root_formatter)
3578 self.que_logger.addHandler(self.root_hdlr)
3579
3580 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3581 listener.start()
3582 self.que_logger.error("error")
3583 listener.stop()
3584 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3585
Vinay Sajipd61910c2016-09-08 01:13:39 +01003586if hasattr(logging.handlers, 'QueueListener'):
3587 import multiprocessing
3588 from unittest.mock import patch
3589
3590 class QueueListenerTest(BaseTest):
3591 """
3592 Tests based on patch submitted for issue #27930. Ensure that
3593 QueueListener handles all log messages.
3594 """
3595
3596 repeat = 20
3597
3598 @staticmethod
3599 def setup_and_log(log_queue, ident):
3600 """
3601 Creates a logger with a QueueHandler that logs to a queue read by a
3602 QueueListener. Starts the listener, logs five messages, and stops
3603 the listener.
3604 """
3605 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3606 logger.setLevel(logging.DEBUG)
3607 handler = logging.handlers.QueueHandler(log_queue)
3608 logger.addHandler(handler)
3609 listener = logging.handlers.QueueListener(log_queue)
3610 listener.start()
3611
3612 logger.info('one')
3613 logger.info('two')
3614 logger.info('three')
3615 logger.info('four')
3616 logger.info('five')
3617
3618 listener.stop()
3619 logger.removeHandler(handler)
3620 handler.close()
3621
3622 @patch.object(logging.handlers.QueueListener, 'handle')
3623 def test_handle_called_with_queue_queue(self, mock_handle):
3624 for i in range(self.repeat):
3625 log_queue = queue.Queue()
3626 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3627 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3628 'correct number of handled log messages')
3629
3630 @patch.object(logging.handlers.QueueListener, 'handle')
3631 def test_handle_called_with_mp_queue(self, mock_handle):
xdegayebf2b65e2017-12-01 08:08:49 +01003632 # Issue 28668: The multiprocessing (mp) module is not functional
3633 # when the mp.synchronize module cannot be imported.
3634 support.import_module('multiprocessing.synchronize')
Vinay Sajipd61910c2016-09-08 01:13:39 +01003635 for i in range(self.repeat):
3636 log_queue = multiprocessing.Queue()
3637 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003638 log_queue.close()
3639 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003640 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3641 'correct number of handled log messages')
3642
3643 @staticmethod
3644 def get_all_from_queue(log_queue):
3645 try:
3646 while True:
3647 yield log_queue.get_nowait()
3648 except queue.Empty:
3649 return []
3650
3651 def test_no_messages_in_queue_after_stop(self):
3652 """
3653 Five messages are logged then the QueueListener is stopped. This
3654 test then gets everything off the queue. Failure of this test
3655 indicates that messages were not registered on the queue until
3656 _after_ the QueueListener stopped.
3657 """
xdegayebf2b65e2017-12-01 08:08:49 +01003658 # Issue 28668: The multiprocessing (mp) module is not functional
3659 # when the mp.synchronize module cannot be imported.
3660 support.import_module('multiprocessing.synchronize')
Vinay Sajipd61910c2016-09-08 01:13:39 +01003661 for i in range(self.repeat):
3662 queue = multiprocessing.Queue()
3663 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3664 # time.sleep(1)
3665 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003666 queue.close()
3667 queue.join_thread()
3668
Vinay Sajipd61910c2016-09-08 01:13:39 +01003669 expected = [[], [logging.handlers.QueueListener._sentinel]]
3670 self.assertIn(items, expected,
3671 'Found unexpected messages in queue: %s' % (
3672 [m.msg if isinstance(m, logging.LogRecord)
3673 else m for m in items]))
3674
Bar Harel6b282e12019-06-01 12:19:09 +03003675 def test_calls_task_done_after_stop(self):
3676 # Issue 36813: Make sure queue.join does not deadlock.
3677 log_queue = queue.Queue()
3678 listener = logging.handlers.QueueListener(log_queue)
3679 listener.start()
3680 listener.stop()
3681 with self.assertRaises(ValueError):
3682 # Make sure all tasks are done and .join won't block.
3683 log_queue.task_done()
3684
Vinay Sajipe723e962011-04-15 22:27:17 +01003685
Vinay Sajip37eb3382011-04-26 20:26:41 +01003686ZERO = datetime.timedelta(0)
3687
3688class UTC(datetime.tzinfo):
3689 def utcoffset(self, dt):
3690 return ZERO
3691
3692 dst = utcoffset
3693
3694 def tzname(self, dt):
3695 return 'UTC'
3696
3697utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003698
Vinay Sajipa39c5712010-10-25 13:57:39 +00003699class FormatterTest(unittest.TestCase):
3700 def setUp(self):
3701 self.common = {
3702 'name': 'formatter.test',
3703 'level': logging.DEBUG,
3704 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3705 'lineno': 42,
3706 'exc_info': None,
3707 'func': None,
3708 'msg': 'Message with %d %s',
3709 'args': (2, 'placeholders'),
3710 }
3711 self.variants = {
3712 }
3713
3714 def get_record(self, name=None):
3715 result = dict(self.common)
3716 if name is not None:
3717 result.update(self.variants[name])
3718 return logging.makeLogRecord(result)
3719
BNMetrics18fb1fb2018-10-15 19:41:36 +01003720 def assert_error_message(self, exception, message, *args, **kwargs):
3721 try:
3722 self.assertRaises(exception, *args, **kwargs)
3723 except exception as e:
3724 self.assertEqual(message, e.message)
3725
Vinay Sajipa39c5712010-10-25 13:57:39 +00003726 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003727 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003728 r = self.get_record()
3729 f = logging.Formatter('${%(message)s}')
3730 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3731 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003732 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003733 self.assertFalse(f.usesTime())
3734 f = logging.Formatter('%(asctime)s')
3735 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003736 f = logging.Formatter('%(asctime)-15s')
3737 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003738 f = logging.Formatter('%(asctime)#15s')
3739 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003740
3741 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003742 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003743 r = self.get_record()
3744 f = logging.Formatter('$%{message}%$', style='{')
3745 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3746 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003747 self.assertRaises(ValueError, f.format, r)
3748 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003749 self.assertFalse(f.usesTime())
3750 f = logging.Formatter('{asctime}', style='{')
3751 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003752 f = logging.Formatter('{asctime!s:15}', style='{')
3753 self.assertTrue(f.usesTime())
3754 f = logging.Formatter('{asctime:15}', style='{')
3755 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003756
3757 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003758 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003759 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003760 f = logging.Formatter('${message}', style='$')
3761 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003762 f = logging.Formatter('$message', style='$')
3763 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3764 f = logging.Formatter('$$%${message}%$$', style='$')
3765 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3766 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003767 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003768 self.assertFalse(f.usesTime())
3769 f = logging.Formatter('${asctime}', style='$')
3770 self.assertTrue(f.usesTime())
3771 f = logging.Formatter('$asctime', style='$')
3772 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003773 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003774 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003775 f = logging.Formatter('${asctime}--', style='$')
3776 self.assertTrue(f.usesTime())
3777
3778 def test_format_validate(self):
3779 # Check correct formatting
3780 # Percentage style
3781 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3782 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3783 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3784 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3785 f = logging.Formatter("%(process)#+027.23X")
3786 self.assertEqual(f._fmt, "%(process)#+027.23X")
3787 f = logging.Formatter("%(foo)#.*g")
3788 self.assertEqual(f._fmt, "%(foo)#.*g")
3789
3790 # StrFormat Style
3791 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3792 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3793 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3794 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3795 f = logging.Formatter("{customfield!s:#<30}", style="{")
3796 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3797 f = logging.Formatter("{message!r}", style="{")
3798 self.assertEqual(f._fmt, "{message!r}")
3799 f = logging.Formatter("{message!s}", style="{")
3800 self.assertEqual(f._fmt, "{message!s}")
3801 f = logging.Formatter("{message!a}", style="{")
3802 self.assertEqual(f._fmt, "{message!a}")
3803 f = logging.Formatter("{process!r:4.2}", style="{")
3804 self.assertEqual(f._fmt, "{process!r:4.2}")
3805 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3806 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3807 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3808 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3809 f = logging.Formatter("{foo:12.{p}}", style="{")
3810 self.assertEqual(f._fmt, "{foo:12.{p}}")
3811 f = logging.Formatter("{foo:{w}.6}", style="{")
3812 self.assertEqual(f._fmt, "{foo:{w}.6}")
3813 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3814 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3815 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3816 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3817 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3818 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3819
3820 # Dollar style
3821 f = logging.Formatter("${asctime} - $message", style="$")
3822 self.assertEqual(f._fmt, "${asctime} - $message")
3823 f = logging.Formatter("$bar $$", style="$")
3824 self.assertEqual(f._fmt, "$bar $$")
3825 f = logging.Formatter("$bar $$$$", style="$")
3826 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3827
3828 # Testing when ValueError being raised from incorrect format
3829 # Percentage Style
3830 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3831 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3832 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3833 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3834 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3835 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3836 self.assertRaises(ValueError, logging.Formatter, '${message}')
3837 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3838 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3839
3840 # StrFormat Style
3841 # Testing failure for '-' in field name
3842 self.assert_error_message(
3843 ValueError,
3844 "invalid field name/expression: 'name-thing'",
3845 logging.Formatter, "{name-thing}", style="{"
3846 )
3847 # Testing failure for style mismatch
3848 self.assert_error_message(
3849 ValueError,
3850 "invalid format: no fields",
3851 logging.Formatter, '%(asctime)s', style='{'
3852 )
3853 # Testing failure for invalid conversion
3854 self.assert_error_message(
3855 ValueError,
3856 "invalid conversion: 'Z'"
3857 )
3858 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3859 self.assert_error_message(
3860 ValueError,
3861 "invalid format: expected ':' after conversion specifier",
3862 logging.Formatter, '{asctime!aa:15}', style='{'
3863 )
3864 # Testing failure for invalid spec
3865 self.assert_error_message(
3866 ValueError,
3867 "bad specifier: '.2ff'",
3868 logging.Formatter, '{process:.2ff}', style='{'
3869 )
3870 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3871 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3872 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3873 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3874 # Testing failure for mismatch braces
3875 self.assert_error_message(
3876 ValueError,
3877 "invalid format: unmatched '{' in format spec",
3878 logging.Formatter, '{process', style='{'
3879 )
3880 self.assert_error_message(
3881 ValueError,
3882 "invalid format: unmatched '{' in format spec",
3883 logging.Formatter, 'process}', style='{'
3884 )
3885 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3886 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3887 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3888 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3889 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3890 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3891 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3892 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3893 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3894
3895 # Dollar style
3896 # Testing failure for mismatch bare $
3897 self.assert_error_message(
3898 ValueError,
3899 "invalid format: bare \'$\' not allowed",
3900 logging.Formatter, '$bar $$$', style='$'
3901 )
3902 self.assert_error_message(
3903 ValueError,
3904 "invalid format: bare \'$\' not allowed",
3905 logging.Formatter, 'bar $', style='$'
3906 )
3907 self.assert_error_message(
3908 ValueError,
3909 "invalid format: bare \'$\' not allowed",
3910 logging.Formatter, 'foo $.', style='$'
3911 )
3912 # Testing failure for mismatch style
3913 self.assert_error_message(
3914 ValueError,
3915 "invalid format: no fields",
3916 logging.Formatter, '{asctime}', style='$'
3917 )
3918 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3919
3920 # Testing failure for incorrect fields
3921 self.assert_error_message(
3922 ValueError,
3923 "invalid format: no fields",
3924 logging.Formatter, 'foo', style='$'
3925 )
3926 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003927
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003928 def test_invalid_style(self):
3929 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3930
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003931 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003932 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003933 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3934 # We use None to indicate we want the local timezone
3935 # We're essentially converting a UTC time to local time
3936 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003937 r.msecs = 123
3938 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003939 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003940 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3941 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003942 f.format(r)
3943 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
3944
Mariusz Felisiak06a35542020-04-17 18:02:47 +02003945 def test_default_msec_format_none(self):
3946 class NoMsecFormatter(logging.Formatter):
3947 default_msec_format = None
3948 default_time_format = '%d/%m/%Y %H:%M:%S'
3949
3950 r = self.get_record()
3951 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
3952 r.created = time.mktime(dt.astimezone(None).timetuple())
3953 f = NoMsecFormatter()
3954 f.converter = time.gmtime
3955 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
3956
3957
Vinay Sajip985ef872011-04-26 19:34:04 +01003958class TestBufferingFormatter(logging.BufferingFormatter):
3959 def formatHeader(self, records):
3960 return '[(%d)' % len(records)
3961
3962 def formatFooter(self, records):
3963 return '(%d)]' % len(records)
3964
3965class BufferingFormatterTest(unittest.TestCase):
3966 def setUp(self):
3967 self.records = [
3968 logging.makeLogRecord({'msg': 'one'}),
3969 logging.makeLogRecord({'msg': 'two'}),
3970 ]
3971
3972 def test_default(self):
3973 f = logging.BufferingFormatter()
3974 self.assertEqual('', f.format([]))
3975 self.assertEqual('onetwo', f.format(self.records))
3976
3977 def test_custom(self):
3978 f = TestBufferingFormatter()
3979 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
3980 lf = logging.Formatter('<%(message)s>')
3981 f = TestBufferingFormatter(lf)
3982 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003983
3984class ExceptionTest(BaseTest):
3985 def test_formatting(self):
3986 r = self.root_logger
3987 h = RecordingHandler()
3988 r.addHandler(h)
3989 try:
3990 raise RuntimeError('deliberate mistake')
3991 except:
3992 logging.exception('failed', stack_info=True)
3993 r.removeHandler(h)
3994 h.close()
3995 r = h.records[0]
3996 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
3997 'call last):\n'))
3998 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
3999 'deliberate mistake'))
4000 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4001 'call last):\n'))
4002 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4003 'stack_info=True)'))
4004
4005
Vinay Sajip5a27d402010-12-10 11:42:57 +00004006class LastResortTest(BaseTest):
4007 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004008 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004009 root = self.root_logger
4010 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004011 old_lastresort = logging.lastResort
4012 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004013
Vinay Sajip5a27d402010-12-10 11:42:57 +00004014 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004015 with support.captured_stderr() as stderr:
4016 root.debug('This should not appear')
4017 self.assertEqual(stderr.getvalue(), '')
4018 root.warning('Final chance!')
4019 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4020
4021 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004022 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004023 with support.captured_stderr() as stderr:
4024 root.warning('Final chance!')
4025 msg = 'No handlers could be found for logger "root"\n'
4026 self.assertEqual(stderr.getvalue(), msg)
4027
Vinay Sajip5a27d402010-12-10 11:42:57 +00004028 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004029 with support.captured_stderr() as stderr:
4030 root.warning('Final chance!')
4031 self.assertEqual(stderr.getvalue(), '')
4032
4033 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004034 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004035 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004036 with support.captured_stderr() as stderr:
4037 root.warning('Final chance!')
4038 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004039 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004040 root.addHandler(self.root_hdlr)
4041 logging.lastResort = old_lastresort
4042 logging.raiseExceptions = old_raise_exceptions
4043
4044
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004045class FakeHandler:
4046
4047 def __init__(self, identifier, called):
4048 for method in ('acquire', 'flush', 'close', 'release'):
4049 setattr(self, method, self.record_call(identifier, method, called))
4050
4051 def record_call(self, identifier, method_name, called):
4052 def inner():
4053 called.append('{} - {}'.format(identifier, method_name))
4054 return inner
4055
4056
4057class RecordingHandler(logging.NullHandler):
4058
4059 def __init__(self, *args, **kwargs):
4060 super(RecordingHandler, self).__init__(*args, **kwargs)
4061 self.records = []
4062
4063 def handle(self, record):
4064 """Keep track of all the emitted records."""
4065 self.records.append(record)
4066
4067
4068class ShutdownTest(BaseTest):
4069
Vinay Sajip5e66b162011-04-20 15:41:14 +01004070 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004071
4072 def setUp(self):
4073 super(ShutdownTest, self).setUp()
4074 self.called = []
4075
4076 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004077 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004078
4079 def raise_error(self, error):
4080 def inner():
4081 raise error()
4082 return inner
4083
4084 def test_no_failure(self):
4085 # create some fake handlers
4086 handler0 = FakeHandler(0, self.called)
4087 handler1 = FakeHandler(1, self.called)
4088 handler2 = FakeHandler(2, self.called)
4089
4090 # create live weakref to those handlers
4091 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4092
4093 logging.shutdown(handlerList=list(handlers))
4094
4095 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4096 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4097 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4098 self.assertEqual(expected, self.called)
4099
4100 def _test_with_failure_in_method(self, method, error):
4101 handler = FakeHandler(0, self.called)
4102 setattr(handler, method, self.raise_error(error))
4103 handlers = [logging.weakref.ref(handler)]
4104
4105 logging.shutdown(handlerList=list(handlers))
4106
4107 self.assertEqual('0 - release', self.called[-1])
4108
4109 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004110 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004111
4112 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004113 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004114
4115 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004116 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004117
4118 def test_with_valueerror_in_acquire(self):
4119 self._test_with_failure_in_method('acquire', ValueError)
4120
4121 def test_with_valueerror_in_flush(self):
4122 self._test_with_failure_in_method('flush', ValueError)
4123
4124 def test_with_valueerror_in_close(self):
4125 self._test_with_failure_in_method('close', ValueError)
4126
4127 def test_with_other_error_in_acquire_without_raise(self):
4128 logging.raiseExceptions = False
4129 self._test_with_failure_in_method('acquire', IndexError)
4130
4131 def test_with_other_error_in_flush_without_raise(self):
4132 logging.raiseExceptions = False
4133 self._test_with_failure_in_method('flush', IndexError)
4134
4135 def test_with_other_error_in_close_without_raise(self):
4136 logging.raiseExceptions = False
4137 self._test_with_failure_in_method('close', IndexError)
4138
4139 def test_with_other_error_in_acquire_with_raise(self):
4140 logging.raiseExceptions = True
4141 self.assertRaises(IndexError, self._test_with_failure_in_method,
4142 'acquire', IndexError)
4143
4144 def test_with_other_error_in_flush_with_raise(self):
4145 logging.raiseExceptions = True
4146 self.assertRaises(IndexError, self._test_with_failure_in_method,
4147 'flush', IndexError)
4148
4149 def test_with_other_error_in_close_with_raise(self):
4150 logging.raiseExceptions = True
4151 self.assertRaises(IndexError, self._test_with_failure_in_method,
4152 'close', IndexError)
4153
4154
4155class ModuleLevelMiscTest(BaseTest):
4156
Vinay Sajip5e66b162011-04-20 15:41:14 +01004157 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004158
4159 def test_disable(self):
4160 old_disable = logging.root.manager.disable
4161 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004162 self.assertEqual(old_disable, 0)
4163 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004164
4165 logging.disable(83)
4166 self.assertEqual(logging.root.manager.disable, 83)
4167
Vinay Sajipd489ac92016-12-31 11:40:11 +00004168 # test the default value introduced in 3.7
4169 # (Issue #28524)
4170 logging.disable()
4171 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4172
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004173 def _test_log(self, method, level=None):
4174 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004175 support.patch(self, logging, 'basicConfig',
4176 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004177
4178 recording = RecordingHandler()
4179 logging.root.addHandler(recording)
4180
4181 log_method = getattr(logging, method)
4182 if level is not None:
4183 log_method(level, "test me: %r", recording)
4184 else:
4185 log_method("test me: %r", recording)
4186
4187 self.assertEqual(len(recording.records), 1)
4188 record = recording.records[0]
4189 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4190
4191 expected_level = level if level is not None else getattr(logging, method.upper())
4192 self.assertEqual(record.levelno, expected_level)
4193
4194 # basicConfig was not called!
4195 self.assertEqual(called, [])
4196
4197 def test_log(self):
4198 self._test_log('log', logging.ERROR)
4199
4200 def test_debug(self):
4201 self._test_log('debug')
4202
4203 def test_info(self):
4204 self._test_log('info')
4205
4206 def test_warning(self):
4207 self._test_log('warning')
4208
4209 def test_error(self):
4210 self._test_log('error')
4211
4212 def test_critical(self):
4213 self._test_log('critical')
4214
4215 def test_set_logger_class(self):
4216 self.assertRaises(TypeError, logging.setLoggerClass, object)
4217
4218 class MyLogger(logging.Logger):
4219 pass
4220
4221 logging.setLoggerClass(MyLogger)
4222 self.assertEqual(logging.getLoggerClass(), MyLogger)
4223
4224 logging.setLoggerClass(logging.Logger)
4225 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4226
Vinay Sajip01500012019-06-19 11:46:53 +01004227 def test_subclass_logger_cache(self):
4228 # bpo-37258
4229 message = []
4230
4231 class MyLogger(logging.getLoggerClass()):
4232 def __init__(self, name='MyLogger', level=logging.NOTSET):
4233 super().__init__(name, level)
4234 message.append('initialized')
4235
4236 logging.setLoggerClass(MyLogger)
4237 logger = logging.getLogger('just_some_logger')
4238 self.assertEqual(message, ['initialized'])
4239 stream = io.StringIO()
4240 h = logging.StreamHandler(stream)
4241 logger.addHandler(h)
4242 try:
4243 logger.setLevel(logging.DEBUG)
4244 logger.debug("hello")
4245 self.assertEqual(stream.getvalue().strip(), "hello")
4246
4247 stream.truncate(0)
4248 stream.seek(0)
4249
4250 logger.setLevel(logging.INFO)
4251 logger.debug("hello")
4252 self.assertEqual(stream.getvalue(), "")
4253 finally:
4254 logger.removeHandler(h)
4255 h.close()
4256 logging.setLoggerClass(logging.Logger)
4257
Antoine Pitrou712cb732013-12-21 15:51:54 +01004258 def test_logging_at_shutdown(self):
4259 # Issue #20037
4260 code = """if 1:
4261 import logging
4262
4263 class A:
4264 def __del__(self):
4265 try:
4266 raise ValueError("some error")
4267 except Exception:
4268 logging.exception("exception in __del__")
4269
4270 a = A()"""
4271 rc, out, err = assert_python_ok("-c", code)
4272 err = err.decode()
4273 self.assertIn("exception in __del__", err)
4274 self.assertIn("ValueError: some error", err)
4275
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004276 def test_recursion_error(self):
4277 # Issue 36272
4278 code = """if 1:
4279 import logging
4280
4281 def rec():
4282 logging.error("foo")
4283 rec()
4284
4285 rec()"""
4286 rc, out, err = assert_python_failure("-c", code)
4287 err = err.decode()
4288 self.assertNotIn("Cannot recover from stack overflow.", err)
4289 self.assertEqual(rc, 1)
4290
Antoine Pitrou712cb732013-12-21 15:51:54 +01004291
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004292class LogRecordTest(BaseTest):
4293 def test_str_rep(self):
4294 r = logging.makeLogRecord({})
4295 s = str(r)
4296 self.assertTrue(s.startswith('<LogRecord: '))
4297 self.assertTrue(s.endswith('>'))
4298
4299 def test_dict_arg(self):
4300 h = RecordingHandler()
4301 r = logging.getLogger()
4302 r.addHandler(h)
4303 d = {'less' : 'more' }
4304 logging.warning('less is %(less)s', d)
4305 self.assertIs(h.records[0].args, d)
4306 self.assertEqual(h.records[0].message, 'less is more')
4307 r.removeHandler(h)
4308 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004309
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004310 def test_multiprocessing(self):
4311 r = logging.makeLogRecord({})
4312 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004313 try:
4314 import multiprocessing as mp
4315 r = logging.makeLogRecord({})
4316 self.assertEqual(r.processName, mp.current_process().name)
4317 except ImportError:
4318 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004319
4320 def test_optional(self):
4321 r = logging.makeLogRecord({})
4322 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004323 NOT_NONE(r.thread)
4324 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004325 NOT_NONE(r.process)
4326 NOT_NONE(r.processName)
4327 log_threads = logging.logThreads
4328 log_processes = logging.logProcesses
4329 log_multiprocessing = logging.logMultiprocessing
4330 try:
4331 logging.logThreads = False
4332 logging.logProcesses = False
4333 logging.logMultiprocessing = False
4334 r = logging.makeLogRecord({})
4335 NONE = self.assertIsNone
4336 NONE(r.thread)
4337 NONE(r.threadName)
4338 NONE(r.process)
4339 NONE(r.processName)
4340 finally:
4341 logging.logThreads = log_threads
4342 logging.logProcesses = log_processes
4343 logging.logMultiprocessing = log_multiprocessing
4344
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004345class BasicConfigTest(unittest.TestCase):
4346
Vinay Sajip95bf5042011-04-20 11:50:56 +01004347 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004348
4349 def setUp(self):
4350 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004351 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004352 self.saved_handlers = logging._handlers.copy()
4353 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004354 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004355 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004356 logging.root.handlers = []
4357
4358 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004359 for h in logging.root.handlers[:]:
4360 logging.root.removeHandler(h)
4361 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004362 super(BasicConfigTest, self).tearDown()
4363
Vinay Sajip3def7e02011-04-20 10:58:06 +01004364 def cleanup(self):
4365 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004366 logging._handlers.clear()
4367 logging._handlers.update(self.saved_handlers)
4368 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004369 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004370
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004371 def test_no_kwargs(self):
4372 logging.basicConfig()
4373
4374 # handler defaults to a StreamHandler to sys.stderr
4375 self.assertEqual(len(logging.root.handlers), 1)
4376 handler = logging.root.handlers[0]
4377 self.assertIsInstance(handler, logging.StreamHandler)
4378 self.assertEqual(handler.stream, sys.stderr)
4379
4380 formatter = handler.formatter
4381 # format defaults to logging.BASIC_FORMAT
4382 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4383 # datefmt defaults to None
4384 self.assertIsNone(formatter.datefmt)
4385 # style defaults to %
4386 self.assertIsInstance(formatter._style, logging.PercentStyle)
4387
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004388 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004389 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004390
Vinay Sajip1fd12022014-01-13 21:59:56 +00004391 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004392 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004393 logging.basicConfig(stream=sys.stdout, style="{")
4394 logging.error("Log an error")
4395 sys.stdout.seek(0)
4396 self.assertEqual(output.getvalue().strip(),
4397 "ERROR:root:Log an error")
4398
4399 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004400 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004401 logging.basicConfig(stream=sys.stdout, style="$")
4402 logging.error("Log an error")
4403 sys.stdout.seek(0)
4404 self.assertEqual(output.getvalue().strip(),
4405 "ERROR:root:Log an error")
4406
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004407 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004408
4409 def cleanup(h1, h2, fn):
4410 h1.close()
4411 h2.close()
4412 os.remove(fn)
4413
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004414 logging.basicConfig(filename='test.log')
4415
4416 self.assertEqual(len(logging.root.handlers), 1)
4417 handler = logging.root.handlers[0]
4418 self.assertIsInstance(handler, logging.FileHandler)
4419
4420 expected = logging.FileHandler('test.log', 'a')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004421 self.assertEqual(handler.stream.mode, expected.stream.mode)
4422 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004423 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004424
4425 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004426
4427 def cleanup(h1, h2, fn):
4428 h1.close()
4429 h2.close()
4430 os.remove(fn)
4431
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004432 logging.basicConfig(filename='test.log', filemode='wb')
4433
4434 handler = logging.root.handlers[0]
4435 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004436 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004437 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004438
4439 def test_stream(self):
4440 stream = io.StringIO()
4441 self.addCleanup(stream.close)
4442 logging.basicConfig(stream=stream)
4443
4444 self.assertEqual(len(logging.root.handlers), 1)
4445 handler = logging.root.handlers[0]
4446 self.assertIsInstance(handler, logging.StreamHandler)
4447 self.assertEqual(handler.stream, stream)
4448
4449 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004450 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004451
4452 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004453 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004454
4455 def test_datefmt(self):
4456 logging.basicConfig(datefmt='bar')
4457
4458 formatter = logging.root.handlers[0].formatter
4459 self.assertEqual(formatter.datefmt, 'bar')
4460
4461 def test_style(self):
4462 logging.basicConfig(style='$')
4463
4464 formatter = logging.root.handlers[0].formatter
4465 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4466
4467 def test_level(self):
4468 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004469 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004470
4471 logging.basicConfig(level=57)
4472 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004473 # Test that second call has no effect
4474 logging.basicConfig(level=58)
4475 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004476
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004477 def test_incompatible(self):
4478 assertRaises = self.assertRaises
4479 handlers = [logging.StreamHandler()]
4480 stream = sys.stderr
4481 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004482 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004483 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004484 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004485 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004486 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004487 # Issue 23207: test for invalid kwargs
4488 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4489 # Should pop both filename and filemode even if filename is None
4490 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004491
4492 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004493 handlers = [
4494 logging.StreamHandler(),
4495 logging.StreamHandler(sys.stdout),
4496 logging.StreamHandler(),
4497 ]
4498 f = logging.Formatter()
4499 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004500 logging.basicConfig(handlers=handlers)
4501 self.assertIs(handlers[0], logging.root.handlers[0])
4502 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004503 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004504 self.assertIsNotNone(handlers[0].formatter)
4505 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004506 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004507 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4508
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004509 def test_force(self):
4510 old_string_io = io.StringIO()
4511 new_string_io = io.StringIO()
4512 old_handlers = [logging.StreamHandler(old_string_io)]
4513 new_handlers = [logging.StreamHandler(new_string_io)]
4514 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4515 logging.warning('warn')
4516 logging.info('info')
4517 logging.debug('debug')
4518 self.assertEqual(len(logging.root.handlers), 1)
4519 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4520 force=True)
4521 logging.warning('warn')
4522 logging.info('info')
4523 logging.debug('debug')
4524 self.assertEqual(len(logging.root.handlers), 1)
4525 self.assertEqual(old_string_io.getvalue().strip(),
4526 'WARNING:root:warn')
4527 self.assertEqual(new_string_io.getvalue().strip(),
4528 'WARNING:root:warn\nINFO:root:info')
4529
Vinay Sajipca7b5042019-06-17 17:40:52 +01004530 def test_encoding(self):
4531 try:
4532 encoding = 'utf-8'
4533 logging.basicConfig(filename='test.log', encoding=encoding,
4534 errors='strict',
4535 format='%(message)s', level=logging.DEBUG)
4536
4537 self.assertEqual(len(logging.root.handlers), 1)
4538 handler = logging.root.handlers[0]
4539 self.assertIsInstance(handler, logging.FileHandler)
4540 self.assertEqual(handler.encoding, encoding)
4541 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4542 finally:
4543 handler.close()
4544 with open('test.log', encoding='utf-8') as f:
4545 data = f.read().strip()
4546 os.remove('test.log')
4547 self.assertEqual(data,
4548 'The Øresund Bridge joins Copenhagen to Malmö')
4549
4550 def test_encoding_errors(self):
4551 try:
4552 encoding = 'ascii'
4553 logging.basicConfig(filename='test.log', encoding=encoding,
4554 errors='ignore',
4555 format='%(message)s', level=logging.DEBUG)
4556
4557 self.assertEqual(len(logging.root.handlers), 1)
4558 handler = logging.root.handlers[0]
4559 self.assertIsInstance(handler, logging.FileHandler)
4560 self.assertEqual(handler.encoding, encoding)
4561 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4562 finally:
4563 handler.close()
4564 with open('test.log', encoding='utf-8') as f:
4565 data = f.read().strip()
4566 os.remove('test.log')
4567 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4568
4569 def test_encoding_errors_default(self):
4570 try:
4571 encoding = 'ascii'
4572 logging.basicConfig(filename='test.log', encoding=encoding,
4573 format='%(message)s', level=logging.DEBUG)
4574
4575 self.assertEqual(len(logging.root.handlers), 1)
4576 handler = logging.root.handlers[0]
4577 self.assertIsInstance(handler, logging.FileHandler)
4578 self.assertEqual(handler.encoding, encoding)
4579 self.assertEqual(handler.errors, 'backslashreplace')
4580 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4581 finally:
4582 handler.close()
4583 with open('test.log', encoding='utf-8') as f:
4584 data = f.read().strip()
4585 os.remove('test.log')
4586 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4587 r'Bridge joins Copenhagen to Malm\xf6')
4588
4589 def test_encoding_errors_none(self):
4590 # Specifying None should behave as 'strict'
4591 try:
4592 encoding = 'ascii'
4593 logging.basicConfig(filename='test.log', encoding=encoding,
4594 errors=None,
4595 format='%(message)s', level=logging.DEBUG)
4596
4597 self.assertEqual(len(logging.root.handlers), 1)
4598 handler = logging.root.handlers[0]
4599 self.assertIsInstance(handler, logging.FileHandler)
4600 self.assertEqual(handler.encoding, encoding)
4601 self.assertIsNone(handler.errors)
4602
4603 message = []
4604
4605 def dummy_handle_error(record):
4606 _, v, _ = sys.exc_info()
4607 message.append(str(v))
4608
4609 handler.handleError = dummy_handle_error
4610 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4611 self.assertTrue(message)
4612 self.assertIn("'ascii' codec can't encode "
4613 "character '\\xd8' in position 4:", message[0])
4614 finally:
4615 handler.close()
4616 with open('test.log', encoding='utf-8') as f:
4617 data = f.read().strip()
4618 os.remove('test.log')
4619 # didn't write anything due to the encoding error
4620 self.assertEqual(data, r'')
4621
4622
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004623 def _test_log(self, method, level=None):
4624 # logging.root has no handlers so basicConfig should be called
4625 called = []
4626
4627 old_basic_config = logging.basicConfig
4628 def my_basic_config(*a, **kw):
4629 old_basic_config()
4630 old_level = logging.root.level
4631 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004632 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004633 called.append((a, kw))
4634
Vinay Sajip1feedb42014-05-31 08:19:12 +01004635 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004636
4637 log_method = getattr(logging, method)
4638 if level is not None:
4639 log_method(level, "test me")
4640 else:
4641 log_method("test me")
4642
4643 # basicConfig was called with no arguments
4644 self.assertEqual(called, [((), {})])
4645
4646 def test_log(self):
4647 self._test_log('log', logging.WARNING)
4648
4649 def test_debug(self):
4650 self._test_log('debug')
4651
4652 def test_info(self):
4653 self._test_log('info')
4654
4655 def test_warning(self):
4656 self._test_log('warning')
4657
4658 def test_error(self):
4659 self._test_log('error')
4660
4661 def test_critical(self):
4662 self._test_log('critical')
4663
4664
4665class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004666 def setUp(self):
4667 super(LoggerAdapterTest, self).setUp()
4668 old_handler_list = logging._handlerList[:]
4669
4670 self.recording = RecordingHandler()
4671 self.logger = logging.root
4672 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004673 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004674 self.addCleanup(self.recording.close)
4675
4676 def cleanup():
4677 logging._handlerList[:] = old_handler_list
4678
4679 self.addCleanup(cleanup)
4680 self.addCleanup(logging.shutdown)
4681 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4682
4683 def test_exception(self):
4684 msg = 'testing exception: %r'
4685 exc = None
4686 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004687 1 / 0
4688 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004689 exc = e
4690 self.adapter.exception(msg, self.recording)
4691
4692 self.assertEqual(len(self.recording.records), 1)
4693 record = self.recording.records[0]
4694 self.assertEqual(record.levelno, logging.ERROR)
4695 self.assertEqual(record.msg, msg)
4696 self.assertEqual(record.args, (self.recording,))
4697 self.assertEqual(record.exc_info,
4698 (exc.__class__, exc, exc.__traceback__))
4699
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004700 def test_exception_excinfo(self):
4701 try:
4702 1 / 0
4703 except ZeroDivisionError as e:
4704 exc = e
4705
4706 self.adapter.exception('exc_info test', exc_info=exc)
4707
4708 self.assertEqual(len(self.recording.records), 1)
4709 record = self.recording.records[0]
4710 self.assertEqual(record.exc_info,
4711 (exc.__class__, exc, exc.__traceback__))
4712
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004713 def test_critical(self):
4714 msg = 'critical test! %r'
4715 self.adapter.critical(msg, self.recording)
4716
4717 self.assertEqual(len(self.recording.records), 1)
4718 record = self.recording.records[0]
4719 self.assertEqual(record.levelno, logging.CRITICAL)
4720 self.assertEqual(record.msg, msg)
4721 self.assertEqual(record.args, (self.recording,))
4722
4723 def test_is_enabled_for(self):
4724 old_disable = self.adapter.logger.manager.disable
4725 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004726 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4727 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004728 self.assertFalse(self.adapter.isEnabledFor(32))
4729
4730 def test_has_handlers(self):
4731 self.assertTrue(self.adapter.hasHandlers())
4732
4733 for handler in self.logger.handlers:
4734 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004735
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004736 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004737 self.assertFalse(self.adapter.hasHandlers())
4738
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004739 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004740 class Adapter(logging.LoggerAdapter):
4741 prefix = 'Adapter'
4742
4743 def process(self, msg, kwargs):
4744 return f"{self.prefix} {msg}", kwargs
4745
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004746 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004747 adapter = Adapter(logger=self.logger, extra=None)
4748 adapter_adapter = Adapter(logger=adapter, extra=None)
4749 adapter_adapter.prefix = 'AdapterAdapter'
4750 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004751 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004752 self.assertEqual(len(self.recording.records), 1)
4753 record = self.recording.records[0]
4754 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004755 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004756 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004757 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004758 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004759 self.assertIs(self.logger.manager, orig_manager)
4760 temp_manager = object()
4761 try:
4762 adapter_adapter.manager = temp_manager
4763 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004764 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004765 self.assertIs(self.logger.manager, temp_manager)
4766 finally:
4767 adapter_adapter.manager = orig_manager
4768 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004769 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004770 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004771
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004772
4773class LoggerTest(BaseTest):
4774
4775 def setUp(self):
4776 super(LoggerTest, self).setUp()
4777 self.recording = RecordingHandler()
4778 self.logger = logging.Logger(name='blah')
4779 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004780 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004781 self.addCleanup(self.recording.close)
4782 self.addCleanup(logging.shutdown)
4783
4784 def test_set_invalid_level(self):
4785 self.assertRaises(TypeError, self.logger.setLevel, object())
4786
4787 def test_exception(self):
4788 msg = 'testing exception: %r'
4789 exc = None
4790 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004791 1 / 0
4792 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004793 exc = e
4794 self.logger.exception(msg, self.recording)
4795
4796 self.assertEqual(len(self.recording.records), 1)
4797 record = self.recording.records[0]
4798 self.assertEqual(record.levelno, logging.ERROR)
4799 self.assertEqual(record.msg, msg)
4800 self.assertEqual(record.args, (self.recording,))
4801 self.assertEqual(record.exc_info,
4802 (exc.__class__, exc, exc.__traceback__))
4803
4804 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004805 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004806 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004807
4808 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004809 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004810 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004811
4812 def test_find_caller_with_stack_info(self):
4813 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004814 support.patch(self, logging.traceback, 'print_stack',
4815 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004816
4817 self.logger.findCaller(stack_info=True)
4818
4819 self.assertEqual(len(called), 1)
4820 self.assertEqual('Stack (most recent call last):\n', called[0])
4821
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004822 def test_find_caller_with_stacklevel(self):
4823 the_level = 1
4824
4825 def innermost():
4826 self.logger.warning('test', stacklevel=the_level)
4827
4828 def inner():
4829 innermost()
4830
4831 def outer():
4832 inner()
4833
4834 records = self.recording.records
4835 outer()
4836 self.assertEqual(records[-1].funcName, 'innermost')
4837 lineno = records[-1].lineno
4838 the_level += 1
4839 outer()
4840 self.assertEqual(records[-1].funcName, 'inner')
4841 self.assertGreater(records[-1].lineno, lineno)
4842 lineno = records[-1].lineno
4843 the_level += 1
4844 outer()
4845 self.assertEqual(records[-1].funcName, 'outer')
4846 self.assertGreater(records[-1].lineno, lineno)
4847 lineno = records[-1].lineno
4848 the_level += 1
4849 outer()
4850 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
4851 self.assertGreater(records[-1].lineno, lineno)
4852
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004853 def test_make_record_with_extra_overwrite(self):
4854 name = 'my record'
4855 level = 13
4856 fn = lno = msg = args = exc_info = func = sinfo = None
4857 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
4858 exc_info, func, sinfo)
4859
4860 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
4861 extra = {key: 'some value'}
4862 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
4863 fn, lno, msg, args, exc_info,
4864 extra=extra, sinfo=sinfo)
4865
4866 def test_make_record_with_extra_no_overwrite(self):
4867 name = 'my record'
4868 level = 13
4869 fn = lno = msg = args = exc_info = func = sinfo = None
4870 extra = {'valid_key': 'some value'}
4871 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
4872 exc_info, extra=extra, sinfo=sinfo)
4873 self.assertIn('valid_key', result.__dict__)
4874
4875 def test_has_handlers(self):
4876 self.assertTrue(self.logger.hasHandlers())
4877
4878 for handler in self.logger.handlers:
4879 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004880 self.assertFalse(self.logger.hasHandlers())
4881
4882 def test_has_handlers_no_propagate(self):
4883 child_logger = logging.getLogger('blah.child')
4884 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004885 self.assertFalse(child_logger.hasHandlers())
4886
4887 def test_is_enabled_for(self):
4888 old_disable = self.logger.manager.disable
4889 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004890 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004891 self.assertFalse(self.logger.isEnabledFor(22))
4892
Timo Furrer6e3ca642018-06-01 09:29:46 +02004893 def test_is_enabled_for_disabled_logger(self):
4894 old_disabled = self.logger.disabled
4895 old_disable = self.logger.manager.disable
4896
4897 self.logger.disabled = True
4898 self.logger.manager.disable = 21
4899
4900 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
4901 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
4902
4903 self.assertFalse(self.logger.isEnabledFor(22))
4904
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004905 def test_root_logger_aliases(self):
4906 root = logging.getLogger()
4907 self.assertIs(root, logging.root)
4908 self.assertIs(root, logging.getLogger(None))
4909 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01004910 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004911 self.assertIs(root, logging.getLogger('foo').root)
4912 self.assertIs(root, logging.getLogger('foo.bar').root)
4913 self.assertIs(root, logging.getLogger('foo').parent)
4914
4915 self.assertIsNot(root, logging.getLogger('\0'))
4916 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
4917
4918 def test_invalid_names(self):
4919 self.assertRaises(TypeError, logging.getLogger, any)
4920 self.assertRaises(TypeError, logging.getLogger, b'foo')
4921
Vinay Sajip6260d9f2017-06-06 16:34:29 +01004922 def test_pickling(self):
4923 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
4924 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
4925 logger = logging.getLogger(name)
4926 s = pickle.dumps(logger, proto)
4927 unpickled = pickle.loads(s)
4928 self.assertIs(unpickled, logger)
4929
Avram Lubkin78c18a92017-07-30 05:36:33 -04004930 def test_caching(self):
4931 root = self.root_logger
4932 logger1 = logging.getLogger("abc")
4933 logger2 = logging.getLogger("abc.def")
4934
4935 # Set root logger level and ensure cache is empty
4936 root.setLevel(logging.ERROR)
4937 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
4938 self.assertEqual(logger2._cache, {})
4939
4940 # Ensure cache is populated and calls are consistent
4941 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4942 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
4943 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
4944 self.assertEqual(root._cache, {})
4945 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4946
4947 # Ensure root cache gets populated
4948 self.assertEqual(root._cache, {})
4949 self.assertTrue(root.isEnabledFor(logging.ERROR))
4950 self.assertEqual(root._cache, {logging.ERROR: True})
4951
4952 # Set parent logger level and ensure caches are emptied
4953 logger1.setLevel(logging.CRITICAL)
4954 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4955 self.assertEqual(logger2._cache, {})
4956
4957 # Ensure logger2 uses parent logger's effective level
4958 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4959
4960 # Set level to NOTSET and ensure caches are empty
4961 logger2.setLevel(logging.NOTSET)
4962 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4963 self.assertEqual(logger2._cache, {})
4964 self.assertEqual(logger1._cache, {})
4965 self.assertEqual(root._cache, {})
4966
4967 # Verify logger2 follows parent and not root
4968 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4969 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
4970 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
4971 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
4972 self.assertTrue(root.isEnabledFor(logging.ERROR))
4973
4974 # Disable logging in manager and ensure caches are clear
4975 logging.disable()
4976 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4977 self.assertEqual(logger2._cache, {})
4978 self.assertEqual(logger1._cache, {})
4979 self.assertEqual(root._cache, {})
4980
4981 # Ensure no loggers are enabled
4982 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
4983 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
4984 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
4985
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004986
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004987class BaseFileTest(BaseTest):
4988 "Base class for handler tests that write log files"
4989
4990 def setUp(self):
4991 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00004992 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
4993 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004994 self.rmfiles = []
4995
4996 def tearDown(self):
4997 for fn in self.rmfiles:
4998 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00004999 if os.path.exists(self.fn):
5000 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005001 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005002
5003 def assertLogFile(self, filename):
5004 "Assert a log file is there and register it for deletion"
5005 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005006 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005007 self.rmfiles.append(filename)
5008
5009
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005010class FileHandlerTest(BaseFileTest):
5011 def test_delay(self):
5012 os.unlink(self.fn)
5013 fh = logging.FileHandler(self.fn, delay=True)
5014 self.assertIsNone(fh.stream)
5015 self.assertFalse(os.path.exists(self.fn))
5016 fh.handle(logging.makeLogRecord({}))
5017 self.assertIsNotNone(fh.stream)
5018 self.assertTrue(os.path.exists(self.fn))
5019 fh.close()
5020
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005021class RotatingFileHandlerTest(BaseFileTest):
5022 def next_rec(self):
5023 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5024 self.next_message(), None, None, None)
5025
5026 def test_should_not_rollover(self):
5027 # If maxbytes is zero rollover never occurs
5028 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
5029 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005030 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005031
5032 def test_should_rollover(self):
5033 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
5034 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005035 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005036
5037 def test_file_created(self):
5038 # checks that the file is created and assumes it was created
5039 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005040 rh = logging.handlers.RotatingFileHandler(self.fn)
5041 rh.emit(self.next_rec())
5042 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005043 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005044
5045 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005046 def namer(name):
5047 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005048 rh = logging.handlers.RotatingFileHandler(
5049 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005050 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005051 rh.emit(self.next_rec())
5052 self.assertLogFile(self.fn)
5053 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005054 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005055 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005056 self.assertLogFile(namer(self.fn + ".2"))
5057 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5058 rh.close()
5059
l0rb519cb872019-11-06 22:21:40 +01005060 def test_namer_rotator_inheritance(self):
5061 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5062 def namer(self, name):
5063 return name + ".test"
5064
5065 def rotator(self, source, dest):
5066 if os.path.exists(source):
5067 os.rename(source, dest + ".rotated")
5068
5069 rh = HandlerWithNamerAndRotator(
5070 self.fn, backupCount=2, maxBytes=1)
5071 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5072 rh.emit(self.next_rec())
5073 self.assertLogFile(self.fn)
5074 rh.emit(self.next_rec())
5075 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5076 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5077 rh.close()
5078
Hai Shia3ec3ad2020-05-19 06:02:57 +08005079 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005080 def test_rotator(self):
5081 def namer(name):
5082 return name + ".gz"
5083
5084 def rotator(source, dest):
5085 with open(source, "rb") as sf:
5086 data = sf.read()
5087 compressed = zlib.compress(data, 9)
5088 with open(dest, "wb") as df:
5089 df.write(compressed)
5090 os.remove(source)
5091
5092 rh = logging.handlers.RotatingFileHandler(
5093 self.fn, backupCount=2, maxBytes=1)
5094 rh.rotator = rotator
5095 rh.namer = namer
5096 m1 = self.next_rec()
5097 rh.emit(m1)
5098 self.assertLogFile(self.fn)
5099 m2 = self.next_rec()
5100 rh.emit(m2)
5101 fn = namer(self.fn + ".1")
5102 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005103 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005104 with open(fn, "rb") as f:
5105 compressed = f.read()
5106 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005107 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005108 rh.emit(self.next_rec())
5109 fn = namer(self.fn + ".2")
5110 self.assertLogFile(fn)
5111 with open(fn, "rb") as f:
5112 compressed = f.read()
5113 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005114 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005115 rh.emit(self.next_rec())
5116 fn = namer(self.fn + ".2")
5117 with open(fn, "rb") as f:
5118 compressed = f.read()
5119 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005120 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005121 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005122 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005123
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005124class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005125 # other test methods added below
5126 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01005127 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
5128 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005129 fmt = logging.Formatter('%(asctime)s %(message)s')
5130 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005131 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005132 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005133 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005134 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005135 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005136 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005137 fh.close()
5138 # At this point, we should have a recent rotated file which we
5139 # can test for the existence of. However, in practice, on some
5140 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005141 # in time to go to look for the log file. So, we go back a fair
5142 # bit, and stop as soon as we see a rotated file. In theory this
5143 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005144 found = False
5145 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005146 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005147 for secs in range(GO_BACK):
5148 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005149 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5150 found = os.path.exists(fn)
5151 if found:
5152 self.rmfiles.append(fn)
5153 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005154 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5155 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005156 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005157 dn, fn = os.path.split(self.fn)
5158 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005159 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5160 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005161 for f in files:
5162 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005163 path = os.path.join(dn, f)
5164 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005165 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005166 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005167
Vinay Sajip0372e102011-05-05 12:59:14 +01005168 def test_invalid(self):
5169 assertRaises = self.assertRaises
5170 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005171 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005172 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005173 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005174 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005175 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005176
Vinay Sajipa7130792013-04-12 17:04:23 +01005177 def test_compute_rollover_daily_attime(self):
5178 currentTime = 0
5179 atTime = datetime.time(12, 0, 0)
5180 rh = logging.handlers.TimedRotatingFileHandler(
5181 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
5182 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005183 try:
5184 actual = rh.computeRollover(currentTime)
5185 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005186
Vinay Sajipd86ac962013-04-14 12:20:46 +01005187 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5188 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5189 finally:
5190 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005191
Vinay Sajip10e8c492013-05-18 10:19:54 -07005192 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005193 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005194 currentTime = int(time.time())
5195 today = currentTime - currentTime % 86400
5196
Vinay Sajipa7130792013-04-12 17:04:23 +01005197 atTime = datetime.time(12, 0, 0)
5198
Vinay Sajip10e8c492013-05-18 10:19:54 -07005199 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005200 for day in range(7):
5201 rh = logging.handlers.TimedRotatingFileHandler(
5202 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
5203 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005204 try:
5205 if wday > day:
5206 # The rollover day has already passed this week, so we
5207 # go over into next week
5208 expected = (7 - wday + day)
5209 else:
5210 expected = (day - wday)
5211 # At this point expected is in days from now, convert to seconds
5212 expected *= 24 * 60 * 60
5213 # Add in the rollover time
5214 expected += 12 * 60 * 60
5215 # Add in adjustment for today
5216 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005217 actual = rh.computeRollover(today)
5218 if actual != expected:
5219 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005220 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005221 self.assertEqual(actual, expected)
5222 if day == wday:
5223 # goes into following week
5224 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005225 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005226 if actual != expected:
5227 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005228 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005229 self.assertEqual(actual, expected)
5230 finally:
5231 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005232
5233
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005234def secs(**kw):
5235 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5236
5237for when, exp in (('S', 1),
5238 ('M', 60),
5239 ('H', 60 * 60),
5240 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005241 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005242 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005243 ('W0', secs(days=4, hours=24)),
5244 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005245 def test_compute_rollover(self, when=when, exp=exp):
5246 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00005247 self.fn, when=when, interval=1, backupCount=0, utc=True)
5248 currentTime = 0.0
5249 actual = rh.computeRollover(currentTime)
5250 if exp != actual:
5251 # Failures occur on some systems for MIDNIGHT and W0.
5252 # Print detailed calculation for MIDNIGHT so we can try to see
5253 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005254 if when == 'MIDNIGHT':
5255 try:
5256 if rh.utc:
5257 t = time.gmtime(currentTime)
5258 else:
5259 t = time.localtime(currentTime)
5260 currentHour = t[3]
5261 currentMinute = t[4]
5262 currentSecond = t[5]
5263 # r is the number of seconds left between now and midnight
5264 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5265 currentMinute) * 60 +
5266 currentSecond)
5267 result = currentTime + r
5268 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5269 print('currentHour: %s' % currentHour, file=sys.stderr)
5270 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5271 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5272 print('r: %s' % r, file=sys.stderr)
5273 print('result: %s' % result, file=sys.stderr)
5274 except Exception:
5275 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5276 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005277 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005278 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5279
Vinay Sajip60ccd822011-05-09 17:32:09 +01005280
Vinay Sajip223349c2015-10-01 20:37:54 +01005281@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005282class NTEventLogHandlerTest(BaseTest):
5283 def test_basic(self):
5284 logtype = 'Application'
5285 elh = win32evtlog.OpenEventLog(None, logtype)
5286 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005287
5288 try:
5289 h = logging.handlers.NTEventLogHandler('test_logging')
5290 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005291 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005292 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005293 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005294
Vinay Sajip60ccd822011-05-09 17:32:09 +01005295 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5296 h.handle(r)
5297 h.close()
5298 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005299 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005300 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5301 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5302 found = False
5303 GO_BACK = 100
5304 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5305 for e in events:
5306 if e.SourceName != 'test_logging':
5307 continue
5308 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5309 if msg != 'Test Log Message\r\n':
5310 continue
5311 found = True
5312 break
5313 msg = 'Record not found in event log, went back %d records' % GO_BACK
5314 self.assertTrue(found, msg=msg)
5315
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005316
5317class MiscTestCase(unittest.TestCase):
5318 def test__all__(self):
5319 blacklist = {'logThreads', 'logMultiprocessing',
5320 'logProcesses', 'currentframe',
5321 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5322 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger',
Martin Panter94332cb2016-10-20 05:10:44 +00005323 'root', 'threading'}
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005324 support.check__all__(self, logging, blacklist=blacklist)
5325
5326
Christian Heimes180510d2008-03-03 19:15:45 +00005327# Set the locale to the platform-dependent default. I have no idea
5328# why the test does this, but in any case we save the current locale
5329# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005330@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005331def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005332 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005333 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5334 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5335 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5336 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5337 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5338 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5339 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5340 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005341 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005342 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005343 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005344 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005345 ]
5346 if hasattr(logging.handlers, 'QueueListener'):
5347 tests.append(QueueListenerTest)
5348 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005349
Christian Heimes180510d2008-03-03 19:15:45 +00005350if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005351 test_main()