blob: e719d264a919148591499bb945d7001db2e7c5e2 [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
Hai Shie80697d2020-05-28 06:10:27 +080046from test.support import threading_helper
Serhiy Storchaka515fce42020-04-25 11:35:18 +030047from test.support.logging_helper import TestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000048import textwrap
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020049import threading
Vinay Sajip37eb3382011-04-26 20:26:41 +010050import time
Christian Heimes180510d2008-03-03 19:15:45 +000051import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000052import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000053import weakref
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020054
55import asyncore
56from http.server import HTTPServer, BaseHTTPRequestHandler
57import smtpd
58from urllib.parse import urlparse, parse_qs
59from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
60 ThreadingTCPServer, StreamRequestHandler)
61
Vinay Sajip60ccd822011-05-09 17:32:09 +010062try:
Vinay Sajip223349c2015-10-01 20:37:54 +010063 import win32evtlog, win32evtlogutil, pywintypes
Vinay Sajip60ccd822011-05-09 17:32:09 +010064except ImportError:
Vinay Sajip223349c2015-10-01 20:37:54 +010065 win32evtlog = win32evtlogutil = pywintypes = None
66
Eric V. Smith851cad72012-03-11 22:46:04 -070067try:
68 import zlib
69except ImportError:
70 pass
Christian Heimes18c66892008-02-17 13:31:39 +000071
Christian Heimes180510d2008-03-03 19:15:45 +000072class BaseTest(unittest.TestCase):
73
74 """Base class for logging tests."""
75
76 log_format = "%(name)s -> %(levelname)s: %(message)s"
Ezio Melotti3ffd29b2012-10-02 19:45:00 +030077 expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +000078 message_num = 0
79
80 def setUp(self):
81 """Setup the default logging stream to an internal StringIO instance,
82 so that we can examine log output as we want."""
Hai Shie80697d2020-05-28 06:10:27 +080083 self._threading_key = threading_helper.threading_setup()
Victor Stinner69669602017-08-18 23:47:54 +020084
Christian Heimes180510d2008-03-03 19:15:45 +000085 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000086 logging._acquireLock()
87 try:
Christian Heimes180510d2008-03-03 19:15:45 +000088 self.saved_handlers = logging._handlers.copy()
89 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000090 self.saved_loggers = saved_loggers = logger_dict.copy()
Vinay Sajip3b84eae2013-05-25 03:20:34 -070091 self.saved_name_to_level = logging._nameToLevel.copy()
92 self.saved_level_to_name = logging._levelToName.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000093 self.logger_states = logger_states = {}
94 for name in saved_loggers:
95 logger_states[name] = getattr(saved_loggers[name],
96 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000097 finally:
98 logging._releaseLock()
99
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100100 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000101 self.logger1 = logging.getLogger("\xab\xd7\xbb")
102 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000103
Christian Heimes180510d2008-03-03 19:15:45 +0000104 self.root_logger = logging.getLogger("")
105 self.original_logging_level = self.root_logger.getEffectiveLevel()
106
107 self.stream = io.StringIO()
108 self.root_logger.setLevel(logging.DEBUG)
109 self.root_hdlr = logging.StreamHandler(self.stream)
110 self.root_formatter = logging.Formatter(self.log_format)
111 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000112 if self.logger1.hasHandlers():
113 hlist = self.logger1.handlers + self.root_logger.handlers
114 raise AssertionError('Unexpected handlers: %s' % hlist)
115 if self.logger2.hasHandlers():
116 hlist = self.logger2.handlers + self.root_logger.handlers
117 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000118 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000119 self.assertTrue(self.logger1.hasHandlers())
120 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000121
122 def tearDown(self):
123 """Remove our logging stream, and restore the original logging
124 level."""
125 self.stream.close()
126 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000127 while self.root_logger.handlers:
128 h = self.root_logger.handlers[0]
129 self.root_logger.removeHandler(h)
130 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000131 self.root_logger.setLevel(self.original_logging_level)
132 logging._acquireLock()
133 try:
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700134 logging._levelToName.clear()
135 logging._levelToName.update(self.saved_level_to_name)
136 logging._nameToLevel.clear()
137 logging._nameToLevel.update(self.saved_name_to_level)
Christian Heimes180510d2008-03-03 19:15:45 +0000138 logging._handlers.clear()
139 logging._handlers.update(self.saved_handlers)
140 logging._handlerList[:] = self.saved_handler_list
Avram Lubkin78c18a92017-07-30 05:36:33 -0400141 manager = logging.getLogger().manager
142 manager.disable = 0
143 loggerDict = manager.loggerDict
Christian Heimes180510d2008-03-03 19:15:45 +0000144 loggerDict.clear()
145 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000146 logger_states = self.logger_states
147 for name in self.logger_states:
148 if logger_states[name] is not None:
149 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000150 finally:
151 logging._releaseLock()
152
Victor Stinner69669602017-08-18 23:47:54 +0200153 self.doCleanups()
Hai Shie80697d2020-05-28 06:10:27 +0800154 threading_helper.threading_cleanup(*self._threading_key)
Victor Stinner69669602017-08-18 23:47:54 +0200155
Vinay Sajip4ded5512012-10-02 15:56:16 +0100156 def assert_log_lines(self, expected_values, stream=None, pat=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000157 """Match the collected log lines against the regular expression
158 self.expected_log_pat, and compare the extracted group values to
159 the expected_values list of tuples."""
160 stream = stream or self.stream
Vinay Sajip4ded5512012-10-02 15:56:16 +0100161 pat = re.compile(pat or self.expected_log_pat)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300162 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100163 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000164 for actual, expected in zip(actual_lines, expected_values):
165 match = pat.search(actual)
166 if not match:
167 self.fail("Log line does not match expected pattern:\n" +
168 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000169 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000170 s = stream.read()
171 if s:
172 self.fail("Remaining output at end of log stream:\n" + s)
173
174 def next_message(self):
175 """Generate a message consisting solely of an auto-incrementing
176 integer."""
177 self.message_num += 1
178 return "%d" % self.message_num
179
180
181class BuiltinLevelsTest(BaseTest):
182 """Test builtin levels and their inheritance."""
183
184 def test_flat(self):
Mike53f7a7c2017-12-14 14:04:53 +0300185 # Logging levels in a flat logger namespace.
Christian Heimes180510d2008-03-03 19:15:45 +0000186 m = self.next_message
187
188 ERR = logging.getLogger("ERR")
189 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000190 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000191 INF.setLevel(logging.INFO)
192 DEB = logging.getLogger("DEB")
193 DEB.setLevel(logging.DEBUG)
194
195 # These should log.
196 ERR.log(logging.CRITICAL, m())
197 ERR.error(m())
198
199 INF.log(logging.CRITICAL, m())
200 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100201 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000202 INF.info(m())
203
204 DEB.log(logging.CRITICAL, m())
205 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100206 DEB.warning(m())
207 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000208 DEB.debug(m())
209
210 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100211 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000212 ERR.info(m())
213 ERR.debug(m())
214
215 INF.debug(m())
216
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000217 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000218 ('ERR', 'CRITICAL', '1'),
219 ('ERR', 'ERROR', '2'),
220 ('INF', 'CRITICAL', '3'),
221 ('INF', 'ERROR', '4'),
222 ('INF', 'WARNING', '5'),
223 ('INF', 'INFO', '6'),
224 ('DEB', 'CRITICAL', '7'),
225 ('DEB', 'ERROR', '8'),
226 ('DEB', 'WARNING', '9'),
227 ('DEB', 'INFO', '10'),
228 ('DEB', 'DEBUG', '11'),
229 ])
230
231 def test_nested_explicit(self):
232 # Logging levels in a nested namespace, all explicitly set.
233 m = self.next_message
234
235 INF = logging.getLogger("INF")
236 INF.setLevel(logging.INFO)
237 INF_ERR = logging.getLogger("INF.ERR")
238 INF_ERR.setLevel(logging.ERROR)
239
240 # These should log.
241 INF_ERR.log(logging.CRITICAL, m())
242 INF_ERR.error(m())
243
244 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100245 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000246 INF_ERR.info(m())
247 INF_ERR.debug(m())
248
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000249 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000250 ('INF.ERR', 'CRITICAL', '1'),
251 ('INF.ERR', 'ERROR', '2'),
252 ])
253
254 def test_nested_inherited(self):
Mike53f7a7c2017-12-14 14:04:53 +0300255 # Logging levels in a nested namespace, inherited from parent loggers.
Christian Heimes180510d2008-03-03 19:15:45 +0000256 m = self.next_message
257
258 INF = logging.getLogger("INF")
259 INF.setLevel(logging.INFO)
260 INF_ERR = logging.getLogger("INF.ERR")
261 INF_ERR.setLevel(logging.ERROR)
262 INF_UNDEF = logging.getLogger("INF.UNDEF")
263 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
264 UNDEF = logging.getLogger("UNDEF")
265
266 # These should log.
267 INF_UNDEF.log(logging.CRITICAL, m())
268 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100269 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000270 INF_UNDEF.info(m())
271 INF_ERR_UNDEF.log(logging.CRITICAL, m())
272 INF_ERR_UNDEF.error(m())
273
274 # These should not log.
275 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100276 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000277 INF_ERR_UNDEF.info(m())
278 INF_ERR_UNDEF.debug(m())
279
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000280 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000281 ('INF.UNDEF', 'CRITICAL', '1'),
282 ('INF.UNDEF', 'ERROR', '2'),
283 ('INF.UNDEF', 'WARNING', '3'),
284 ('INF.UNDEF', 'INFO', '4'),
285 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
286 ('INF.ERR.UNDEF', 'ERROR', '6'),
287 ])
288
289 def test_nested_with_virtual_parent(self):
290 # Logging levels when some parent does not exist yet.
291 m = self.next_message
292
293 INF = logging.getLogger("INF")
294 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
295 CHILD = logging.getLogger("INF.BADPARENT")
296 INF.setLevel(logging.INFO)
297
298 # These should log.
299 GRANDCHILD.log(logging.FATAL, m())
300 GRANDCHILD.info(m())
301 CHILD.log(logging.FATAL, m())
302 CHILD.info(m())
303
304 # These should not log.
305 GRANDCHILD.debug(m())
306 CHILD.debug(m())
307
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000308 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000309 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
310 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
311 ('INF.BADPARENT', 'CRITICAL', '3'),
312 ('INF.BADPARENT', 'INFO', '4'),
313 ])
314
Vinay Sajipd1d4fbf2014-09-11 23:06:09 +0100315 def test_regression_22386(self):
316 """See issue #22386 for more information."""
317 self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
318 self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
Christian Heimes180510d2008-03-03 19:15:45 +0000319
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100320 def test_issue27935(self):
321 fatal = logging.getLevelName('FATAL')
322 self.assertEqual(fatal, logging.FATAL)
323
Vinay Sajip924aaae2017-01-11 17:35:36 +0000324 def test_regression_29220(self):
325 """See issue #29220 for more information."""
326 logging.addLevelName(logging.INFO, '')
327 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
328 self.assertEqual(logging.getLevelName(logging.INFO), '')
329 self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
330 self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
331
Christian Heimes180510d2008-03-03 19:15:45 +0000332class BasicFilterTest(BaseTest):
333
334 """Test the bundled Filter class."""
335
336 def test_filter(self):
337 # Only messages satisfying the specified criteria pass through the
338 # filter.
339 filter_ = logging.Filter("spam.eggs")
340 handler = self.root_logger.handlers[0]
341 try:
342 handler.addFilter(filter_)
343 spam = logging.getLogger("spam")
344 spam_eggs = logging.getLogger("spam.eggs")
345 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
346 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
347
348 spam.info(self.next_message())
349 spam_eggs.info(self.next_message()) # Good.
350 spam_eggs_fish.info(self.next_message()) # Good.
351 spam_bakedbeans.info(self.next_message())
352
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000353 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000354 ('spam.eggs', 'INFO', '2'),
355 ('spam.eggs.fish', 'INFO', '3'),
356 ])
357 finally:
358 handler.removeFilter(filter_)
359
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000360 def test_callable_filter(self):
361 # Only messages satisfying the specified criteria pass through the
362 # filter.
363
364 def filterfunc(record):
365 parts = record.name.split('.')
366 prefix = '.'.join(parts[:2])
367 return prefix == 'spam.eggs'
368
369 handler = self.root_logger.handlers[0]
370 try:
371 handler.addFilter(filterfunc)
372 spam = logging.getLogger("spam")
373 spam_eggs = logging.getLogger("spam.eggs")
374 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
375 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
376
377 spam.info(self.next_message())
378 spam_eggs.info(self.next_message()) # Good.
379 spam_eggs_fish.info(self.next_message()) # Good.
380 spam_bakedbeans.info(self.next_message())
381
382 self.assert_log_lines([
383 ('spam.eggs', 'INFO', '2'),
384 ('spam.eggs.fish', 'INFO', '3'),
385 ])
386 finally:
387 handler.removeFilter(filterfunc)
388
Vinay Sajip985ef872011-04-26 19:34:04 +0100389 def test_empty_filter(self):
390 f = logging.Filter()
391 r = logging.makeLogRecord({'name': 'spam.eggs'})
392 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000393
394#
395# First, we define our levels. There can be as many as you want - the only
396# limitations are that they should be integers, the lowest should be > 0 and
397# larger values mean less information being logged. If you need specific
398# level values which do not fit into these limitations, you can use a
399# mapping dictionary to convert between your application levels and the
400# logging system.
401#
402SILENT = 120
403TACITURN = 119
404TERSE = 118
405EFFUSIVE = 117
406SOCIABLE = 116
407VERBOSE = 115
408TALKATIVE = 114
409GARRULOUS = 113
410CHATTERBOX = 112
411BORING = 111
412
413LEVEL_RANGE = range(BORING, SILENT + 1)
414
415#
416# Next, we define names for our levels. You don't need to do this - in which
417# case the system will use "Level n" to denote the text for the level.
418#
419my_logging_levels = {
420 SILENT : 'Silent',
421 TACITURN : 'Taciturn',
422 TERSE : 'Terse',
423 EFFUSIVE : 'Effusive',
424 SOCIABLE : 'Sociable',
425 VERBOSE : 'Verbose',
426 TALKATIVE : 'Talkative',
427 GARRULOUS : 'Garrulous',
428 CHATTERBOX : 'Chatterbox',
429 BORING : 'Boring',
430}
431
432class GarrulousFilter(logging.Filter):
433
434 """A filter which blocks garrulous messages."""
435
436 def filter(self, record):
437 return record.levelno != GARRULOUS
438
439class VerySpecificFilter(logging.Filter):
440
441 """A filter which blocks sociable and taciturn messages."""
442
443 def filter(self, record):
444 return record.levelno not in [SOCIABLE, TACITURN]
445
446
447class CustomLevelsAndFiltersTest(BaseTest):
448
449 """Test various filtering possibilities with custom logging levels."""
450
451 # Skip the logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +0300452 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +0000453
454 def setUp(self):
455 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000456 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000457 logging.addLevelName(k, v)
458
459 def log_at_all_levels(self, logger):
460 for lvl in LEVEL_RANGE:
461 logger.log(lvl, self.next_message())
462
463 def test_logger_filter(self):
464 # Filter at logger level.
465 self.root_logger.setLevel(VERBOSE)
466 # Levels >= 'Verbose' are good.
467 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000468 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000469 ('Verbose', '5'),
470 ('Sociable', '6'),
471 ('Effusive', '7'),
472 ('Terse', '8'),
473 ('Taciturn', '9'),
474 ('Silent', '10'),
475 ])
476
477 def test_handler_filter(self):
478 # Filter at handler level.
479 self.root_logger.handlers[0].setLevel(SOCIABLE)
480 try:
481 # Levels >= 'Sociable' are good.
482 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000483 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000484 ('Sociable', '6'),
485 ('Effusive', '7'),
486 ('Terse', '8'),
487 ('Taciturn', '9'),
488 ('Silent', '10'),
489 ])
490 finally:
491 self.root_logger.handlers[0].setLevel(logging.NOTSET)
492
493 def test_specific_filters(self):
494 # Set a specific filter object on the handler, and then add another
495 # filter object on the logger itself.
496 handler = self.root_logger.handlers[0]
497 specific_filter = None
498 garr = GarrulousFilter()
499 handler.addFilter(garr)
500 try:
501 self.log_at_all_levels(self.root_logger)
502 first_lines = [
503 # Notice how 'Garrulous' is missing
504 ('Boring', '1'),
505 ('Chatterbox', '2'),
506 ('Talkative', '4'),
507 ('Verbose', '5'),
508 ('Sociable', '6'),
509 ('Effusive', '7'),
510 ('Terse', '8'),
511 ('Taciturn', '9'),
512 ('Silent', '10'),
513 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000514 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000515
516 specific_filter = VerySpecificFilter()
517 self.root_logger.addFilter(specific_filter)
518 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000519 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000520 # Not only 'Garrulous' is still missing, but also 'Sociable'
521 # and 'Taciturn'
522 ('Boring', '11'),
523 ('Chatterbox', '12'),
524 ('Talkative', '14'),
525 ('Verbose', '15'),
526 ('Effusive', '17'),
527 ('Terse', '18'),
528 ('Silent', '20'),
529 ])
530 finally:
531 if specific_filter:
532 self.root_logger.removeFilter(specific_filter)
533 handler.removeFilter(garr)
534
535
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100536class HandlerTest(BaseTest):
537 def test_name(self):
538 h = logging.Handler()
539 h.name = 'generic'
540 self.assertEqual(h.name, 'generic')
541 h.name = 'anothergeneric'
542 self.assertEqual(h.name, 'anothergeneric')
543 self.assertRaises(NotImplementedError, h.emit, None)
544
Vinay Sajip5a35b062011-04-27 11:31:14 +0100545 def test_builtin_handlers(self):
546 # We can't actually *use* too many handlers in the tests,
547 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200548 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100549 for existing in (True, False):
550 fd, fn = tempfile.mkstemp()
551 os.close(fd)
552 if not existing:
553 os.unlink(fn)
554 h = logging.handlers.WatchedFileHandler(fn, delay=True)
555 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100556 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100557 self.assertEqual(dev, -1)
558 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100559 r = logging.makeLogRecord({'msg': 'Test'})
560 h.handle(r)
561 # Now remove the file.
562 os.unlink(fn)
563 self.assertFalse(os.path.exists(fn))
564 # The next call should recreate the file.
565 h.handle(r)
566 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100567 else:
568 self.assertEqual(h.dev, -1)
569 self.assertEqual(h.ino, -1)
570 h.close()
571 if existing:
572 os.unlink(fn)
573 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100574 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100575 else:
576 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100577 try:
578 h = logging.handlers.SysLogHandler(sockname)
579 self.assertEqual(h.facility, h.LOG_USER)
580 self.assertTrue(h.unixsocket)
581 h.close()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200582 except OSError: # syslogd might not be available
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100583 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100584 for method in ('GET', 'POST', 'PUT'):
585 if method == 'PUT':
586 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
587 'localhost', '/log', method)
588 else:
589 h = logging.handlers.HTTPHandler('localhost', '/log', method)
590 h.close()
591 h = logging.handlers.BufferingHandler(0)
592 r = logging.makeLogRecord({})
593 self.assertTrue(h.shouldFlush(r))
594 h.close()
595 h = logging.handlers.BufferingHandler(1)
596 self.assertFalse(h.shouldFlush(r))
597 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100598
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100599 def test_path_objects(self):
600 """
601 Test that Path objects are accepted as filename arguments to handlers.
602
603 See Issue #27493.
604 """
605 fd, fn = tempfile.mkstemp()
606 os.close(fd)
607 os.unlink(fn)
608 pfn = pathlib.Path(fn)
609 cases = (
610 (logging.FileHandler, (pfn, 'w')),
611 (logging.handlers.RotatingFileHandler, (pfn, 'a')),
612 (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
613 )
614 if sys.platform in ('linux', 'darwin'):
615 cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
616 for cls, args in cases:
617 h = cls(*args)
618 self.assertTrue(os.path.exists(fn))
619 h.close()
620 os.unlink(fn)
621
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100622 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100623 def test_race(self):
624 # Issue #14632 refers.
625 def remove_loop(fname, tries):
626 for _ in range(tries):
627 try:
628 os.unlink(fname)
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000629 self.deletion_time = time.time()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100630 except OSError:
631 pass
632 time.sleep(0.004 * random.randint(0, 4))
633
Vinay Sajipc94871a2012-04-25 10:51:35 +0100634 del_count = 500
635 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100636
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000637 self.handle_time = None
638 self.deletion_time = None
639
Vinay Sajipa5798de2012-04-24 23:33:33 +0100640 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100641 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
642 os.close(fd)
643 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
644 remover.daemon = True
645 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100646 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100647 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
648 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100649 try:
650 for _ in range(log_count):
651 time.sleep(0.005)
652 r = logging.makeLogRecord({'msg': 'testing' })
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000653 try:
654 self.handle_time = time.time()
655 h.handle(r)
656 except Exception:
657 print('Deleted at %s, '
658 'opened at %s' % (self.deletion_time,
659 self.handle_time))
660 raise
Vinay Sajipc94871a2012-04-25 10:51:35 +0100661 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100662 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100663 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100664 if os.path.exists(fn):
665 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100666
Gregory P. Smith19003842018-09-13 22:08:31 -0700667 # The implementation relies on os.register_at_fork existing, but we test
668 # based on os.fork existing because that is what users and this test use.
669 # This helps ensure that when fork exists (the important concept) that the
670 # register_at_fork mechanism is also present and used.
671 @unittest.skipIf(not hasattr(os, 'fork'), 'Test requires os.fork().')
672 def test_post_fork_child_no_deadlock(self):
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400673 """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
674 class _OurHandler(logging.Handler):
675 def __init__(self):
676 super().__init__()
677 self.sub_handler = logging.StreamHandler(
678 stream=open('/dev/null', 'wt'))
679
680 def emit(self, record):
681 self.sub_handler.acquire()
682 try:
683 self.sub_handler.emit(record)
684 finally:
685 self.sub_handler.release()
686
687 self.assertEqual(len(logging._handlers), 0)
688 refed_h = _OurHandler()
Xtreak2c105382019-05-13 20:18:52 +0530689 self.addCleanup(refed_h.sub_handler.stream.close)
Gregory P. Smith19003842018-09-13 22:08:31 -0700690 refed_h.name = 'because we need at least one for this test'
691 self.assertGreater(len(logging._handlers), 0)
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400692 self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
693 test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
694 test_logger.addHandler(refed_h)
695 test_logger.setLevel(logging.DEBUG)
Gregory P. Smith19003842018-09-13 22:08:31 -0700696
697 locks_held__ready_to_fork = threading.Event()
698 fork_happened__release_locks_and_end_thread = threading.Event()
699
700 def lock_holder_thread_fn():
701 logging._acquireLock()
702 try:
703 refed_h.acquire()
704 try:
705 # Tell the main thread to do the fork.
706 locks_held__ready_to_fork.set()
707
708 # If the deadlock bug exists, the fork will happen
709 # without dealing with the locks we hold, deadlocking
710 # the child.
711
712 # Wait for a successful fork or an unreasonable amount of
713 # time before releasing our locks. To avoid a timing based
714 # test we'd need communication from os.fork() as to when it
715 # has actually happened. Given this is a regression test
716 # for a fixed issue, potentially less reliably detecting
717 # regression via timing is acceptable for simplicity.
718 # The test will always take at least this long. :(
719 fork_happened__release_locks_and_end_thread.wait(0.5)
720 finally:
721 refed_h.release()
722 finally:
723 logging._releaseLock()
724
725 lock_holder_thread = threading.Thread(
726 target=lock_holder_thread_fn,
727 name='test_post_fork_child_no_deadlock lock holder')
728 lock_holder_thread.start()
729
730 locks_held__ready_to_fork.wait()
731 pid = os.fork()
Victor Stinner278c1e12020-03-31 20:08:12 +0200732 if pid == 0:
733 # Child process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400734 try:
735 test_logger.info(r'Child process did not deadlock. \o/')
736 finally:
737 os._exit(0)
Victor Stinner278c1e12020-03-31 20:08:12 +0200738 else:
739 # Parent process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400740 test_logger.info(r'Parent process returned from fork. \o/')
Gregory P. Smith19003842018-09-13 22:08:31 -0700741 fork_happened__release_locks_and_end_thread.set()
742 lock_holder_thread.join()
Victor Stinner278c1e12020-03-31 20:08:12 +0200743
744 support.wait_process(pid, exitcode=0)
Gregory P. Smith19003842018-09-13 22:08:31 -0700745
Vinay Sajipa5798de2012-04-24 23:33:33 +0100746
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100747class BadStream(object):
748 def write(self, data):
749 raise RuntimeError('deliberate mistake')
750
751class TestStreamHandler(logging.StreamHandler):
752 def handleError(self, record):
753 self.error_record = record
754
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200755class StreamWithIntName(object):
756 level = logging.NOTSET
757 name = 2
758
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100759class StreamHandlerTest(BaseTest):
760 def test_error_handling(self):
761 h = TestStreamHandler(BadStream())
762 r = logging.makeLogRecord({})
763 old_raise = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +0100764
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100765 try:
766 h.handle(r)
767 self.assertIs(h.error_record, r)
Vinay Sajip1feedb42014-05-31 08:19:12 +0100768
Vinay Sajip985ef872011-04-26 19:34:04 +0100769 h = logging.StreamHandler(BadStream())
Vinay Sajip1feedb42014-05-31 08:19:12 +0100770 with support.captured_stderr() as stderr:
771 h.handle(r)
772 msg = '\nRuntimeError: deliberate mistake\n'
773 self.assertIn(msg, stderr.getvalue())
774
Vinay Sajip985ef872011-04-26 19:34:04 +0100775 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +0100776 with support.captured_stderr() as stderr:
777 h.handle(r)
778 self.assertEqual('', stderr.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100779 finally:
780 logging.raiseExceptions = old_raise
781
Vinay Sajip2543f502017-07-30 10:41:45 +0100782 def test_stream_setting(self):
783 """
784 Test setting the handler's stream
785 """
786 h = logging.StreamHandler()
787 stream = io.StringIO()
788 old = h.setStream(stream)
789 self.assertIs(old, sys.stderr)
790 actual = h.setStream(old)
791 self.assertIs(actual, stream)
792 # test that setting to existing value returns None
793 actual = h.setStream(old)
794 self.assertIsNone(actual)
795
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200796 def test_can_represent_stream_with_int_name(self):
797 h = logging.StreamHandler(StreamWithIntName())
798 self.assertEqual(repr(h), '<StreamHandler 2 (NOTSET)>')
799
Vinay Sajip7367d082011-05-02 13:17:27 +0100800# -- The following section could be moved into a server_helper.py module
801# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100802
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200803class TestSMTPServer(smtpd.SMTPServer):
804 """
805 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100806
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200807 :param addr: A (host, port) tuple which the server listens on.
808 You can specify a port value of zero: the server's
809 *port* attribute will hold the actual port number
810 used, which can be used in client connections.
811 :param handler: A callable which will be called to process
812 incoming messages. The handler will be passed
813 the client address tuple, who the message is from,
814 a list of recipients and the message data.
815 :param poll_interval: The interval, in seconds, used in the underlying
816 :func:`select` or :func:`poll` call by
817 :func:`asyncore.loop`.
818 :param sockmap: A dictionary which will be used to hold
819 :class:`asyncore.dispatcher` instances used by
820 :func:`asyncore.loop`. This avoids changing the
821 :mod:`asyncore` module's global state.
822 """
823
824 def __init__(self, addr, handler, poll_interval, sockmap):
825 smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
826 decode_data=True)
827 self.port = self.socket.getsockname()[1]
828 self._handler = handler
829 self._thread = None
830 self.poll_interval = poll_interval
831
832 def process_message(self, peer, mailfrom, rcpttos, data):
833 """
834 Delegates to the handler passed in to the server's constructor.
835
836 Typically, this will be a test case method.
837 :param peer: The client (host, port) tuple.
838 :param mailfrom: The address of the sender.
839 :param rcpttos: The addresses of the recipients.
840 :param data: The message.
841 """
842 self._handler(peer, mailfrom, rcpttos, data)
843
844 def start(self):
845 """
846 Start the server running on a separate daemon thread.
847 """
848 self._thread = t = threading.Thread(target=self.serve_forever,
849 args=(self.poll_interval,))
850 t.setDaemon(True)
851 t.start()
852
853 def serve_forever(self, poll_interval):
854 """
855 Run the :mod:`asyncore` loop until normal termination
856 conditions arise.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100857 :param poll_interval: The interval, in seconds, used in the underlying
858 :func:`select` or :func:`poll` call by
859 :func:`asyncore.loop`.
860 """
Victor Stinner13ff2452018-01-22 18:32:50 +0100861 asyncore.loop(poll_interval, map=self._map)
Vinay Sajipce7c9782011-05-17 07:15:53 +0100862
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100863 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200864 """
865 Stop the thread by closing the server instance.
866 Wait for the server thread to terminate.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200867 """
868 self.close()
Hai Shie80697d2020-05-28 06:10:27 +0800869 threading_helper.join_thread(self._thread)
Victor Stinnerb9b69002017-09-14 14:40:56 -0700870 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200871 asyncore.close_all(map=self._map, ignore_all=True)
Vinay Sajipa463d252011-04-30 21:52:48 +0100872
Vinay Sajip7367d082011-05-02 13:17:27 +0100873
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200874class ControlMixin(object):
875 """
876 This mixin is used to start a server on a separate thread, and
877 shut it down programmatically. Request handling is simplified - instead
878 of needing to derive a suitable RequestHandler subclass, you just
879 provide a callable which will be passed each received request to be
880 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100881
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200882 :param handler: A handler callable which will be called with a
883 single parameter - the request - in order to
884 process the request. This handler is called on the
885 server thread, effectively meaning that requests are
886 processed serially. While not quite Web scale ;-),
887 this should be fine for testing applications.
888 :param poll_interval: The polling interval in seconds.
889 """
890 def __init__(self, handler, poll_interval):
891 self._thread = None
892 self.poll_interval = poll_interval
893 self._handler = handler
894 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100895
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200896 def start(self):
897 """
898 Create a daemon thread to run the server, and start it.
899 """
900 self._thread = t = threading.Thread(target=self.serve_forever,
901 args=(self.poll_interval,))
902 t.setDaemon(True)
903 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100904
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200905 def serve_forever(self, poll_interval):
906 """
907 Run the server. Set the ready flag before entering the
908 service loop.
909 """
910 self.ready.set()
911 super(ControlMixin, self).serve_forever(poll_interval)
912
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100913 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200914 """
915 Tell the server thread to stop, and wait for it to do so.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200916 """
917 self.shutdown()
918 if self._thread is not None:
Hai Shie80697d2020-05-28 06:10:27 +0800919 threading_helper.join_thread(self._thread)
Vinay Sajip7367d082011-05-02 13:17:27 +0100920 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200921 self.server_close()
922 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100923
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200924class TestHTTPServer(ControlMixin, HTTPServer):
925 """
926 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100927
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200928 :param addr: A tuple with the IP address and port to listen on.
929 :param handler: A handler callable which will be called with a
930 single parameter - the request - in order to
931 process the request.
932 :param poll_interval: The polling interval in seconds.
933 :param log: Pass ``True`` to enable log messages.
934 """
935 def __init__(self, addr, handler, poll_interval=0.5,
936 log=False, sslctx=None):
937 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
938 def __getattr__(self, name, default=None):
939 if name.startswith('do_'):
940 return self.process_request
941 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100942
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200943 def process_request(self):
944 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100945
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200946 def log_message(self, format, *args):
947 if log:
948 super(DelegatingHTTPRequestHandler,
949 self).log_message(format, *args)
950 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
951 ControlMixin.__init__(self, handler, poll_interval)
952 self.sslctx = sslctx
Vinay Sajip7367d082011-05-02 13:17:27 +0100953
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200954 def get_request(self):
955 try:
956 sock, addr = self.socket.accept()
957 if self.sslctx:
958 sock = self.sslctx.wrap_socket(sock, server_side=True)
959 except OSError as e:
960 # socket errors are silenced by the caller, print them here
961 sys.stderr.write("Got an error:\n%s\n" % e)
962 raise
963 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100964
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200965class TestTCPServer(ControlMixin, ThreadingTCPServer):
966 """
967 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100968
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200969 :param addr: A tuple with the IP address and port to listen on.
970 :param handler: A handler callable which will be called with a single
971 parameter - the request - in order to process the request.
972 :param poll_interval: The polling interval in seconds.
973 :bind_and_activate: If True (the default), binds the server and starts it
974 listening. If False, you need to call
975 :meth:`server_bind` and :meth:`server_activate` at
976 some later time before calling :meth:`start`, so that
977 the server will set up the socket and listen on it.
978 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100979
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200980 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100981
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200982 def __init__(self, addr, handler, poll_interval=0.5,
983 bind_and_activate=True):
984 class DelegatingTCPRequestHandler(StreamRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +0100985
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200986 def handle(self):
987 self.server._handler(self)
988 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
989 bind_and_activate)
990 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100991
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200992 def server_bind(self):
993 super(TestTCPServer, self).server_bind()
994 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100995
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200996class TestUDPServer(ControlMixin, ThreadingUDPServer):
997 """
998 A UDP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100999
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001000 :param addr: A tuple with the IP address and port to listen on.
1001 :param handler: A handler callable which will be called with a
1002 single parameter - the request - in order to
1003 process the request.
1004 :param poll_interval: The polling interval for shutdown requests,
1005 in seconds.
1006 :bind_and_activate: If True (the default), binds the server and
1007 starts it listening. If False, you need to
1008 call :meth:`server_bind` and
1009 :meth:`server_activate` at some later time
1010 before calling :meth:`start`, so that the server will
1011 set up the socket and listen on it.
1012 """
1013 def __init__(self, addr, handler, poll_interval=0.5,
1014 bind_and_activate=True):
1015 class DelegatingUDPRequestHandler(DatagramRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +01001016
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001017 def handle(self):
1018 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001019
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001020 def finish(self):
1021 data = self.wfile.getvalue()
1022 if data:
1023 try:
1024 super(DelegatingUDPRequestHandler, self).finish()
1025 except OSError:
1026 if not self.server._closed:
1027 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +01001028
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001029 ThreadingUDPServer.__init__(self, addr,
1030 DelegatingUDPRequestHandler,
1031 bind_and_activate)
1032 ControlMixin.__init__(self, handler, poll_interval)
1033 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +01001034
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001035 def server_bind(self):
1036 super(TestUDPServer, self).server_bind()
1037 self.port = self.socket.getsockname()[1]
Vinay Sajipce7c9782011-05-17 07:15:53 +01001038
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001039 def server_close(self):
1040 super(TestUDPServer, self).server_close()
1041 self._closed = True
Vinay Sajipce7c9782011-05-17 07:15:53 +01001042
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001043if hasattr(socket, "AF_UNIX"):
1044 class TestUnixStreamServer(TestTCPServer):
1045 address_family = socket.AF_UNIX
Vinay Sajipce7c9782011-05-17 07:15:53 +01001046
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001047 class TestUnixDatagramServer(TestUDPServer):
1048 address_family = socket.AF_UNIX
Vinay Sajip5421f352013-09-27 18:18:28 +01001049
Vinay Sajip7367d082011-05-02 13:17:27 +01001050# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +01001051
1052class SMTPHandlerTest(BaseTest):
Victor Stinnerc98b0192019-12-10 21:12:26 +01001053 # bpo-14314, bpo-19665, bpo-34092: don't wait forever
1054 TIMEOUT = support.LONG_TIMEOUT
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02001055
Vinay Sajipa463d252011-04-30 21:52:48 +01001056 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +01001057 sockmap = {}
Serhiy Storchaka16994912020-04-25 10:06:29 +03001058 server = TestSMTPServer((socket_helper.HOST, 0), self.process_message, 0.001,
Vinay Sajip7367d082011-05-02 13:17:27 +01001059 sockmap)
1060 server.start()
Serhiy Storchaka16994912020-04-25 10:06:29 +03001061 addr = (socket_helper.HOST, server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +00001062 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
1063 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001064 self.assertEqual(h.toaddrs, ['you'])
1065 self.messages = []
Vinay Sajip277640a2015-10-17 16:13:10 +01001066 r = logging.makeLogRecord({'msg': 'Hello \u2713'})
Vinay Sajip7367d082011-05-02 13:17:27 +01001067 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +01001068 h.handle(r)
Victor Stinner31b50b82018-07-11 12:35:38 +02001069 self.handled.wait(self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001070 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +00001071 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +01001072 self.assertEqual(len(self.messages), 1)
1073 peer, mailfrom, rcpttos, data = self.messages[0]
1074 self.assertEqual(mailfrom, 'me')
1075 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001076 self.assertIn('\nSubject: Log\n', data)
Vinay Sajip277640a2015-10-17 16:13:10 +01001077 self.assertTrue(data.endswith('\n\nHello \u2713'))
Vinay Sajipa463d252011-04-30 21:52:48 +01001078 h.close()
1079
1080 def process_message(self, *args):
1081 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +01001082 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +01001083
Christian Heimes180510d2008-03-03 19:15:45 +00001084class MemoryHandlerTest(BaseTest):
1085
1086 """Tests for the MemoryHandler."""
1087
1088 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001089 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001090
1091 def setUp(self):
1092 BaseTest.setUp(self)
1093 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001094 self.root_hdlr)
Christian Heimes180510d2008-03-03 19:15:45 +00001095 self.mem_logger = logging.getLogger('mem')
1096 self.mem_logger.propagate = 0
1097 self.mem_logger.addHandler(self.mem_hdlr)
1098
1099 def tearDown(self):
1100 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001101 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001102
1103 def test_flush(self):
1104 # The memory handler flushes to its target handler based on specific
1105 # criteria (message count and message level).
1106 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001107 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001108 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001109 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001110 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001111 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001112 lines = [
1113 ('DEBUG', '1'),
1114 ('INFO', '2'),
1115 ('WARNING', '3'),
1116 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001117 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001118 for n in (4, 14):
1119 for i in range(9):
1120 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001121 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001122 # This will flush because it's the 10th message since the last
1123 # flush.
1124 self.mem_logger.debug(self.next_message())
1125 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001126 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001127
1128 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001129 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001130
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001131 def test_flush_on_close(self):
1132 """
1133 Test that the flush-on-close configuration works as expected.
1134 """
1135 self.mem_logger.debug(self.next_message())
1136 self.assert_log_lines([])
1137 self.mem_logger.info(self.next_message())
1138 self.assert_log_lines([])
1139 self.mem_logger.removeHandler(self.mem_hdlr)
1140 # Default behaviour is to flush on close. Check that it happens.
1141 self.mem_hdlr.close()
1142 lines = [
1143 ('DEBUG', '1'),
1144 ('INFO', '2'),
1145 ]
1146 self.assert_log_lines(lines)
1147 # Now configure for flushing not to be done on close.
1148 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1149 self.root_hdlr,
1150 False)
1151 self.mem_logger.addHandler(self.mem_hdlr)
1152 self.mem_logger.debug(self.next_message())
1153 self.assert_log_lines(lines) # no change
1154 self.mem_logger.info(self.next_message())
1155 self.assert_log_lines(lines) # no change
1156 self.mem_logger.removeHandler(self.mem_hdlr)
1157 self.mem_hdlr.close()
1158 # assert that no new lines have been added
1159 self.assert_log_lines(lines) # no change
1160
Christian Heimes180510d2008-03-03 19:15:45 +00001161
1162class ExceptionFormatter(logging.Formatter):
1163 """A special exception formatter."""
1164 def formatException(self, ei):
1165 return "Got a [%s]" % ei[0].__name__
1166
1167
1168class ConfigFileTest(BaseTest):
1169
1170 """Reading logging config from a .ini-style config file."""
1171
Xtreak087570a2018-07-02 14:27:46 +05301172 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001173 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001174
1175 # config0 is a standard configuration.
1176 config0 = """
1177 [loggers]
1178 keys=root
1179
1180 [handlers]
1181 keys=hand1
1182
1183 [formatters]
1184 keys=form1
1185
1186 [logger_root]
1187 level=WARNING
1188 handlers=hand1
1189
1190 [handler_hand1]
1191 class=StreamHandler
1192 level=NOTSET
1193 formatter=form1
1194 args=(sys.stdout,)
1195
1196 [formatter_form1]
1197 format=%(levelname)s ++ %(message)s
1198 datefmt=
1199 """
1200
1201 # config1 adds a little to the standard configuration.
1202 config1 = """
1203 [loggers]
1204 keys=root,parser
1205
1206 [handlers]
1207 keys=hand1
1208
1209 [formatters]
1210 keys=form1
1211
1212 [logger_root]
1213 level=WARNING
1214 handlers=
1215
1216 [logger_parser]
1217 level=DEBUG
1218 handlers=hand1
1219 propagate=1
1220 qualname=compiler.parser
1221
1222 [handler_hand1]
1223 class=StreamHandler
1224 level=NOTSET
1225 formatter=form1
1226 args=(sys.stdout,)
1227
1228 [formatter_form1]
1229 format=%(levelname)s ++ %(message)s
1230 datefmt=
1231 """
1232
Vinay Sajip3f84b072011-03-07 17:49:33 +00001233 # config1a moves the handler to the root.
1234 config1a = """
1235 [loggers]
1236 keys=root,parser
1237
1238 [handlers]
1239 keys=hand1
1240
1241 [formatters]
1242 keys=form1
1243
1244 [logger_root]
1245 level=WARNING
1246 handlers=hand1
1247
1248 [logger_parser]
1249 level=DEBUG
1250 handlers=
1251 propagate=1
1252 qualname=compiler.parser
1253
1254 [handler_hand1]
1255 class=StreamHandler
1256 level=NOTSET
1257 formatter=form1
1258 args=(sys.stdout,)
1259
1260 [formatter_form1]
1261 format=%(levelname)s ++ %(message)s
1262 datefmt=
1263 """
1264
Christian Heimes180510d2008-03-03 19:15:45 +00001265 # config2 has a subtle configuration error that should be reported
1266 config2 = config1.replace("sys.stdout", "sys.stbout")
1267
1268 # config3 has a less subtle configuration error
1269 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1270
1271 # config4 specifies a custom formatter class to be loaded
1272 config4 = """
1273 [loggers]
1274 keys=root
1275
1276 [handlers]
1277 keys=hand1
1278
1279 [formatters]
1280 keys=form1
1281
1282 [logger_root]
1283 level=NOTSET
1284 handlers=hand1
1285
1286 [handler_hand1]
1287 class=StreamHandler
1288 level=NOTSET
1289 formatter=form1
1290 args=(sys.stdout,)
1291
1292 [formatter_form1]
1293 class=""" + __name__ + """.ExceptionFormatter
1294 format=%(levelname)s:%(name)s:%(message)s
1295 datefmt=
1296 """
1297
Georg Brandl3dbca812008-07-23 16:10:53 +00001298 # config5 specifies a custom handler class to be loaded
1299 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1300
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001301 # config6 uses ', ' delimiters in the handlers and formatters sections
1302 config6 = """
1303 [loggers]
1304 keys=root,parser
1305
1306 [handlers]
1307 keys=hand1, hand2
1308
1309 [formatters]
1310 keys=form1, form2
1311
1312 [logger_root]
1313 level=WARNING
1314 handlers=
1315
1316 [logger_parser]
1317 level=DEBUG
1318 handlers=hand1
1319 propagate=1
1320 qualname=compiler.parser
1321
1322 [handler_hand1]
1323 class=StreamHandler
1324 level=NOTSET
1325 formatter=form1
1326 args=(sys.stdout,)
1327
1328 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001329 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001330 level=NOTSET
1331 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001332 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001333
1334 [formatter_form1]
1335 format=%(levelname)s ++ %(message)s
1336 datefmt=
1337
1338 [formatter_form2]
1339 format=%(message)s
1340 datefmt=
1341 """
1342
Preston Landers6ea56d22017-08-02 15:44:28 -05001343 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001344 config7 = """
1345 [loggers]
1346 keys=root,parser,compiler
1347
1348 [handlers]
1349 keys=hand1
1350
1351 [formatters]
1352 keys=form1
1353
1354 [logger_root]
1355 level=WARNING
1356 handlers=hand1
1357
1358 [logger_compiler]
1359 level=DEBUG
1360 handlers=
1361 propagate=1
1362 qualname=compiler
1363
1364 [logger_parser]
1365 level=DEBUG
1366 handlers=
1367 propagate=1
1368 qualname=compiler.parser
1369
1370 [handler_hand1]
1371 class=StreamHandler
1372 level=NOTSET
1373 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001374 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001375
1376 [formatter_form1]
1377 format=%(levelname)s ++ %(message)s
1378 datefmt=
1379 """
1380
Xtreak087570a2018-07-02 14:27:46 +05301381 # config 8, check for resource warning
1382 config8 = r"""
1383 [loggers]
1384 keys=root
1385
1386 [handlers]
1387 keys=file
1388
1389 [formatters]
1390 keys=
1391
1392 [logger_root]
1393 level=DEBUG
1394 handlers=file
1395
1396 [handler_file]
1397 class=FileHandler
1398 level=DEBUG
1399 args=("{tempfile}",)
1400 """
1401
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001402 disable_test = """
1403 [loggers]
1404 keys=root
1405
1406 [handlers]
1407 keys=screen
1408
1409 [formatters]
1410 keys=
1411
1412 [logger_root]
1413 level=DEBUG
1414 handlers=screen
1415
1416 [handler_screen]
1417 level=DEBUG
1418 class=StreamHandler
1419 args=(sys.stdout,)
1420 formatter=
1421 """
1422
1423 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001424 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001425 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001426
1427 def test_config0_ok(self):
1428 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001429 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001430 self.apply_config(self.config0)
1431 logger = logging.getLogger()
1432 # Won't output anything
1433 logger.info(self.next_message())
1434 # Outputs a message
1435 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001436 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001437 ('ERROR', '2'),
1438 ], stream=output)
1439 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001440 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001441
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001442 def test_config0_using_cp_ok(self):
1443 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001444 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001445 file = io.StringIO(textwrap.dedent(self.config0))
1446 cp = configparser.ConfigParser()
1447 cp.read_file(file)
1448 logging.config.fileConfig(cp)
1449 logger = logging.getLogger()
1450 # Won't output anything
1451 logger.info(self.next_message())
1452 # Outputs a message
1453 logger.error(self.next_message())
1454 self.assert_log_lines([
1455 ('ERROR', '2'),
1456 ], stream=output)
1457 # Original logger output is empty.
1458 self.assert_log_lines([])
1459
Georg Brandl3dbca812008-07-23 16:10:53 +00001460 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001461 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001462 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001463 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001464 logger = logging.getLogger("compiler.parser")
1465 # Both will output a message
1466 logger.info(self.next_message())
1467 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001468 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001469 ('INFO', '1'),
1470 ('ERROR', '2'),
1471 ], stream=output)
1472 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001473 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001474
1475 def test_config2_failure(self):
1476 # A simple config file which overrides the default settings.
1477 self.assertRaises(Exception, self.apply_config, self.config2)
1478
1479 def test_config3_failure(self):
1480 # A simple config file which overrides the default settings.
1481 self.assertRaises(Exception, self.apply_config, self.config3)
1482
1483 def test_config4_ok(self):
1484 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001485 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001486 self.apply_config(self.config4)
1487 logger = logging.getLogger()
1488 try:
1489 raise RuntimeError()
1490 except RuntimeError:
1491 logging.exception("just testing")
1492 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001493 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001494 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1495 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001496 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001497
Georg Brandl3dbca812008-07-23 16:10:53 +00001498 def test_config5_ok(self):
1499 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001500
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001501 def test_config6_ok(self):
1502 self.test_config1_ok(config=self.config6)
1503
Vinay Sajip3f84b072011-03-07 17:49:33 +00001504 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001505 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001506 self.apply_config(self.config1a)
1507 logger = logging.getLogger("compiler.parser")
1508 # See issue #11424. compiler-hyphenated sorts
1509 # between compiler and compiler.xyz and this
1510 # was preventing compiler.xyz from being included
1511 # in the child loggers of compiler because of an
1512 # overzealous loop termination condition.
1513 hyphenated = logging.getLogger('compiler-hyphenated')
1514 # All will output a message
1515 logger.info(self.next_message())
1516 logger.error(self.next_message())
1517 hyphenated.critical(self.next_message())
1518 self.assert_log_lines([
1519 ('INFO', '1'),
1520 ('ERROR', '2'),
1521 ('CRITICAL', '3'),
1522 ], stream=output)
1523 # Original logger output is empty.
1524 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001525 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001526 self.apply_config(self.config7)
1527 logger = logging.getLogger("compiler.parser")
1528 self.assertFalse(logger.disabled)
1529 # Both will output a message
1530 logger.info(self.next_message())
1531 logger.error(self.next_message())
1532 logger = logging.getLogger("compiler.lexer")
1533 # Both will output a message
1534 logger.info(self.next_message())
1535 logger.error(self.next_message())
1536 # Will not appear
1537 hyphenated.critical(self.next_message())
1538 self.assert_log_lines([
1539 ('INFO', '4'),
1540 ('ERROR', '5'),
1541 ('INFO', '6'),
1542 ('ERROR', '7'),
1543 ], stream=output)
1544 # Original logger output is empty.
1545 self.assert_log_lines([])
1546
Xtreak087570a2018-07-02 14:27:46 +05301547 def test_config8_ok(self):
1548
1549 def cleanup(h1, fn):
1550 h1.close()
1551 os.remove(fn)
1552
1553 with self.check_no_resource_warning():
1554 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1555 os.close(fd)
1556
1557 # Replace single backslash with double backslash in windows
1558 # to avoid unicode error during string formatting
1559 if os.name == "nt":
1560 fn = fn.replace("\\", "\\\\")
1561
1562 config8 = self.config8.format(tempfile=fn)
1563
1564 self.apply_config(config8)
1565 self.apply_config(config8)
1566
1567 handler = logging.root.handlers[0]
1568 self.addCleanup(cleanup, handler, fn)
1569
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001570 def test_logger_disabling(self):
1571 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001572 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001573 self.assertFalse(logger.disabled)
1574 self.apply_config(self.disable_test)
1575 self.assertTrue(logger.disabled)
1576 self.apply_config(self.disable_test, disable_existing_loggers=False)
1577 self.assertFalse(logger.disabled)
1578
Lucas Cimonb15100f2019-10-31 09:06:25 +01001579 def test_config_set_handler_names(self):
1580 test_config = """
1581 [loggers]
1582 keys=root
1583
1584 [handlers]
1585 keys=hand1
1586
1587 [formatters]
1588 keys=form1
1589
1590 [logger_root]
1591 handlers=hand1
1592
1593 [handler_hand1]
1594 class=StreamHandler
1595 formatter=form1
1596
1597 [formatter_form1]
1598 format=%(levelname)s ++ %(message)s
1599 """
1600 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001601 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001602
Łukasz Langa214f18e2018-06-08 04:02:48 -07001603 def test_defaults_do_no_interpolation(self):
1604 """bpo-33802 defaults should not get interpolated"""
1605 ini = textwrap.dedent("""
1606 [formatters]
1607 keys=default
1608
1609 [formatter_default]
1610
1611 [handlers]
1612 keys=console
1613
1614 [handler_console]
1615 class=logging.StreamHandler
1616 args=tuple()
1617
1618 [loggers]
1619 keys=root
1620
1621 [logger_root]
1622 formatter=default
1623 handlers=console
1624 """).strip()
1625 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1626 try:
1627 os.write(fd, ini.encode('ascii'))
1628 os.close(fd)
1629 logging.config.fileConfig(
1630 fn,
1631 defaults=dict(
1632 version=1,
1633 disable_existing_loggers=False,
1634 formatters={
1635 "generic": {
1636 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1637 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1638 "class": "logging.Formatter"
1639 },
1640 },
1641 )
1642 )
1643 finally:
1644 os.unlink(fn)
1645
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001646
Christian Heimes180510d2008-03-03 19:15:45 +00001647class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001648
Christian Heimes180510d2008-03-03 19:15:45 +00001649 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001650
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001651 server_class = TestTCPServer
1652 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001653
Christian Heimes180510d2008-03-03 19:15:45 +00001654 def setUp(self):
1655 """Set up a TCP server to receive log messages, and a SocketHandler
1656 pointing to that server's address and port."""
1657 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001658 # Issue #29177: deal with errors that happen during setup
1659 self.server = self.sock_hdlr = self.server_exception = None
1660 try:
1661 self.server = server = self.server_class(self.address,
1662 self.handle_socket, 0.01)
1663 server.start()
1664 # Uncomment next line to test error recovery in setUp()
1665 # raise OSError('dummy error raised')
1666 except OSError as e:
1667 self.server_exception = e
1668 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001669 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001670 hcls = logging.handlers.SocketHandler
1671 if isinstance(server.server_address, tuple):
1672 self.sock_hdlr = hcls('localhost', server.port)
1673 else:
1674 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001675 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001676 self.root_logger.removeHandler(self.root_logger.handlers[0])
1677 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001678 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001679
Christian Heimes180510d2008-03-03 19:15:45 +00001680 def tearDown(self):
1681 """Shutdown the TCP server."""
1682 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001683 if self.sock_hdlr:
1684 self.root_logger.removeHandler(self.sock_hdlr)
1685 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001686 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001687 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001688 finally:
1689 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001690
Vinay Sajip7367d082011-05-02 13:17:27 +01001691 def handle_socket(self, request):
1692 conn = request.connection
1693 while True:
1694 chunk = conn.recv(4)
1695 if len(chunk) < 4:
1696 break
1697 slen = struct.unpack(">L", chunk)[0]
1698 chunk = conn.recv(slen)
1699 while len(chunk) < slen:
1700 chunk = chunk + conn.recv(slen - len(chunk))
1701 obj = pickle.loads(chunk)
1702 record = logging.makeLogRecord(obj)
1703 self.log_output += record.msg + '\n'
1704 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001705
Christian Heimes180510d2008-03-03 19:15:45 +00001706 def test_output(self):
1707 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001708 if self.server_exception:
1709 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001710 logger = logging.getLogger("tcp")
1711 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001712 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001713 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001714 self.handled.acquire()
1715 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001716
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001717 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001718 if self.server_exception:
1719 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001720 # Avoid timing-related failures due to SocketHandler's own hard-wired
1721 # one-second timeout on socket.create_connection() (issue #16264).
1722 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001723 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001724 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001725 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001726 try:
1727 raise RuntimeError('Deliberate mistake')
1728 except RuntimeError:
1729 self.root_logger.exception('Never sent')
1730 self.root_logger.error('Never sent, either')
1731 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001732 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001733 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1734 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001735
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001736def _get_temp_domain_socket():
1737 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1738 os.close(fd)
1739 # just need a name - file can't be present, or we'll get an
1740 # 'address already in use' error.
1741 os.remove(fn)
1742 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001743
Victor Stinnerec5a8602014-06-02 14:41:51 +02001744@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001745class UnixSocketHandlerTest(SocketHandlerTest):
1746
1747 """Test for SocketHandler with unix sockets."""
1748
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001749 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001750 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001751
1752 def setUp(self):
1753 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001754 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001755 SocketHandlerTest.setUp(self)
1756
1757 def tearDown(self):
1758 SocketHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001759 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001760
Vinay Sajip7367d082011-05-02 13:17:27 +01001761class DatagramHandlerTest(BaseTest):
1762
1763 """Test for DatagramHandler."""
1764
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001765 server_class = TestUDPServer
1766 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001767
Vinay Sajip7367d082011-05-02 13:17:27 +01001768 def setUp(self):
1769 """Set up a UDP server to receive log messages, and a DatagramHandler
1770 pointing to that server's address and port."""
1771 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001772 # Issue #29177: deal with errors that happen during setup
1773 self.server = self.sock_hdlr = self.server_exception = None
1774 try:
1775 self.server = server = self.server_class(self.address,
1776 self.handle_datagram, 0.01)
1777 server.start()
1778 # Uncomment next line to test error recovery in setUp()
1779 # raise OSError('dummy error raised')
1780 except OSError as e:
1781 self.server_exception = e
1782 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001783 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001784 hcls = logging.handlers.DatagramHandler
1785 if isinstance(server.server_address, tuple):
1786 self.sock_hdlr = hcls('localhost', server.port)
1787 else:
1788 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001789 self.log_output = ''
1790 self.root_logger.removeHandler(self.root_logger.handlers[0])
1791 self.root_logger.addHandler(self.sock_hdlr)
1792 self.handled = threading.Event()
1793
1794 def tearDown(self):
1795 """Shutdown the UDP server."""
1796 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001797 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001798 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001799 if self.sock_hdlr:
1800 self.root_logger.removeHandler(self.sock_hdlr)
1801 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001802 finally:
1803 BaseTest.tearDown(self)
1804
1805 def handle_datagram(self, request):
1806 slen = struct.pack('>L', 0) # length of prefix
1807 packet = request.packet[len(slen):]
1808 obj = pickle.loads(packet)
1809 record = logging.makeLogRecord(obj)
1810 self.log_output += record.msg + '\n'
1811 self.handled.set()
1812
1813 def test_output(self):
1814 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001815 if self.server_exception:
1816 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001817 logger = logging.getLogger("udp")
1818 logger.error("spam")
1819 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001820 self.handled.clear()
1821 logger.error("eggs")
1822 self.handled.wait()
1823 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001824
Victor Stinnerec5a8602014-06-02 14:41:51 +02001825@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001826class UnixDatagramHandlerTest(DatagramHandlerTest):
1827
1828 """Test for DatagramHandler using Unix sockets."""
1829
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001830 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001831 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001832
1833 def setUp(self):
1834 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001835 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001836 DatagramHandlerTest.setUp(self)
1837
1838 def tearDown(self):
1839 DatagramHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001840 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001841
Vinay Sajip7367d082011-05-02 13:17:27 +01001842class SysLogHandlerTest(BaseTest):
1843
1844 """Test for SysLogHandler using UDP."""
1845
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001846 server_class = TestUDPServer
1847 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001848
Vinay Sajip7367d082011-05-02 13:17:27 +01001849 def setUp(self):
1850 """Set up a UDP server to receive log messages, and a SysLogHandler
1851 pointing to that server's address and port."""
1852 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001853 # Issue #29177: deal with errors that happen during setup
1854 self.server = self.sl_hdlr = self.server_exception = None
1855 try:
1856 self.server = server = self.server_class(self.address,
1857 self.handle_datagram, 0.01)
1858 server.start()
1859 # Uncomment next line to test error recovery in setUp()
1860 # raise OSError('dummy error raised')
1861 except OSError as e:
1862 self.server_exception = e
1863 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001864 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001865 hcls = logging.handlers.SysLogHandler
1866 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001867 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001868 else:
1869 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001870 self.log_output = ''
1871 self.root_logger.removeHandler(self.root_logger.handlers[0])
1872 self.root_logger.addHandler(self.sl_hdlr)
1873 self.handled = threading.Event()
1874
1875 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001876 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001877 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001878 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001879 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001880 if self.sl_hdlr:
1881 self.root_logger.removeHandler(self.sl_hdlr)
1882 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001883 finally:
1884 BaseTest.tearDown(self)
1885
1886 def handle_datagram(self, request):
1887 self.log_output = request.packet
1888 self.handled.set()
1889
1890 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001891 if self.server_exception:
1892 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001893 # The log message sent to the SysLogHandler is properly received.
1894 logger = logging.getLogger("slh")
1895 logger.error("sp\xe4m")
1896 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001897 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001898 self.handled.clear()
1899 self.sl_hdlr.append_nul = False
1900 logger.error("sp\xe4m")
1901 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001902 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001903 self.handled.clear()
1904 self.sl_hdlr.ident = "h\xe4m-"
1905 logger.error("sp\xe4m")
1906 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001907 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001908
Victor Stinnerec5a8602014-06-02 14:41:51 +02001909@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001910class UnixSysLogHandlerTest(SysLogHandlerTest):
1911
1912 """Test for SysLogHandler with Unix sockets."""
1913
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001914 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001915 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001916
1917 def setUp(self):
1918 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001919 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001920 SysLogHandlerTest.setUp(self)
1921
1922 def tearDown(self):
1923 SysLogHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001924 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001925
Serhiy Storchaka16994912020-04-25 10:06:29 +03001926@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001927 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001928class IPv6SysLogHandlerTest(SysLogHandlerTest):
1929
1930 """Test for SysLogHandler with IPv6 host."""
1931
1932 server_class = TestUDPServer
1933 address = ('::1', 0)
1934
1935 def setUp(self):
1936 self.server_class.address_family = socket.AF_INET6
1937 super(IPv6SysLogHandlerTest, self).setUp()
1938
1939 def tearDown(self):
1940 self.server_class.address_family = socket.AF_INET
1941 super(IPv6SysLogHandlerTest, self).tearDown()
1942
Vinay Sajip7367d082011-05-02 13:17:27 +01001943class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001944 """Test for HTTPHandler."""
1945
1946 def setUp(self):
1947 """Set up an HTTP server to receive log messages, and a HTTPHandler
1948 pointing to that server's address and port."""
1949 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001950 self.handled = threading.Event()
1951
Vinay Sajip7367d082011-05-02 13:17:27 +01001952 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001953 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001954 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001955 if self.command == 'POST':
1956 try:
1957 rlen = int(request.headers['Content-Length'])
1958 self.post_data = request.rfile.read(rlen)
1959 except:
1960 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001961 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001962 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001963 self.handled.set()
1964
1965 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001966 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001967 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001968 root_logger = self.root_logger
1969 root_logger.removeHandler(self.root_logger.handlers[0])
1970 for secure in (False, True):
1971 addr = ('localhost', 0)
1972 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001973 try:
1974 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01001975 except ImportError:
1976 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001977 else:
1978 here = os.path.dirname(__file__)
1979 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02001980 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001981 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06001982
1983 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01001984 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001985 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06001986 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01001987 self.server = server = TestHTTPServer(addr, self.handle_request,
1988 0.01, sslctx=sslctx)
1989 server.start()
1990 server.ready.wait()
1991 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001992 secure_client = secure and sslctx
1993 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06001994 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01001995 context=context,
1996 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01001997 self.log_data = None
1998 root_logger.addHandler(self.h_hdlr)
1999
2000 for method in ('GET', 'POST'):
2001 self.h_hdlr.method = method
2002 self.handled.clear()
2003 msg = "sp\xe4m"
2004 logger.error(msg)
2005 self.handled.wait()
2006 self.assertEqual(self.log_data.path, '/frob')
2007 self.assertEqual(self.command, method)
2008 if method == 'GET':
2009 d = parse_qs(self.log_data.query)
2010 else:
2011 d = parse_qs(self.post_data.decode('utf-8'))
2012 self.assertEqual(d['name'], ['http'])
2013 self.assertEqual(d['funcName'], ['test_output'])
2014 self.assertEqual(d['msg'], [msg])
2015
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002016 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002017 self.root_logger.removeHandler(self.h_hdlr)
2018 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002019
Christian Heimes180510d2008-03-03 19:15:45 +00002020class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002021
Christian Heimes180510d2008-03-03 19:15:45 +00002022 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002023
Christian Heimes180510d2008-03-03 19:15:45 +00002024 def setUp(self):
2025 """Create a dict to remember potentially destroyed objects."""
2026 BaseTest.setUp(self)
2027 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002028
Christian Heimes180510d2008-03-03 19:15:45 +00002029 def _watch_for_survival(self, *args):
2030 """Watch the given objects for survival, by creating weakrefs to
2031 them."""
2032 for obj in args:
2033 key = id(obj), repr(obj)
2034 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002035
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002036 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002037 """Assert that all objects watched for survival have survived."""
2038 # Trigger cycle breaking.
2039 gc.collect()
2040 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002041 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002042 if ref() is None:
2043 dead.append(repr_)
2044 if dead:
2045 self.fail("%d objects should have survived "
2046 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002047
Christian Heimes180510d2008-03-03 19:15:45 +00002048 def test_persistent_loggers(self):
2049 # Logger objects are persistent and retain their configuration, even
2050 # if visible references are destroyed.
2051 self.root_logger.setLevel(logging.INFO)
2052 foo = logging.getLogger("foo")
2053 self._watch_for_survival(foo)
2054 foo.setLevel(logging.DEBUG)
2055 self.root_logger.debug(self.next_message())
2056 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002057 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002058 ('foo', 'DEBUG', '2'),
2059 ])
2060 del foo
2061 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002062 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002063 # foo has retained its settings.
2064 bar = logging.getLogger("foo")
2065 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002066 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002067 ('foo', 'DEBUG', '2'),
2068 ('foo', 'DEBUG', '3'),
2069 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002070
Benjamin Petersonf91df042009-02-13 02:50:59 +00002071
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002072class EncodingTest(BaseTest):
2073 def test_encoding_plain_file(self):
2074 # In Python 2.x, a plain file object is treated as having no encoding.
2075 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002076 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2077 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002078 # the non-ascii data we write to the log.
2079 data = "foo\x80"
2080 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002081 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002082 log.addHandler(handler)
2083 try:
2084 # write non-ascii data to the log.
2085 log.warning(data)
2086 finally:
2087 log.removeHandler(handler)
2088 handler.close()
2089 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002090 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002091 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002092 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002093 finally:
2094 f.close()
2095 finally:
2096 if os.path.isfile(fn):
2097 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002098
Benjamin Petersonf91df042009-02-13 02:50:59 +00002099 def test_encoding_cyrillic_unicode(self):
2100 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002101 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002102 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002103 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002104 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002105 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002106 stream = io.BytesIO()
2107 writer = writer_class(stream, 'strict')
2108 handler = logging.StreamHandler(writer)
2109 log.addHandler(handler)
2110 try:
2111 log.warning(message)
2112 finally:
2113 log.removeHandler(handler)
2114 handler.close()
2115 # check we wrote exactly those bytes, ignoring trailing \n etc
2116 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002117 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002118 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2119
2120
Georg Brandlf9734072008-12-07 15:30:06 +00002121class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002122
Georg Brandlf9734072008-12-07 15:30:06 +00002123 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002124 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002125 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002126 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002127 warnings.filterwarnings("always", category=UserWarning)
2128 stream = io.StringIO()
2129 h = logging.StreamHandler(stream)
2130 logger = logging.getLogger("py.warnings")
2131 logger.addHandler(h)
2132 warnings.warn("I'm warning you...")
2133 logger.removeHandler(h)
2134 s = stream.getvalue()
2135 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002136 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002137
Mike53f7a7c2017-12-14 14:04:53 +03002138 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002139 a_file = io.StringIO()
2140 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2141 a_file, "Dummy line")
2142 s = a_file.getvalue()
2143 a_file.close()
2144 self.assertEqual(s,
2145 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2146
2147 def test_warnings_no_handlers(self):
2148 with warnings.catch_warnings():
2149 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002150 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002151
2152 # confirm our assumption: no loggers are set
2153 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002154 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002155
2156 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002157 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002158 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002159
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002160
2161def formatFunc(format, datefmt=None):
2162 return logging.Formatter(format, datefmt)
2163
BNMetrics18fb1fb2018-10-15 19:41:36 +01002164class myCustomFormatter:
2165 def __init__(self, fmt, datefmt=None):
2166 pass
2167
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002168def handlerFunc():
2169 return logging.StreamHandler()
2170
2171class CustomHandler(logging.StreamHandler):
2172 pass
2173
2174class ConfigDictTest(BaseTest):
2175
2176 """Reading logging config from a dictionary."""
2177
Xtreak087570a2018-07-02 14:27:46 +05302178 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002179 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002180
2181 # config0 is a standard configuration.
2182 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002183 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002184 'formatters': {
2185 'form1' : {
2186 'format' : '%(levelname)s ++ %(message)s',
2187 },
2188 },
2189 'handlers' : {
2190 'hand1' : {
2191 'class' : 'logging.StreamHandler',
2192 'formatter' : 'form1',
2193 'level' : 'NOTSET',
2194 'stream' : 'ext://sys.stdout',
2195 },
2196 },
2197 'root' : {
2198 'level' : 'WARNING',
2199 'handlers' : ['hand1'],
2200 },
2201 }
2202
2203 # config1 adds a little to the standard configuration.
2204 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002205 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002206 'formatters': {
2207 'form1' : {
2208 'format' : '%(levelname)s ++ %(message)s',
2209 },
2210 },
2211 'handlers' : {
2212 'hand1' : {
2213 'class' : 'logging.StreamHandler',
2214 'formatter' : 'form1',
2215 'level' : 'NOTSET',
2216 'stream' : 'ext://sys.stdout',
2217 },
2218 },
2219 'loggers' : {
2220 'compiler.parser' : {
2221 'level' : 'DEBUG',
2222 'handlers' : ['hand1'],
2223 },
2224 },
2225 'root' : {
2226 'level' : 'WARNING',
2227 },
2228 }
2229
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002230 # config1a moves the handler to the root. Used with config8a
2231 config1a = {
2232 'version': 1,
2233 'formatters': {
2234 'form1' : {
2235 'format' : '%(levelname)s ++ %(message)s',
2236 },
2237 },
2238 'handlers' : {
2239 'hand1' : {
2240 'class' : 'logging.StreamHandler',
2241 'formatter' : 'form1',
2242 'level' : 'NOTSET',
2243 'stream' : 'ext://sys.stdout',
2244 },
2245 },
2246 'loggers' : {
2247 'compiler.parser' : {
2248 'level' : 'DEBUG',
2249 },
2250 },
2251 'root' : {
2252 'level' : 'WARNING',
2253 'handlers' : ['hand1'],
2254 },
2255 }
2256
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002257 # config2 has a subtle configuration error that should be reported
2258 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002259 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002260 'formatters': {
2261 'form1' : {
2262 'format' : '%(levelname)s ++ %(message)s',
2263 },
2264 },
2265 'handlers' : {
2266 'hand1' : {
2267 'class' : 'logging.StreamHandler',
2268 'formatter' : 'form1',
2269 'level' : 'NOTSET',
2270 'stream' : 'ext://sys.stdbout',
2271 },
2272 },
2273 'loggers' : {
2274 'compiler.parser' : {
2275 'level' : 'DEBUG',
2276 'handlers' : ['hand1'],
2277 },
2278 },
2279 'root' : {
2280 'level' : 'WARNING',
2281 },
2282 }
2283
Mike53f7a7c2017-12-14 14:04:53 +03002284 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002285 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002286 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002287 'formatters': {
2288 'form1' : {
2289 'format' : '%(levelname)s ++ %(message)s',
2290 },
2291 },
2292 'handlers' : {
2293 'hand1' : {
2294 'class' : 'logging.StreamHandler',
2295 'formatter' : 'form1',
2296 'level' : 'NTOSET',
2297 'stream' : 'ext://sys.stdout',
2298 },
2299 },
2300 'loggers' : {
2301 'compiler.parser' : {
2302 'level' : 'DEBUG',
2303 'handlers' : ['hand1'],
2304 },
2305 },
2306 'root' : {
2307 'level' : 'WARNING',
2308 },
2309 }
2310
2311
Mike53f7a7c2017-12-14 14:04:53 +03002312 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002313 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002314 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002315 'formatters': {
2316 'form1' : {
2317 'format' : '%(levelname)s ++ %(message)s',
2318 },
2319 },
2320 'handlers' : {
2321 'hand1' : {
2322 'class' : 'logging.StreamHandler',
2323 'formatter' : 'form1',
2324 'level' : 'NOTSET',
2325 'stream' : 'ext://sys.stdout',
2326 },
2327 },
2328 'loggers' : {
2329 'compiler.parser' : {
2330 'level' : 'DEBUG',
2331 'handlers' : ['hand1'],
2332 },
2333 },
2334 'root' : {
2335 'level' : 'WRANING',
2336 },
2337 }
2338
2339 # config3 has a less subtle configuration error
2340 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002341 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002342 'formatters': {
2343 'form1' : {
2344 'format' : '%(levelname)s ++ %(message)s',
2345 },
2346 },
2347 'handlers' : {
2348 'hand1' : {
2349 'class' : 'logging.StreamHandler',
2350 'formatter' : 'misspelled_name',
2351 'level' : 'NOTSET',
2352 'stream' : 'ext://sys.stdout',
2353 },
2354 },
2355 'loggers' : {
2356 'compiler.parser' : {
2357 'level' : 'DEBUG',
2358 'handlers' : ['hand1'],
2359 },
2360 },
2361 'root' : {
2362 'level' : 'WARNING',
2363 },
2364 }
2365
2366 # config4 specifies a custom formatter class to be loaded
2367 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002368 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002369 'formatters': {
2370 'form1' : {
2371 '()' : __name__ + '.ExceptionFormatter',
2372 'format' : '%(levelname)s:%(name)s:%(message)s',
2373 },
2374 },
2375 'handlers' : {
2376 'hand1' : {
2377 'class' : 'logging.StreamHandler',
2378 'formatter' : 'form1',
2379 'level' : 'NOTSET',
2380 'stream' : 'ext://sys.stdout',
2381 },
2382 },
2383 'root' : {
2384 'level' : 'NOTSET',
2385 'handlers' : ['hand1'],
2386 },
2387 }
2388
2389 # As config4 but using an actual callable rather than a string
2390 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002391 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002392 'formatters': {
2393 'form1' : {
2394 '()' : ExceptionFormatter,
2395 'format' : '%(levelname)s:%(name)s:%(message)s',
2396 },
2397 'form2' : {
2398 '()' : __name__ + '.formatFunc',
2399 'format' : '%(levelname)s:%(name)s:%(message)s',
2400 },
2401 'form3' : {
2402 '()' : formatFunc,
2403 'format' : '%(levelname)s:%(name)s:%(message)s',
2404 },
2405 },
2406 'handlers' : {
2407 'hand1' : {
2408 'class' : 'logging.StreamHandler',
2409 'formatter' : 'form1',
2410 'level' : 'NOTSET',
2411 'stream' : 'ext://sys.stdout',
2412 },
2413 'hand2' : {
2414 '()' : handlerFunc,
2415 },
2416 },
2417 'root' : {
2418 'level' : 'NOTSET',
2419 'handlers' : ['hand1'],
2420 },
2421 }
2422
2423 # config5 specifies a custom handler class to be loaded
2424 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002425 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002426 'formatters': {
2427 'form1' : {
2428 'format' : '%(levelname)s ++ %(message)s',
2429 },
2430 },
2431 'handlers' : {
2432 'hand1' : {
2433 'class' : __name__ + '.CustomHandler',
2434 'formatter' : 'form1',
2435 'level' : 'NOTSET',
2436 'stream' : 'ext://sys.stdout',
2437 },
2438 },
2439 'loggers' : {
2440 'compiler.parser' : {
2441 'level' : 'DEBUG',
2442 'handlers' : ['hand1'],
2443 },
2444 },
2445 'root' : {
2446 'level' : 'WARNING',
2447 },
2448 }
2449
2450 # config6 specifies a custom handler class to be loaded
2451 # but has bad arguments
2452 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002453 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002454 'formatters': {
2455 'form1' : {
2456 'format' : '%(levelname)s ++ %(message)s',
2457 },
2458 },
2459 'handlers' : {
2460 'hand1' : {
2461 'class' : __name__ + '.CustomHandler',
2462 'formatter' : 'form1',
2463 'level' : 'NOTSET',
2464 'stream' : 'ext://sys.stdout',
2465 '9' : 'invalid parameter name',
2466 },
2467 },
2468 'loggers' : {
2469 'compiler.parser' : {
2470 'level' : 'DEBUG',
2471 'handlers' : ['hand1'],
2472 },
2473 },
2474 'root' : {
2475 'level' : 'WARNING',
2476 },
2477 }
2478
Mike53f7a7c2017-12-14 14:04:53 +03002479 # config 7 does not define compiler.parser but defines compiler.lexer
2480 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002481 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002482 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002483 'formatters': {
2484 'form1' : {
2485 'format' : '%(levelname)s ++ %(message)s',
2486 },
2487 },
2488 'handlers' : {
2489 'hand1' : {
2490 'class' : 'logging.StreamHandler',
2491 'formatter' : 'form1',
2492 'level' : 'NOTSET',
2493 'stream' : 'ext://sys.stdout',
2494 },
2495 },
2496 'loggers' : {
2497 'compiler.lexer' : {
2498 'level' : 'DEBUG',
2499 'handlers' : ['hand1'],
2500 },
2501 },
2502 'root' : {
2503 'level' : 'WARNING',
2504 },
2505 }
2506
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002507 # config8 defines both compiler and compiler.lexer
2508 # so compiler.parser should not be disabled (since
2509 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002510 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002511 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002512 'disable_existing_loggers' : False,
2513 'formatters': {
2514 'form1' : {
2515 'format' : '%(levelname)s ++ %(message)s',
2516 },
2517 },
2518 'handlers' : {
2519 'hand1' : {
2520 'class' : 'logging.StreamHandler',
2521 'formatter' : 'form1',
2522 'level' : 'NOTSET',
2523 'stream' : 'ext://sys.stdout',
2524 },
2525 },
2526 'loggers' : {
2527 'compiler' : {
2528 'level' : 'DEBUG',
2529 'handlers' : ['hand1'],
2530 },
2531 'compiler.lexer' : {
2532 },
2533 },
2534 'root' : {
2535 'level' : 'WARNING',
2536 },
2537 }
2538
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002539 # config8a disables existing loggers
2540 config8a = {
2541 'version': 1,
2542 'disable_existing_loggers' : True,
2543 'formatters': {
2544 'form1' : {
2545 'format' : '%(levelname)s ++ %(message)s',
2546 },
2547 },
2548 'handlers' : {
2549 'hand1' : {
2550 'class' : 'logging.StreamHandler',
2551 'formatter' : 'form1',
2552 'level' : 'NOTSET',
2553 'stream' : 'ext://sys.stdout',
2554 },
2555 },
2556 'loggers' : {
2557 'compiler' : {
2558 'level' : 'DEBUG',
2559 'handlers' : ['hand1'],
2560 },
2561 'compiler.lexer' : {
2562 },
2563 },
2564 'root' : {
2565 'level' : 'WARNING',
2566 },
2567 }
2568
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002569 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002570 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002571 'formatters': {
2572 'form1' : {
2573 'format' : '%(levelname)s ++ %(message)s',
2574 },
2575 },
2576 'handlers' : {
2577 'hand1' : {
2578 'class' : 'logging.StreamHandler',
2579 'formatter' : 'form1',
2580 'level' : 'WARNING',
2581 'stream' : 'ext://sys.stdout',
2582 },
2583 },
2584 'loggers' : {
2585 'compiler.parser' : {
2586 'level' : 'WARNING',
2587 'handlers' : ['hand1'],
2588 },
2589 },
2590 'root' : {
2591 'level' : 'NOTSET',
2592 },
2593 }
2594
2595 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002596 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002597 'incremental' : True,
2598 'handlers' : {
2599 'hand1' : {
2600 'level' : 'WARNING',
2601 },
2602 },
2603 'loggers' : {
2604 'compiler.parser' : {
2605 'level' : 'INFO',
2606 },
2607 },
2608 }
2609
2610 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002611 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002612 'incremental' : True,
2613 'handlers' : {
2614 'hand1' : {
2615 'level' : 'INFO',
2616 },
2617 },
2618 'loggers' : {
2619 'compiler.parser' : {
2620 'level' : 'INFO',
2621 },
2622 },
2623 }
2624
Mike53f7a7c2017-12-14 14:04:53 +03002625 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002626 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002627 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002628 'formatters': {
2629 'form1' : {
2630 'format' : '%(levelname)s ++ %(message)s',
2631 },
2632 },
2633 'filters' : {
2634 'filt1' : {
2635 'name' : 'compiler.parser',
2636 },
2637 },
2638 'handlers' : {
2639 'hand1' : {
2640 'class' : 'logging.StreamHandler',
2641 'formatter' : 'form1',
2642 'level' : 'NOTSET',
2643 'stream' : 'ext://sys.stdout',
2644 'filters' : ['filt1'],
2645 },
2646 },
2647 'loggers' : {
2648 'compiler.parser' : {
2649 'level' : 'DEBUG',
2650 'filters' : ['filt1'],
2651 },
2652 },
2653 'root' : {
2654 'level' : 'WARNING',
2655 'handlers' : ['hand1'],
2656 },
2657 }
2658
Mike53f7a7c2017-12-14 14:04:53 +03002659 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002660 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002661 'version': 1,
2662 'true_formatters': {
2663 'form1' : {
2664 'format' : '%(levelname)s ++ %(message)s',
2665 },
2666 },
2667 'handler_configs': {
2668 'hand1' : {
2669 'class' : 'logging.StreamHandler',
2670 'formatter' : 'form1',
2671 'level' : 'NOTSET',
2672 'stream' : 'ext://sys.stdout',
2673 },
2674 },
2675 'formatters' : 'cfg://true_formatters',
2676 'handlers' : {
2677 'hand1' : 'cfg://handler_configs[hand1]',
2678 },
2679 'loggers' : {
2680 'compiler.parser' : {
2681 'level' : 'DEBUG',
2682 'handlers' : ['hand1'],
2683 },
2684 },
2685 'root' : {
2686 'level' : 'WARNING',
2687 },
2688 }
2689
Mike53f7a7c2017-12-14 14:04:53 +03002690 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002691 config12 = {
2692 'true_formatters': {
2693 'form1' : {
2694 'format' : '%(levelname)s ++ %(message)s',
2695 },
2696 },
2697 'handler_configs': {
2698 'hand1' : {
2699 'class' : 'logging.StreamHandler',
2700 'formatter' : 'form1',
2701 'level' : 'NOTSET',
2702 'stream' : 'ext://sys.stdout',
2703 },
2704 },
2705 'formatters' : 'cfg://true_formatters',
2706 'handlers' : {
2707 'hand1' : 'cfg://handler_configs[hand1]',
2708 },
2709 'loggers' : {
2710 'compiler.parser' : {
2711 'level' : 'DEBUG',
2712 'handlers' : ['hand1'],
2713 },
2714 },
2715 'root' : {
2716 'level' : 'WARNING',
2717 },
2718 }
2719
Mike53f7a7c2017-12-14 14:04:53 +03002720 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002721 config13 = {
2722 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002723 'true_formatters': {
2724 'form1' : {
2725 'format' : '%(levelname)s ++ %(message)s',
2726 },
2727 },
2728 'handler_configs': {
2729 'hand1' : {
2730 'class' : 'logging.StreamHandler',
2731 'formatter' : 'form1',
2732 'level' : 'NOTSET',
2733 'stream' : 'ext://sys.stdout',
2734 },
2735 },
2736 'formatters' : 'cfg://true_formatters',
2737 'handlers' : {
2738 'hand1' : 'cfg://handler_configs[hand1]',
2739 },
2740 'loggers' : {
2741 'compiler.parser' : {
2742 'level' : 'DEBUG',
2743 'handlers' : ['hand1'],
2744 },
2745 },
2746 'root' : {
2747 'level' : 'WARNING',
2748 },
2749 }
2750
Vinay Sajip8d270232012-11-15 14:20:18 +00002751 # As config0, but with properties
2752 config14 = {
2753 'version': 1,
2754 'formatters': {
2755 'form1' : {
2756 'format' : '%(levelname)s ++ %(message)s',
2757 },
2758 },
2759 'handlers' : {
2760 'hand1' : {
2761 'class' : 'logging.StreamHandler',
2762 'formatter' : 'form1',
2763 'level' : 'NOTSET',
2764 'stream' : 'ext://sys.stdout',
2765 '.': {
2766 'foo': 'bar',
2767 'terminator': '!\n',
2768 }
2769 },
2770 },
2771 'root' : {
2772 'level' : 'WARNING',
2773 'handlers' : ['hand1'],
2774 },
2775 }
2776
Vinay Sajip3f885b52013-03-22 15:19:54 +00002777 out_of_order = {
2778 "version": 1,
2779 "formatters": {
2780 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002781 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2782 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002783 }
2784 },
2785 "handlers": {
2786 "fileGlobal": {
2787 "class": "logging.StreamHandler",
2788 "level": "DEBUG",
2789 "formatter": "mySimpleFormatter"
2790 },
2791 "bufferGlobal": {
2792 "class": "logging.handlers.MemoryHandler",
2793 "capacity": 5,
2794 "formatter": "mySimpleFormatter",
2795 "target": "fileGlobal",
2796 "level": "DEBUG"
2797 }
2798 },
2799 "loggers": {
2800 "mymodule": {
2801 "level": "DEBUG",
2802 "handlers": ["bufferGlobal"],
2803 "propagate": "true"
2804 }
2805 }
2806 }
2807
BNMetrics18fb1fb2018-10-15 19:41:36 +01002808 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2809 custom_formatter_class_validate = {
2810 'version': 1,
2811 'formatters': {
2812 'form1': {
2813 '()': __name__ + '.ExceptionFormatter',
2814 'format': '%(levelname)s:%(name)s:%(message)s',
2815 'validate': False,
2816 },
2817 },
2818 'handlers' : {
2819 'hand1' : {
2820 'class': 'logging.StreamHandler',
2821 'formatter': 'form1',
2822 'level': 'NOTSET',
2823 'stream': 'ext://sys.stdout',
2824 },
2825 },
2826 "loggers": {
2827 "my_test_logger_custom_formatter": {
2828 "level": "DEBUG",
2829 "handlers": ["hand1"],
2830 "propagate": "true"
2831 }
2832 }
2833 }
2834
2835 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2836 custom_formatter_class_validate2 = {
2837 'version': 1,
2838 'formatters': {
2839 'form1': {
2840 'class': __name__ + '.ExceptionFormatter',
2841 'format': '%(levelname)s:%(name)s:%(message)s',
2842 'validate': False,
2843 },
2844 },
2845 'handlers' : {
2846 'hand1' : {
2847 'class': 'logging.StreamHandler',
2848 'formatter': 'form1',
2849 'level': 'NOTSET',
2850 'stream': 'ext://sys.stdout',
2851 },
2852 },
2853 "loggers": {
2854 "my_test_logger_custom_formatter": {
2855 "level": "DEBUG",
2856 "handlers": ["hand1"],
2857 "propagate": "true"
2858 }
2859 }
2860 }
2861
2862 # Configuration with custom class that is not inherited from logging.Formatter
2863 custom_formatter_class_validate3 = {
2864 'version': 1,
2865 'formatters': {
2866 'form1': {
2867 'class': __name__ + '.myCustomFormatter',
2868 'format': '%(levelname)s:%(name)s:%(message)s',
2869 'validate': False,
2870 },
2871 },
2872 'handlers' : {
2873 'hand1' : {
2874 'class': 'logging.StreamHandler',
2875 'formatter': 'form1',
2876 'level': 'NOTSET',
2877 'stream': 'ext://sys.stdout',
2878 },
2879 },
2880 "loggers": {
2881 "my_test_logger_custom_formatter": {
2882 "level": "DEBUG",
2883 "handlers": ["hand1"],
2884 "propagate": "true"
2885 }
2886 }
2887 }
2888
2889 # Configuration with custom function and 'validate' set to False
2890 custom_formatter_with_function = {
2891 'version': 1,
2892 'formatters': {
2893 'form1': {
2894 '()': formatFunc,
2895 'format': '%(levelname)s:%(name)s:%(message)s',
2896 'validate': False,
2897 },
2898 },
2899 'handlers' : {
2900 'hand1' : {
2901 'class': 'logging.StreamHandler',
2902 'formatter': 'form1',
2903 'level': 'NOTSET',
2904 'stream': 'ext://sys.stdout',
2905 },
2906 },
2907 "loggers": {
2908 "my_test_logger_custom_formatter": {
2909 "level": "DEBUG",
2910 "handlers": ["hand1"],
2911 "propagate": "true"
2912 }
2913 }
2914 }
2915
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002916 def apply_config(self, conf):
2917 logging.config.dictConfig(conf)
2918
2919 def test_config0_ok(self):
2920 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002921 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002922 self.apply_config(self.config0)
2923 logger = logging.getLogger()
2924 # Won't output anything
2925 logger.info(self.next_message())
2926 # Outputs a message
2927 logger.error(self.next_message())
2928 self.assert_log_lines([
2929 ('ERROR', '2'),
2930 ], stream=output)
2931 # Original logger output is empty.
2932 self.assert_log_lines([])
2933
2934 def test_config1_ok(self, config=config1):
2935 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002936 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002937 self.apply_config(config)
2938 logger = logging.getLogger("compiler.parser")
2939 # Both will output a message
2940 logger.info(self.next_message())
2941 logger.error(self.next_message())
2942 self.assert_log_lines([
2943 ('INFO', '1'),
2944 ('ERROR', '2'),
2945 ], stream=output)
2946 # Original logger output is empty.
2947 self.assert_log_lines([])
2948
2949 def test_config2_failure(self):
2950 # A simple config which overrides the default settings.
2951 self.assertRaises(Exception, self.apply_config, self.config2)
2952
2953 def test_config2a_failure(self):
2954 # A simple config which overrides the default settings.
2955 self.assertRaises(Exception, self.apply_config, self.config2a)
2956
2957 def test_config2b_failure(self):
2958 # A simple config which overrides the default settings.
2959 self.assertRaises(Exception, self.apply_config, self.config2b)
2960
2961 def test_config3_failure(self):
2962 # A simple config which overrides the default settings.
2963 self.assertRaises(Exception, self.apply_config, self.config3)
2964
2965 def test_config4_ok(self):
2966 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002967 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002968 self.apply_config(self.config4)
2969 #logger = logging.getLogger()
2970 try:
2971 raise RuntimeError()
2972 except RuntimeError:
2973 logging.exception("just testing")
2974 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002975 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002976 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2977 # Original logger output is empty
2978 self.assert_log_lines([])
2979
2980 def test_config4a_ok(self):
2981 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002982 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002983 self.apply_config(self.config4a)
2984 #logger = logging.getLogger()
2985 try:
2986 raise RuntimeError()
2987 except RuntimeError:
2988 logging.exception("just testing")
2989 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002990 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002991 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2992 # Original logger output is empty
2993 self.assert_log_lines([])
2994
2995 def test_config5_ok(self):
2996 self.test_config1_ok(config=self.config5)
2997
2998 def test_config6_failure(self):
2999 self.assertRaises(Exception, self.apply_config, self.config6)
3000
3001 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003002 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003003 self.apply_config(self.config1)
3004 logger = logging.getLogger("compiler.parser")
3005 # Both will output a message
3006 logger.info(self.next_message())
3007 logger.error(self.next_message())
3008 self.assert_log_lines([
3009 ('INFO', '1'),
3010 ('ERROR', '2'),
3011 ], stream=output)
3012 # Original logger output is empty.
3013 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003014 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003015 self.apply_config(self.config7)
3016 logger = logging.getLogger("compiler.parser")
3017 self.assertTrue(logger.disabled)
3018 logger = logging.getLogger("compiler.lexer")
3019 # Both will output a message
3020 logger.info(self.next_message())
3021 logger.error(self.next_message())
3022 self.assert_log_lines([
3023 ('INFO', '3'),
3024 ('ERROR', '4'),
3025 ], stream=output)
3026 # Original logger output is empty.
3027 self.assert_log_lines([])
3028
Mike53f7a7c2017-12-14 14:04:53 +03003029 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003030 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003031 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003032 self.apply_config(self.config1)
3033 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003034 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003035 logger.info(self.next_message())
3036 logger.error(self.next_message())
3037 self.assert_log_lines([
3038 ('INFO', '1'),
3039 ('ERROR', '2'),
3040 ], stream=output)
3041 # Original logger output is empty.
3042 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003043 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003044 self.apply_config(self.config8)
3045 logger = logging.getLogger("compiler.parser")
3046 self.assertFalse(logger.disabled)
3047 # Both will output a message
3048 logger.info(self.next_message())
3049 logger.error(self.next_message())
3050 logger = logging.getLogger("compiler.lexer")
3051 # Both will output a message
3052 logger.info(self.next_message())
3053 logger.error(self.next_message())
3054 self.assert_log_lines([
3055 ('INFO', '3'),
3056 ('ERROR', '4'),
3057 ('INFO', '5'),
3058 ('ERROR', '6'),
3059 ], stream=output)
3060 # Original logger output is empty.
3061 self.assert_log_lines([])
3062
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003063 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003064 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003065 self.apply_config(self.config1a)
3066 logger = logging.getLogger("compiler.parser")
3067 # See issue #11424. compiler-hyphenated sorts
3068 # between compiler and compiler.xyz and this
3069 # was preventing compiler.xyz from being included
3070 # in the child loggers of compiler because of an
3071 # overzealous loop termination condition.
3072 hyphenated = logging.getLogger('compiler-hyphenated')
3073 # All will output a message
3074 logger.info(self.next_message())
3075 logger.error(self.next_message())
3076 hyphenated.critical(self.next_message())
3077 self.assert_log_lines([
3078 ('INFO', '1'),
3079 ('ERROR', '2'),
3080 ('CRITICAL', '3'),
3081 ], stream=output)
3082 # Original logger output is empty.
3083 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003084 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003085 self.apply_config(self.config8a)
3086 logger = logging.getLogger("compiler.parser")
3087 self.assertFalse(logger.disabled)
3088 # Both will output a message
3089 logger.info(self.next_message())
3090 logger.error(self.next_message())
3091 logger = logging.getLogger("compiler.lexer")
3092 # Both will output a message
3093 logger.info(self.next_message())
3094 logger.error(self.next_message())
3095 # Will not appear
3096 hyphenated.critical(self.next_message())
3097 self.assert_log_lines([
3098 ('INFO', '4'),
3099 ('ERROR', '5'),
3100 ('INFO', '6'),
3101 ('ERROR', '7'),
3102 ], stream=output)
3103 # Original logger output is empty.
3104 self.assert_log_lines([])
3105
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003106 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003107 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003108 self.apply_config(self.config9)
3109 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003110 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003111 logger.info(self.next_message())
3112 self.assert_log_lines([], stream=output)
3113 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003114 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003115 logger.info(self.next_message())
3116 self.assert_log_lines([], stream=output)
3117 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003118 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003119 logger.info(self.next_message())
3120 self.assert_log_lines([
3121 ('INFO', '3'),
3122 ], stream=output)
3123
3124 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003125 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003126 self.apply_config(self.config10)
3127 logger = logging.getLogger("compiler.parser")
3128 logger.warning(self.next_message())
3129 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003130 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003131 logger.warning(self.next_message())
3132 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003133 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003134 logger.warning(self.next_message())
3135 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003136 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003137 logger.error(self.next_message())
3138 self.assert_log_lines([
3139 ('WARNING', '1'),
3140 ('ERROR', '4'),
3141 ], stream=output)
3142
3143 def test_config11_ok(self):
3144 self.test_config1_ok(self.config11)
3145
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003146 def test_config12_failure(self):
3147 self.assertRaises(Exception, self.apply_config, self.config12)
3148
3149 def test_config13_failure(self):
3150 self.assertRaises(Exception, self.apply_config, self.config13)
3151
Vinay Sajip8d270232012-11-15 14:20:18 +00003152 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003153 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003154 self.apply_config(self.config14)
3155 h = logging._handlers['hand1']
3156 self.assertEqual(h.foo, 'bar')
3157 self.assertEqual(h.terminator, '!\n')
3158 logging.warning('Exclamation')
3159 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3160
Xtreak087570a2018-07-02 14:27:46 +05303161 def test_config15_ok(self):
3162
3163 def cleanup(h1, fn):
3164 h1.close()
3165 os.remove(fn)
3166
3167 with self.check_no_resource_warning():
3168 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3169 os.close(fd)
3170
3171 config = {
3172 "version": 1,
3173 "handlers": {
3174 "file": {
3175 "class": "logging.FileHandler",
3176 "filename": fn
3177 }
3178 },
3179 "root": {
3180 "handlers": ["file"]
3181 }
3182 }
3183
3184 self.apply_config(config)
3185 self.apply_config(config)
3186
3187 handler = logging.root.handlers[0]
3188 self.addCleanup(cleanup, handler, fn)
3189
Vinay Sajip4ded5512012-10-02 15:56:16 +01003190 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003191 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003192 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003193 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003194 t.start()
3195 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003196 # Now get the port allocated
3197 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003198 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003199 try:
3200 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3201 sock.settimeout(2.0)
3202 sock.connect(('localhost', port))
3203
3204 slen = struct.pack('>L', len(text))
3205 s = slen + text
3206 sentsofar = 0
3207 left = len(s)
3208 while left > 0:
3209 sent = sock.send(s[sentsofar:])
3210 sentsofar += sent
3211 left -= sent
3212 sock.close()
3213 finally:
3214 t.ready.wait(2.0)
3215 logging.config.stopListening()
Hai Shie80697d2020-05-28 06:10:27 +08003216 threading_helper.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003217
3218 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003219 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003220 self.setup_via_listener(json.dumps(self.config10))
3221 logger = logging.getLogger("compiler.parser")
3222 logger.warning(self.next_message())
3223 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003224 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003225 logger.warning(self.next_message())
3226 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003227 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003228 logger.warning(self.next_message())
3229 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003230 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003231 logger.error(self.next_message())
3232 self.assert_log_lines([
3233 ('WARNING', '1'),
3234 ('ERROR', '4'),
3235 ], stream=output)
3236
3237 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003238 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003239 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3240 logger = logging.getLogger("compiler.parser")
3241 # Both will output a message
3242 logger.info(self.next_message())
3243 logger.error(self.next_message())
3244 self.assert_log_lines([
3245 ('INFO', '1'),
3246 ('ERROR', '2'),
3247 ], stream=output)
3248 # Original logger output is empty.
3249 self.assert_log_lines([])
3250
Vinay Sajip4ded5512012-10-02 15:56:16 +01003251 def test_listen_verify(self):
3252
3253 def verify_fail(stuff):
3254 return None
3255
3256 def verify_reverse(stuff):
3257 return stuff[::-1]
3258
3259 logger = logging.getLogger("compiler.parser")
3260 to_send = textwrap.dedent(ConfigFileTest.config1)
3261 # First, specify a verification function that will fail.
3262 # We expect to see no output, since our configuration
3263 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003264 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003265 self.setup_via_listener(to_send, verify_fail)
3266 # Both will output a message
3267 logger.info(self.next_message())
3268 logger.error(self.next_message())
3269 self.assert_log_lines([], stream=output)
3270 # Original logger output has the stuff we logged.
3271 self.assert_log_lines([
3272 ('INFO', '1'),
3273 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003274 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003275
3276 # Now, perform no verification. Our configuration
3277 # should take effect.
3278
Vinay Sajip1feedb42014-05-31 08:19:12 +01003279 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003280 self.setup_via_listener(to_send) # no verify callable specified
3281 logger = logging.getLogger("compiler.parser")
3282 # Both will output a message
3283 logger.info(self.next_message())
3284 logger.error(self.next_message())
3285 self.assert_log_lines([
3286 ('INFO', '3'),
3287 ('ERROR', '4'),
3288 ], stream=output)
3289 # Original logger output still has the stuff we logged before.
3290 self.assert_log_lines([
3291 ('INFO', '1'),
3292 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003293 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003294
3295 # Now, perform verification which transforms the bytes.
3296
Vinay Sajip1feedb42014-05-31 08:19:12 +01003297 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003298 self.setup_via_listener(to_send[::-1], verify_reverse)
3299 logger = logging.getLogger("compiler.parser")
3300 # Both will output a message
3301 logger.info(self.next_message())
3302 logger.error(self.next_message())
3303 self.assert_log_lines([
3304 ('INFO', '5'),
3305 ('ERROR', '6'),
3306 ], stream=output)
3307 # Original logger output still has the stuff we logged before.
3308 self.assert_log_lines([
3309 ('INFO', '1'),
3310 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003311 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003312
Vinay Sajip3f885b52013-03-22 15:19:54 +00003313 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003314 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3315
3316 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003317 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003318 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3319
3320 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003321 handler = logging.getLogger('mymodule').handlers[0]
3322 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003323 self.assertIsInstance(handler.formatter._style,
3324 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003325
BNMetrics18fb1fb2018-10-15 19:41:36 +01003326 def test_custom_formatter_class_with_validate(self):
3327 self.apply_config(self.custom_formatter_class_validate)
3328 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3329 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3330
3331 def test_custom_formatter_class_with_validate2(self):
3332 self.apply_config(self.custom_formatter_class_validate2)
3333 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3334 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3335
3336 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3337 config = self.custom_formatter_class_validate.copy()
3338 config['formatters']['form1']['style'] = "$"
3339
3340 # Exception should not be raise as we have configured 'validate' to False
3341 self.apply_config(config)
3342 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3343 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3344
3345 def test_custom_formatter_class_with_validate3(self):
3346 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3347
3348 def test_custom_formatter_function_with_validate(self):
3349 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3350
Vinay Sajip373baef2011-04-26 20:05:24 +01003351 def test_baseconfig(self):
3352 d = {
3353 'atuple': (1, 2, 3),
3354 'alist': ['a', 'b', 'c'],
3355 'adict': {'d': 'e', 'f': 3 },
3356 'nest1': ('g', ('h', 'i'), 'j'),
3357 'nest2': ['k', ['l', 'm'], 'n'],
3358 'nest3': ['o', 'cfg://alist', 'p'],
3359 }
3360 bc = logging.config.BaseConfigurator(d)
3361 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3362 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3363 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3364 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3365 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3366 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3367 v = bc.convert('cfg://nest3')
3368 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3369 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3370 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3371 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003372
Vinay Sajip46abfc12020-01-01 19:32:11 +00003373 def test_namedtuple(self):
3374 # see bpo-39142
3375 from collections import namedtuple
3376
3377 class MyHandler(logging.StreamHandler):
3378 def __init__(self, resource, *args, **kwargs):
3379 super().__init__(*args, **kwargs)
3380 self.resource: namedtuple = resource
3381
3382 def emit(self, record):
3383 record.msg += f' {self.resource.type}'
3384 return super().emit(record)
3385
3386 Resource = namedtuple('Resource', ['type', 'labels'])
3387 resource = Resource(type='my_type', labels=['a'])
3388
3389 config = {
3390 'version': 1,
3391 'handlers': {
3392 'myhandler': {
3393 '()': MyHandler,
3394 'resource': resource
3395 }
3396 },
3397 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3398 }
3399 with support.captured_stderr() as stderr:
3400 self.apply_config(config)
3401 logging.info('some log')
3402 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3403
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003404class ManagerTest(BaseTest):
3405 def test_manager_loggerclass(self):
3406 logged = []
3407
3408 class MyLogger(logging.Logger):
3409 def _log(self, level, msg, args, exc_info=None, extra=None):
3410 logged.append(msg)
3411
3412 man = logging.Manager(None)
3413 self.assertRaises(TypeError, man.setLoggerClass, int)
3414 man.setLoggerClass(MyLogger)
3415 logger = man.getLogger('test')
3416 logger.warning('should appear in logged')
3417 logging.warning('should not appear in logged')
3418
3419 self.assertEqual(logged, ['should appear in logged'])
3420
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003421 def test_set_log_record_factory(self):
3422 man = logging.Manager(None)
3423 expected = object()
3424 man.setLogRecordFactory(expected)
3425 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003426
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003427class ChildLoggerTest(BaseTest):
3428 def test_child_loggers(self):
3429 r = logging.getLogger()
3430 l1 = logging.getLogger('abc')
3431 l2 = logging.getLogger('def.ghi')
3432 c1 = r.getChild('xyz')
3433 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003434 self.assertIs(c1, logging.getLogger('xyz'))
3435 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003436 c1 = l1.getChild('def')
3437 c2 = c1.getChild('ghi')
3438 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003439 self.assertIs(c1, logging.getLogger('abc.def'))
3440 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3441 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003442
3443
Vinay Sajip6fac8172010-10-19 20:44:14 +00003444class DerivedLogRecord(logging.LogRecord):
3445 pass
3446
Vinay Sajip61561522010-12-03 11:50:38 +00003447class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003448
3449 def setUp(self):
3450 class CheckingFilter(logging.Filter):
3451 def __init__(self, cls):
3452 self.cls = cls
3453
3454 def filter(self, record):
3455 t = type(record)
3456 if t is not self.cls:
3457 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3458 self.cls)
3459 raise TypeError(msg)
3460 return True
3461
3462 BaseTest.setUp(self)
3463 self.filter = CheckingFilter(DerivedLogRecord)
3464 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003465 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003466
3467 def tearDown(self):
3468 self.root_logger.removeFilter(self.filter)
3469 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003470 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003471
3472 def test_logrecord_class(self):
3473 self.assertRaises(TypeError, self.root_logger.warning,
3474 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003475 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003476 self.root_logger.error(self.next_message())
3477 self.assert_log_lines([
3478 ('root', 'ERROR', '2'),
3479 ])
3480
3481
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003482class QueueHandlerTest(BaseTest):
3483 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003484 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003485
3486 def setUp(self):
3487 BaseTest.setUp(self)
3488 self.queue = queue.Queue(-1)
3489 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003490 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003491 self.que_logger = logging.getLogger('que')
3492 self.que_logger.propagate = False
3493 self.que_logger.setLevel(logging.WARNING)
3494 self.que_logger.addHandler(self.que_hdlr)
3495
3496 def tearDown(self):
3497 self.que_hdlr.close()
3498 BaseTest.tearDown(self)
3499
3500 def test_queue_handler(self):
3501 self.que_logger.debug(self.next_message())
3502 self.assertRaises(queue.Empty, self.queue.get_nowait)
3503 self.que_logger.info(self.next_message())
3504 self.assertRaises(queue.Empty, self.queue.get_nowait)
3505 msg = self.next_message()
3506 self.que_logger.warning(msg)
3507 data = self.queue.get_nowait()
3508 self.assertTrue(isinstance(data, logging.LogRecord))
3509 self.assertEqual(data.name, self.que_logger.name)
3510 self.assertEqual((data.msg, data.args), (msg, None))
3511
favlladfe3442017-08-01 20:12:26 +02003512 def test_formatting(self):
3513 msg = self.next_message()
3514 levelname = logging.getLevelName(logging.WARNING)
3515 log_format_str = '{name} -> {levelname}: {message}'
3516 formatted_msg = log_format_str.format(name=self.name,
3517 levelname=levelname, message=msg)
3518 formatter = logging.Formatter(self.log_format)
3519 self.que_hdlr.setFormatter(formatter)
3520 self.que_logger.warning(msg)
3521 log_record = self.queue.get_nowait()
3522 self.assertEqual(formatted_msg, log_record.msg)
3523 self.assertEqual(formatted_msg, log_record.message)
3524
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003525 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3526 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003527 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003528 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003529 listener = logging.handlers.QueueListener(self.queue, handler)
3530 listener.start()
3531 try:
3532 self.que_logger.warning(self.next_message())
3533 self.que_logger.error(self.next_message())
3534 self.que_logger.critical(self.next_message())
3535 finally:
3536 listener.stop()
3537 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3538 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3539 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003540 handler.close()
3541
3542 # Now test with respect_handler_level set
3543
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003544 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003545 handler.setLevel(logging.CRITICAL)
3546 listener = logging.handlers.QueueListener(self.queue, handler,
3547 respect_handler_level=True)
3548 listener.start()
3549 try:
3550 self.que_logger.warning(self.next_message())
3551 self.que_logger.error(self.next_message())
3552 self.que_logger.critical(self.next_message())
3553 finally:
3554 listener.stop()
3555 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3556 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3557 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003558 handler.close()
3559
3560 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3561 'logging.handlers.QueueListener required for this test')
3562 def test_queue_listener_with_StreamHandler(self):
3563 # Test that traceback only appends once (bpo-34334).
3564 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3565 listener.start()
3566 try:
3567 1 / 0
3568 except ZeroDivisionError as e:
3569 exc = e
3570 self.que_logger.exception(self.next_message(), exc_info=exc)
3571 listener.stop()
3572 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003573
Xtreak2dad9602019-04-07 13:21:27 +05303574 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3575 'logging.handlers.QueueListener required for this test')
3576 def test_queue_listener_with_multiple_handlers(self):
3577 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3578 self.que_hdlr.setFormatter(self.root_formatter)
3579 self.que_logger.addHandler(self.root_hdlr)
3580
3581 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3582 listener.start()
3583 self.que_logger.error("error")
3584 listener.stop()
3585 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3586
Vinay Sajipd61910c2016-09-08 01:13:39 +01003587if hasattr(logging.handlers, 'QueueListener'):
3588 import multiprocessing
3589 from unittest.mock import patch
3590
3591 class QueueListenerTest(BaseTest):
3592 """
3593 Tests based on patch submitted for issue #27930. Ensure that
3594 QueueListener handles all log messages.
3595 """
3596
3597 repeat = 20
3598
3599 @staticmethod
3600 def setup_and_log(log_queue, ident):
3601 """
3602 Creates a logger with a QueueHandler that logs to a queue read by a
3603 QueueListener. Starts the listener, logs five messages, and stops
3604 the listener.
3605 """
3606 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3607 logger.setLevel(logging.DEBUG)
3608 handler = logging.handlers.QueueHandler(log_queue)
3609 logger.addHandler(handler)
3610 listener = logging.handlers.QueueListener(log_queue)
3611 listener.start()
3612
3613 logger.info('one')
3614 logger.info('two')
3615 logger.info('three')
3616 logger.info('four')
3617 logger.info('five')
3618
3619 listener.stop()
3620 logger.removeHandler(handler)
3621 handler.close()
3622
3623 @patch.object(logging.handlers.QueueListener, 'handle')
3624 def test_handle_called_with_queue_queue(self, mock_handle):
3625 for i in range(self.repeat):
3626 log_queue = queue.Queue()
3627 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3628 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3629 'correct number of handled log messages')
3630
3631 @patch.object(logging.handlers.QueueListener, 'handle')
3632 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003633 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003634 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003635 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003636 for i in range(self.repeat):
3637 log_queue = multiprocessing.Queue()
3638 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003639 log_queue.close()
3640 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003641 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3642 'correct number of handled log messages')
3643
3644 @staticmethod
3645 def get_all_from_queue(log_queue):
3646 try:
3647 while True:
3648 yield log_queue.get_nowait()
3649 except queue.Empty:
3650 return []
3651
3652 def test_no_messages_in_queue_after_stop(self):
3653 """
3654 Five messages are logged then the QueueListener is stopped. This
3655 test then gets everything off the queue. Failure of this test
3656 indicates that messages were not registered on the queue until
3657 _after_ the QueueListener stopped.
3658 """
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003659 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003660 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003661 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003662 for i in range(self.repeat):
3663 queue = multiprocessing.Queue()
3664 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3665 # time.sleep(1)
3666 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003667 queue.close()
3668 queue.join_thread()
3669
Vinay Sajipd61910c2016-09-08 01:13:39 +01003670 expected = [[], [logging.handlers.QueueListener._sentinel]]
3671 self.assertIn(items, expected,
3672 'Found unexpected messages in queue: %s' % (
3673 [m.msg if isinstance(m, logging.LogRecord)
3674 else m for m in items]))
3675
Bar Harel6b282e12019-06-01 12:19:09 +03003676 def test_calls_task_done_after_stop(self):
3677 # Issue 36813: Make sure queue.join does not deadlock.
3678 log_queue = queue.Queue()
3679 listener = logging.handlers.QueueListener(log_queue)
3680 listener.start()
3681 listener.stop()
3682 with self.assertRaises(ValueError):
3683 # Make sure all tasks are done and .join won't block.
3684 log_queue.task_done()
3685
Vinay Sajipe723e962011-04-15 22:27:17 +01003686
Vinay Sajip37eb3382011-04-26 20:26:41 +01003687ZERO = datetime.timedelta(0)
3688
3689class UTC(datetime.tzinfo):
3690 def utcoffset(self, dt):
3691 return ZERO
3692
3693 dst = utcoffset
3694
3695 def tzname(self, dt):
3696 return 'UTC'
3697
3698utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003699
Vinay Sajipa39c5712010-10-25 13:57:39 +00003700class FormatterTest(unittest.TestCase):
3701 def setUp(self):
3702 self.common = {
3703 'name': 'formatter.test',
3704 'level': logging.DEBUG,
3705 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3706 'lineno': 42,
3707 'exc_info': None,
3708 'func': None,
3709 'msg': 'Message with %d %s',
3710 'args': (2, 'placeholders'),
3711 }
3712 self.variants = {
3713 }
3714
3715 def get_record(self, name=None):
3716 result = dict(self.common)
3717 if name is not None:
3718 result.update(self.variants[name])
3719 return logging.makeLogRecord(result)
3720
BNMetrics18fb1fb2018-10-15 19:41:36 +01003721 def assert_error_message(self, exception, message, *args, **kwargs):
3722 try:
3723 self.assertRaises(exception, *args, **kwargs)
3724 except exception as e:
3725 self.assertEqual(message, e.message)
3726
Vinay Sajipa39c5712010-10-25 13:57:39 +00003727 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003728 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003729 r = self.get_record()
3730 f = logging.Formatter('${%(message)s}')
3731 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3732 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003733 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003734 self.assertFalse(f.usesTime())
3735 f = logging.Formatter('%(asctime)s')
3736 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003737 f = logging.Formatter('%(asctime)-15s')
3738 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003739 f = logging.Formatter('%(asctime)#15s')
3740 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003741
3742 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003743 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003744 r = self.get_record()
3745 f = logging.Formatter('$%{message}%$', style='{')
3746 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3747 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003748 self.assertRaises(ValueError, f.format, r)
3749 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003750 self.assertFalse(f.usesTime())
3751 f = logging.Formatter('{asctime}', style='{')
3752 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003753 f = logging.Formatter('{asctime!s:15}', style='{')
3754 self.assertTrue(f.usesTime())
3755 f = logging.Formatter('{asctime:15}', style='{')
3756 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003757
3758 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003759 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003760 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003761 f = logging.Formatter('${message}', style='$')
3762 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003763 f = logging.Formatter('$message', style='$')
3764 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3765 f = logging.Formatter('$$%${message}%$$', style='$')
3766 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3767 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003768 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003769 self.assertFalse(f.usesTime())
3770 f = logging.Formatter('${asctime}', style='$')
3771 self.assertTrue(f.usesTime())
3772 f = logging.Formatter('$asctime', style='$')
3773 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003774 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003775 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003776 f = logging.Formatter('${asctime}--', style='$')
3777 self.assertTrue(f.usesTime())
3778
3779 def test_format_validate(self):
3780 # Check correct formatting
3781 # Percentage style
3782 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3783 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3784 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3785 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3786 f = logging.Formatter("%(process)#+027.23X")
3787 self.assertEqual(f._fmt, "%(process)#+027.23X")
3788 f = logging.Formatter("%(foo)#.*g")
3789 self.assertEqual(f._fmt, "%(foo)#.*g")
3790
3791 # StrFormat Style
3792 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3793 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3794 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3795 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3796 f = logging.Formatter("{customfield!s:#<30}", style="{")
3797 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3798 f = logging.Formatter("{message!r}", style="{")
3799 self.assertEqual(f._fmt, "{message!r}")
3800 f = logging.Formatter("{message!s}", style="{")
3801 self.assertEqual(f._fmt, "{message!s}")
3802 f = logging.Formatter("{message!a}", style="{")
3803 self.assertEqual(f._fmt, "{message!a}")
3804 f = logging.Formatter("{process!r:4.2}", style="{")
3805 self.assertEqual(f._fmt, "{process!r:4.2}")
3806 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3807 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3808 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3809 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3810 f = logging.Formatter("{foo:12.{p}}", style="{")
3811 self.assertEqual(f._fmt, "{foo:12.{p}}")
3812 f = logging.Formatter("{foo:{w}.6}", style="{")
3813 self.assertEqual(f._fmt, "{foo:{w}.6}")
3814 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3815 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3816 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3817 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3818 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3819 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3820
3821 # Dollar style
3822 f = logging.Formatter("${asctime} - $message", style="$")
3823 self.assertEqual(f._fmt, "${asctime} - $message")
3824 f = logging.Formatter("$bar $$", style="$")
3825 self.assertEqual(f._fmt, "$bar $$")
3826 f = logging.Formatter("$bar $$$$", style="$")
3827 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3828
3829 # Testing when ValueError being raised from incorrect format
3830 # Percentage Style
3831 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3832 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3833 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3834 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3835 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3836 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3837 self.assertRaises(ValueError, logging.Formatter, '${message}')
3838 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3839 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3840
3841 # StrFormat Style
3842 # Testing failure for '-' in field name
3843 self.assert_error_message(
3844 ValueError,
3845 "invalid field name/expression: 'name-thing'",
3846 logging.Formatter, "{name-thing}", style="{"
3847 )
3848 # Testing failure for style mismatch
3849 self.assert_error_message(
3850 ValueError,
3851 "invalid format: no fields",
3852 logging.Formatter, '%(asctime)s', style='{'
3853 )
3854 # Testing failure for invalid conversion
3855 self.assert_error_message(
3856 ValueError,
3857 "invalid conversion: 'Z'"
3858 )
3859 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3860 self.assert_error_message(
3861 ValueError,
3862 "invalid format: expected ':' after conversion specifier",
3863 logging.Formatter, '{asctime!aa:15}', style='{'
3864 )
3865 # Testing failure for invalid spec
3866 self.assert_error_message(
3867 ValueError,
3868 "bad specifier: '.2ff'",
3869 logging.Formatter, '{process:.2ff}', style='{'
3870 )
3871 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3872 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3873 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3874 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3875 # Testing failure for mismatch braces
3876 self.assert_error_message(
3877 ValueError,
3878 "invalid format: unmatched '{' in format spec",
3879 logging.Formatter, '{process', style='{'
3880 )
3881 self.assert_error_message(
3882 ValueError,
3883 "invalid format: unmatched '{' in format spec",
3884 logging.Formatter, 'process}', style='{'
3885 )
3886 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3887 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3888 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3889 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3890 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3891 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3892 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3893 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3894 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3895
3896 # Dollar style
3897 # Testing failure for mismatch bare $
3898 self.assert_error_message(
3899 ValueError,
3900 "invalid format: bare \'$\' not allowed",
3901 logging.Formatter, '$bar $$$', style='$'
3902 )
3903 self.assert_error_message(
3904 ValueError,
3905 "invalid format: bare \'$\' not allowed",
3906 logging.Formatter, 'bar $', style='$'
3907 )
3908 self.assert_error_message(
3909 ValueError,
3910 "invalid format: bare \'$\' not allowed",
3911 logging.Formatter, 'foo $.', style='$'
3912 )
3913 # Testing failure for mismatch style
3914 self.assert_error_message(
3915 ValueError,
3916 "invalid format: no fields",
3917 logging.Formatter, '{asctime}', style='$'
3918 )
3919 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3920
3921 # Testing failure for incorrect fields
3922 self.assert_error_message(
3923 ValueError,
3924 "invalid format: no fields",
3925 logging.Formatter, 'foo', style='$'
3926 )
3927 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003928
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003929 def test_invalid_style(self):
3930 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3931
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003932 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003933 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003934 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3935 # We use None to indicate we want the local timezone
3936 # We're essentially converting a UTC time to local time
3937 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003938 r.msecs = 123
3939 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003940 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003941 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3942 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003943 f.format(r)
3944 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
3945
Mariusz Felisiak06a35542020-04-17 18:02:47 +02003946 def test_default_msec_format_none(self):
3947 class NoMsecFormatter(logging.Formatter):
3948 default_msec_format = None
3949 default_time_format = '%d/%m/%Y %H:%M:%S'
3950
3951 r = self.get_record()
3952 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
3953 r.created = time.mktime(dt.astimezone(None).timetuple())
3954 f = NoMsecFormatter()
3955 f.converter = time.gmtime
3956 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
3957
3958
Vinay Sajip985ef872011-04-26 19:34:04 +01003959class TestBufferingFormatter(logging.BufferingFormatter):
3960 def formatHeader(self, records):
3961 return '[(%d)' % len(records)
3962
3963 def formatFooter(self, records):
3964 return '(%d)]' % len(records)
3965
3966class BufferingFormatterTest(unittest.TestCase):
3967 def setUp(self):
3968 self.records = [
3969 logging.makeLogRecord({'msg': 'one'}),
3970 logging.makeLogRecord({'msg': 'two'}),
3971 ]
3972
3973 def test_default(self):
3974 f = logging.BufferingFormatter()
3975 self.assertEqual('', f.format([]))
3976 self.assertEqual('onetwo', f.format(self.records))
3977
3978 def test_custom(self):
3979 f = TestBufferingFormatter()
3980 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
3981 lf = logging.Formatter('<%(message)s>')
3982 f = TestBufferingFormatter(lf)
3983 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003984
3985class ExceptionTest(BaseTest):
3986 def test_formatting(self):
3987 r = self.root_logger
3988 h = RecordingHandler()
3989 r.addHandler(h)
3990 try:
3991 raise RuntimeError('deliberate mistake')
3992 except:
3993 logging.exception('failed', stack_info=True)
3994 r.removeHandler(h)
3995 h.close()
3996 r = h.records[0]
3997 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
3998 'call last):\n'))
3999 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4000 'deliberate mistake'))
4001 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4002 'call last):\n'))
4003 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4004 'stack_info=True)'))
4005
4006
Vinay Sajip5a27d402010-12-10 11:42:57 +00004007class LastResortTest(BaseTest):
4008 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004009 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004010 root = self.root_logger
4011 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004012 old_lastresort = logging.lastResort
4013 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004014
Vinay Sajip5a27d402010-12-10 11:42:57 +00004015 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004016 with support.captured_stderr() as stderr:
4017 root.debug('This should not appear')
4018 self.assertEqual(stderr.getvalue(), '')
4019 root.warning('Final chance!')
4020 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4021
4022 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004023 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004024 with support.captured_stderr() as stderr:
4025 root.warning('Final chance!')
4026 msg = 'No handlers could be found for logger "root"\n'
4027 self.assertEqual(stderr.getvalue(), msg)
4028
Vinay Sajip5a27d402010-12-10 11:42:57 +00004029 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004030 with support.captured_stderr() as stderr:
4031 root.warning('Final chance!')
4032 self.assertEqual(stderr.getvalue(), '')
4033
4034 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004035 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004036 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004037 with support.captured_stderr() as stderr:
4038 root.warning('Final chance!')
4039 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004040 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004041 root.addHandler(self.root_hdlr)
4042 logging.lastResort = old_lastresort
4043 logging.raiseExceptions = old_raise_exceptions
4044
4045
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004046class FakeHandler:
4047
4048 def __init__(self, identifier, called):
4049 for method in ('acquire', 'flush', 'close', 'release'):
4050 setattr(self, method, self.record_call(identifier, method, called))
4051
4052 def record_call(self, identifier, method_name, called):
4053 def inner():
4054 called.append('{} - {}'.format(identifier, method_name))
4055 return inner
4056
4057
4058class RecordingHandler(logging.NullHandler):
4059
4060 def __init__(self, *args, **kwargs):
4061 super(RecordingHandler, self).__init__(*args, **kwargs)
4062 self.records = []
4063
4064 def handle(self, record):
4065 """Keep track of all the emitted records."""
4066 self.records.append(record)
4067
4068
4069class ShutdownTest(BaseTest):
4070
Vinay Sajip5e66b162011-04-20 15:41:14 +01004071 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004072
4073 def setUp(self):
4074 super(ShutdownTest, self).setUp()
4075 self.called = []
4076
4077 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004078 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004079
4080 def raise_error(self, error):
4081 def inner():
4082 raise error()
4083 return inner
4084
4085 def test_no_failure(self):
4086 # create some fake handlers
4087 handler0 = FakeHandler(0, self.called)
4088 handler1 = FakeHandler(1, self.called)
4089 handler2 = FakeHandler(2, self.called)
4090
4091 # create live weakref to those handlers
4092 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4093
4094 logging.shutdown(handlerList=list(handlers))
4095
4096 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4097 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4098 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4099 self.assertEqual(expected, self.called)
4100
4101 def _test_with_failure_in_method(self, method, error):
4102 handler = FakeHandler(0, self.called)
4103 setattr(handler, method, self.raise_error(error))
4104 handlers = [logging.weakref.ref(handler)]
4105
4106 logging.shutdown(handlerList=list(handlers))
4107
4108 self.assertEqual('0 - release', self.called[-1])
4109
4110 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004111 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004112
4113 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004114 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004115
4116 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004117 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004118
4119 def test_with_valueerror_in_acquire(self):
4120 self._test_with_failure_in_method('acquire', ValueError)
4121
4122 def test_with_valueerror_in_flush(self):
4123 self._test_with_failure_in_method('flush', ValueError)
4124
4125 def test_with_valueerror_in_close(self):
4126 self._test_with_failure_in_method('close', ValueError)
4127
4128 def test_with_other_error_in_acquire_without_raise(self):
4129 logging.raiseExceptions = False
4130 self._test_with_failure_in_method('acquire', IndexError)
4131
4132 def test_with_other_error_in_flush_without_raise(self):
4133 logging.raiseExceptions = False
4134 self._test_with_failure_in_method('flush', IndexError)
4135
4136 def test_with_other_error_in_close_without_raise(self):
4137 logging.raiseExceptions = False
4138 self._test_with_failure_in_method('close', IndexError)
4139
4140 def test_with_other_error_in_acquire_with_raise(self):
4141 logging.raiseExceptions = True
4142 self.assertRaises(IndexError, self._test_with_failure_in_method,
4143 'acquire', IndexError)
4144
4145 def test_with_other_error_in_flush_with_raise(self):
4146 logging.raiseExceptions = True
4147 self.assertRaises(IndexError, self._test_with_failure_in_method,
4148 'flush', IndexError)
4149
4150 def test_with_other_error_in_close_with_raise(self):
4151 logging.raiseExceptions = True
4152 self.assertRaises(IndexError, self._test_with_failure_in_method,
4153 'close', IndexError)
4154
4155
4156class ModuleLevelMiscTest(BaseTest):
4157
Vinay Sajip5e66b162011-04-20 15:41:14 +01004158 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004159
4160 def test_disable(self):
4161 old_disable = logging.root.manager.disable
4162 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004163 self.assertEqual(old_disable, 0)
4164 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004165
4166 logging.disable(83)
4167 self.assertEqual(logging.root.manager.disable, 83)
4168
Vinay Sajipd489ac92016-12-31 11:40:11 +00004169 # test the default value introduced in 3.7
4170 # (Issue #28524)
4171 logging.disable()
4172 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4173
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004174 def _test_log(self, method, level=None):
4175 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004176 support.patch(self, logging, 'basicConfig',
4177 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004178
4179 recording = RecordingHandler()
4180 logging.root.addHandler(recording)
4181
4182 log_method = getattr(logging, method)
4183 if level is not None:
4184 log_method(level, "test me: %r", recording)
4185 else:
4186 log_method("test me: %r", recording)
4187
4188 self.assertEqual(len(recording.records), 1)
4189 record = recording.records[0]
4190 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4191
4192 expected_level = level if level is not None else getattr(logging, method.upper())
4193 self.assertEqual(record.levelno, expected_level)
4194
4195 # basicConfig was not called!
4196 self.assertEqual(called, [])
4197
4198 def test_log(self):
4199 self._test_log('log', logging.ERROR)
4200
4201 def test_debug(self):
4202 self._test_log('debug')
4203
4204 def test_info(self):
4205 self._test_log('info')
4206
4207 def test_warning(self):
4208 self._test_log('warning')
4209
4210 def test_error(self):
4211 self._test_log('error')
4212
4213 def test_critical(self):
4214 self._test_log('critical')
4215
4216 def test_set_logger_class(self):
4217 self.assertRaises(TypeError, logging.setLoggerClass, object)
4218
4219 class MyLogger(logging.Logger):
4220 pass
4221
4222 logging.setLoggerClass(MyLogger)
4223 self.assertEqual(logging.getLoggerClass(), MyLogger)
4224
4225 logging.setLoggerClass(logging.Logger)
4226 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4227
Vinay Sajip01500012019-06-19 11:46:53 +01004228 def test_subclass_logger_cache(self):
4229 # bpo-37258
4230 message = []
4231
4232 class MyLogger(logging.getLoggerClass()):
4233 def __init__(self, name='MyLogger', level=logging.NOTSET):
4234 super().__init__(name, level)
4235 message.append('initialized')
4236
4237 logging.setLoggerClass(MyLogger)
4238 logger = logging.getLogger('just_some_logger')
4239 self.assertEqual(message, ['initialized'])
4240 stream = io.StringIO()
4241 h = logging.StreamHandler(stream)
4242 logger.addHandler(h)
4243 try:
4244 logger.setLevel(logging.DEBUG)
4245 logger.debug("hello")
4246 self.assertEqual(stream.getvalue().strip(), "hello")
4247
4248 stream.truncate(0)
4249 stream.seek(0)
4250
4251 logger.setLevel(logging.INFO)
4252 logger.debug("hello")
4253 self.assertEqual(stream.getvalue(), "")
4254 finally:
4255 logger.removeHandler(h)
4256 h.close()
4257 logging.setLoggerClass(logging.Logger)
4258
Antoine Pitrou712cb732013-12-21 15:51:54 +01004259 def test_logging_at_shutdown(self):
4260 # Issue #20037
4261 code = """if 1:
4262 import logging
4263
4264 class A:
4265 def __del__(self):
4266 try:
4267 raise ValueError("some error")
4268 except Exception:
4269 logging.exception("exception in __del__")
4270
4271 a = A()"""
4272 rc, out, err = assert_python_ok("-c", code)
4273 err = err.decode()
4274 self.assertIn("exception in __del__", err)
4275 self.assertIn("ValueError: some error", err)
4276
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004277 def test_recursion_error(self):
4278 # Issue 36272
4279 code = """if 1:
4280 import logging
4281
4282 def rec():
4283 logging.error("foo")
4284 rec()
4285
4286 rec()"""
4287 rc, out, err = assert_python_failure("-c", code)
4288 err = err.decode()
4289 self.assertNotIn("Cannot recover from stack overflow.", err)
4290 self.assertEqual(rc, 1)
4291
Antoine Pitrou712cb732013-12-21 15:51:54 +01004292
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004293class LogRecordTest(BaseTest):
4294 def test_str_rep(self):
4295 r = logging.makeLogRecord({})
4296 s = str(r)
4297 self.assertTrue(s.startswith('<LogRecord: '))
4298 self.assertTrue(s.endswith('>'))
4299
4300 def test_dict_arg(self):
4301 h = RecordingHandler()
4302 r = logging.getLogger()
4303 r.addHandler(h)
4304 d = {'less' : 'more' }
4305 logging.warning('less is %(less)s', d)
4306 self.assertIs(h.records[0].args, d)
4307 self.assertEqual(h.records[0].message, 'less is more')
4308 r.removeHandler(h)
4309 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004310
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004311 def test_multiprocessing(self):
4312 r = logging.makeLogRecord({})
4313 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004314 try:
4315 import multiprocessing as mp
4316 r = logging.makeLogRecord({})
4317 self.assertEqual(r.processName, mp.current_process().name)
4318 except ImportError:
4319 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004320
4321 def test_optional(self):
4322 r = logging.makeLogRecord({})
4323 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004324 NOT_NONE(r.thread)
4325 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004326 NOT_NONE(r.process)
4327 NOT_NONE(r.processName)
4328 log_threads = logging.logThreads
4329 log_processes = logging.logProcesses
4330 log_multiprocessing = logging.logMultiprocessing
4331 try:
4332 logging.logThreads = False
4333 logging.logProcesses = False
4334 logging.logMultiprocessing = False
4335 r = logging.makeLogRecord({})
4336 NONE = self.assertIsNone
4337 NONE(r.thread)
4338 NONE(r.threadName)
4339 NONE(r.process)
4340 NONE(r.processName)
4341 finally:
4342 logging.logThreads = log_threads
4343 logging.logProcesses = log_processes
4344 logging.logMultiprocessing = log_multiprocessing
4345
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004346class BasicConfigTest(unittest.TestCase):
4347
Vinay Sajip95bf5042011-04-20 11:50:56 +01004348 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004349
4350 def setUp(self):
4351 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004352 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004353 self.saved_handlers = logging._handlers.copy()
4354 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004355 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004356 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004357 logging.root.handlers = []
4358
4359 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004360 for h in logging.root.handlers[:]:
4361 logging.root.removeHandler(h)
4362 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004363 super(BasicConfigTest, self).tearDown()
4364
Vinay Sajip3def7e02011-04-20 10:58:06 +01004365 def cleanup(self):
4366 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004367 logging._handlers.clear()
4368 logging._handlers.update(self.saved_handlers)
4369 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004370 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004371
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004372 def test_no_kwargs(self):
4373 logging.basicConfig()
4374
4375 # handler defaults to a StreamHandler to sys.stderr
4376 self.assertEqual(len(logging.root.handlers), 1)
4377 handler = logging.root.handlers[0]
4378 self.assertIsInstance(handler, logging.StreamHandler)
4379 self.assertEqual(handler.stream, sys.stderr)
4380
4381 formatter = handler.formatter
4382 # format defaults to logging.BASIC_FORMAT
4383 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4384 # datefmt defaults to None
4385 self.assertIsNone(formatter.datefmt)
4386 # style defaults to %
4387 self.assertIsInstance(formatter._style, logging.PercentStyle)
4388
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004389 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004390 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004391
Vinay Sajip1fd12022014-01-13 21:59:56 +00004392 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004393 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004394 logging.basicConfig(stream=sys.stdout, style="{")
4395 logging.error("Log an error")
4396 sys.stdout.seek(0)
4397 self.assertEqual(output.getvalue().strip(),
4398 "ERROR:root:Log an error")
4399
4400 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004401 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004402 logging.basicConfig(stream=sys.stdout, style="$")
4403 logging.error("Log an error")
4404 sys.stdout.seek(0)
4405 self.assertEqual(output.getvalue().strip(),
4406 "ERROR:root:Log an error")
4407
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004408 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004409
4410 def cleanup(h1, h2, fn):
4411 h1.close()
4412 h2.close()
4413 os.remove(fn)
4414
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004415 logging.basicConfig(filename='test.log')
4416
4417 self.assertEqual(len(logging.root.handlers), 1)
4418 handler = logging.root.handlers[0]
4419 self.assertIsInstance(handler, logging.FileHandler)
4420
4421 expected = logging.FileHandler('test.log', 'a')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004422 self.assertEqual(handler.stream.mode, expected.stream.mode)
4423 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004424 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004425
4426 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004427
4428 def cleanup(h1, h2, fn):
4429 h1.close()
4430 h2.close()
4431 os.remove(fn)
4432
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004433 logging.basicConfig(filename='test.log', filemode='wb')
4434
4435 handler = logging.root.handlers[0]
4436 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004437 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004438 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004439
4440 def test_stream(self):
4441 stream = io.StringIO()
4442 self.addCleanup(stream.close)
4443 logging.basicConfig(stream=stream)
4444
4445 self.assertEqual(len(logging.root.handlers), 1)
4446 handler = logging.root.handlers[0]
4447 self.assertIsInstance(handler, logging.StreamHandler)
4448 self.assertEqual(handler.stream, stream)
4449
4450 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004451 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004452
4453 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004454 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004455
4456 def test_datefmt(self):
4457 logging.basicConfig(datefmt='bar')
4458
4459 formatter = logging.root.handlers[0].formatter
4460 self.assertEqual(formatter.datefmt, 'bar')
4461
4462 def test_style(self):
4463 logging.basicConfig(style='$')
4464
4465 formatter = logging.root.handlers[0].formatter
4466 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4467
4468 def test_level(self):
4469 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004470 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004471
4472 logging.basicConfig(level=57)
4473 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004474 # Test that second call has no effect
4475 logging.basicConfig(level=58)
4476 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004477
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004478 def test_incompatible(self):
4479 assertRaises = self.assertRaises
4480 handlers = [logging.StreamHandler()]
4481 stream = sys.stderr
4482 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004483 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004484 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004485 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004486 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004487 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004488 # Issue 23207: test for invalid kwargs
4489 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4490 # Should pop both filename and filemode even if filename is None
4491 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004492
4493 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004494 handlers = [
4495 logging.StreamHandler(),
4496 logging.StreamHandler(sys.stdout),
4497 logging.StreamHandler(),
4498 ]
4499 f = logging.Formatter()
4500 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004501 logging.basicConfig(handlers=handlers)
4502 self.assertIs(handlers[0], logging.root.handlers[0])
4503 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004504 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004505 self.assertIsNotNone(handlers[0].formatter)
4506 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004507 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004508 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4509
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004510 def test_force(self):
4511 old_string_io = io.StringIO()
4512 new_string_io = io.StringIO()
4513 old_handlers = [logging.StreamHandler(old_string_io)]
4514 new_handlers = [logging.StreamHandler(new_string_io)]
4515 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4516 logging.warning('warn')
4517 logging.info('info')
4518 logging.debug('debug')
4519 self.assertEqual(len(logging.root.handlers), 1)
4520 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4521 force=True)
4522 logging.warning('warn')
4523 logging.info('info')
4524 logging.debug('debug')
4525 self.assertEqual(len(logging.root.handlers), 1)
4526 self.assertEqual(old_string_io.getvalue().strip(),
4527 'WARNING:root:warn')
4528 self.assertEqual(new_string_io.getvalue().strip(),
4529 'WARNING:root:warn\nINFO:root:info')
4530
Vinay Sajipca7b5042019-06-17 17:40:52 +01004531 def test_encoding(self):
4532 try:
4533 encoding = 'utf-8'
4534 logging.basicConfig(filename='test.log', encoding=encoding,
4535 errors='strict',
4536 format='%(message)s', level=logging.DEBUG)
4537
4538 self.assertEqual(len(logging.root.handlers), 1)
4539 handler = logging.root.handlers[0]
4540 self.assertIsInstance(handler, logging.FileHandler)
4541 self.assertEqual(handler.encoding, encoding)
4542 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4543 finally:
4544 handler.close()
4545 with open('test.log', encoding='utf-8') as f:
4546 data = f.read().strip()
4547 os.remove('test.log')
4548 self.assertEqual(data,
4549 'The Øresund Bridge joins Copenhagen to Malmö')
4550
4551 def test_encoding_errors(self):
4552 try:
4553 encoding = 'ascii'
4554 logging.basicConfig(filename='test.log', encoding=encoding,
4555 errors='ignore',
4556 format='%(message)s', level=logging.DEBUG)
4557
4558 self.assertEqual(len(logging.root.handlers), 1)
4559 handler = logging.root.handlers[0]
4560 self.assertIsInstance(handler, logging.FileHandler)
4561 self.assertEqual(handler.encoding, encoding)
4562 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4563 finally:
4564 handler.close()
4565 with open('test.log', encoding='utf-8') as f:
4566 data = f.read().strip()
4567 os.remove('test.log')
4568 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4569
4570 def test_encoding_errors_default(self):
4571 try:
4572 encoding = 'ascii'
4573 logging.basicConfig(filename='test.log', encoding=encoding,
4574 format='%(message)s', level=logging.DEBUG)
4575
4576 self.assertEqual(len(logging.root.handlers), 1)
4577 handler = logging.root.handlers[0]
4578 self.assertIsInstance(handler, logging.FileHandler)
4579 self.assertEqual(handler.encoding, encoding)
4580 self.assertEqual(handler.errors, 'backslashreplace')
4581 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4582 finally:
4583 handler.close()
4584 with open('test.log', encoding='utf-8') as f:
4585 data = f.read().strip()
4586 os.remove('test.log')
4587 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4588 r'Bridge joins Copenhagen to Malm\xf6')
4589
4590 def test_encoding_errors_none(self):
4591 # Specifying None should behave as 'strict'
4592 try:
4593 encoding = 'ascii'
4594 logging.basicConfig(filename='test.log', encoding=encoding,
4595 errors=None,
4596 format='%(message)s', level=logging.DEBUG)
4597
4598 self.assertEqual(len(logging.root.handlers), 1)
4599 handler = logging.root.handlers[0]
4600 self.assertIsInstance(handler, logging.FileHandler)
4601 self.assertEqual(handler.encoding, encoding)
4602 self.assertIsNone(handler.errors)
4603
4604 message = []
4605
4606 def dummy_handle_error(record):
4607 _, v, _ = sys.exc_info()
4608 message.append(str(v))
4609
4610 handler.handleError = dummy_handle_error
4611 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4612 self.assertTrue(message)
4613 self.assertIn("'ascii' codec can't encode "
4614 "character '\\xd8' in position 4:", message[0])
4615 finally:
4616 handler.close()
4617 with open('test.log', encoding='utf-8') as f:
4618 data = f.read().strip()
4619 os.remove('test.log')
4620 # didn't write anything due to the encoding error
4621 self.assertEqual(data, r'')
4622
4623
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004624 def _test_log(self, method, level=None):
4625 # logging.root has no handlers so basicConfig should be called
4626 called = []
4627
4628 old_basic_config = logging.basicConfig
4629 def my_basic_config(*a, **kw):
4630 old_basic_config()
4631 old_level = logging.root.level
4632 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004633 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004634 called.append((a, kw))
4635
Vinay Sajip1feedb42014-05-31 08:19:12 +01004636 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004637
4638 log_method = getattr(logging, method)
4639 if level is not None:
4640 log_method(level, "test me")
4641 else:
4642 log_method("test me")
4643
4644 # basicConfig was called with no arguments
4645 self.assertEqual(called, [((), {})])
4646
4647 def test_log(self):
4648 self._test_log('log', logging.WARNING)
4649
4650 def test_debug(self):
4651 self._test_log('debug')
4652
4653 def test_info(self):
4654 self._test_log('info')
4655
4656 def test_warning(self):
4657 self._test_log('warning')
4658
4659 def test_error(self):
4660 self._test_log('error')
4661
4662 def test_critical(self):
4663 self._test_log('critical')
4664
4665
4666class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004667 def setUp(self):
4668 super(LoggerAdapterTest, self).setUp()
4669 old_handler_list = logging._handlerList[:]
4670
4671 self.recording = RecordingHandler()
4672 self.logger = logging.root
4673 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004674 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004675 self.addCleanup(self.recording.close)
4676
4677 def cleanup():
4678 logging._handlerList[:] = old_handler_list
4679
4680 self.addCleanup(cleanup)
4681 self.addCleanup(logging.shutdown)
4682 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4683
4684 def test_exception(self):
4685 msg = 'testing exception: %r'
4686 exc = None
4687 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004688 1 / 0
4689 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004690 exc = e
4691 self.adapter.exception(msg, self.recording)
4692
4693 self.assertEqual(len(self.recording.records), 1)
4694 record = self.recording.records[0]
4695 self.assertEqual(record.levelno, logging.ERROR)
4696 self.assertEqual(record.msg, msg)
4697 self.assertEqual(record.args, (self.recording,))
4698 self.assertEqual(record.exc_info,
4699 (exc.__class__, exc, exc.__traceback__))
4700
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004701 def test_exception_excinfo(self):
4702 try:
4703 1 / 0
4704 except ZeroDivisionError as e:
4705 exc = e
4706
4707 self.adapter.exception('exc_info test', exc_info=exc)
4708
4709 self.assertEqual(len(self.recording.records), 1)
4710 record = self.recording.records[0]
4711 self.assertEqual(record.exc_info,
4712 (exc.__class__, exc, exc.__traceback__))
4713
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004714 def test_critical(self):
4715 msg = 'critical test! %r'
4716 self.adapter.critical(msg, self.recording)
4717
4718 self.assertEqual(len(self.recording.records), 1)
4719 record = self.recording.records[0]
4720 self.assertEqual(record.levelno, logging.CRITICAL)
4721 self.assertEqual(record.msg, msg)
4722 self.assertEqual(record.args, (self.recording,))
4723
4724 def test_is_enabled_for(self):
4725 old_disable = self.adapter.logger.manager.disable
4726 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004727 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4728 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004729 self.assertFalse(self.adapter.isEnabledFor(32))
4730
4731 def test_has_handlers(self):
4732 self.assertTrue(self.adapter.hasHandlers())
4733
4734 for handler in self.logger.handlers:
4735 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004736
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004737 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004738 self.assertFalse(self.adapter.hasHandlers())
4739
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004740 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004741 class Adapter(logging.LoggerAdapter):
4742 prefix = 'Adapter'
4743
4744 def process(self, msg, kwargs):
4745 return f"{self.prefix} {msg}", kwargs
4746
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004747 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004748 adapter = Adapter(logger=self.logger, extra=None)
4749 adapter_adapter = Adapter(logger=adapter, extra=None)
4750 adapter_adapter.prefix = 'AdapterAdapter'
4751 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004752 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004753 self.assertEqual(len(self.recording.records), 1)
4754 record = self.recording.records[0]
4755 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004756 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004757 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004758 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004759 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004760 self.assertIs(self.logger.manager, orig_manager)
4761 temp_manager = object()
4762 try:
4763 adapter_adapter.manager = temp_manager
4764 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004765 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004766 self.assertIs(self.logger.manager, temp_manager)
4767 finally:
4768 adapter_adapter.manager = orig_manager
4769 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004770 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004771 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004772
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004773
4774class LoggerTest(BaseTest):
4775
4776 def setUp(self):
4777 super(LoggerTest, self).setUp()
4778 self.recording = RecordingHandler()
4779 self.logger = logging.Logger(name='blah')
4780 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004781 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004782 self.addCleanup(self.recording.close)
4783 self.addCleanup(logging.shutdown)
4784
4785 def test_set_invalid_level(self):
4786 self.assertRaises(TypeError, self.logger.setLevel, object())
4787
4788 def test_exception(self):
4789 msg = 'testing exception: %r'
4790 exc = None
4791 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004792 1 / 0
4793 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004794 exc = e
4795 self.logger.exception(msg, self.recording)
4796
4797 self.assertEqual(len(self.recording.records), 1)
4798 record = self.recording.records[0]
4799 self.assertEqual(record.levelno, logging.ERROR)
4800 self.assertEqual(record.msg, msg)
4801 self.assertEqual(record.args, (self.recording,))
4802 self.assertEqual(record.exc_info,
4803 (exc.__class__, exc, exc.__traceback__))
4804
4805 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004806 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004807 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004808
4809 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004810 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004811 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004812
4813 def test_find_caller_with_stack_info(self):
4814 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004815 support.patch(self, logging.traceback, 'print_stack',
4816 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004817
4818 self.logger.findCaller(stack_info=True)
4819
4820 self.assertEqual(len(called), 1)
4821 self.assertEqual('Stack (most recent call last):\n', called[0])
4822
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004823 def test_find_caller_with_stacklevel(self):
4824 the_level = 1
4825
4826 def innermost():
4827 self.logger.warning('test', stacklevel=the_level)
4828
4829 def inner():
4830 innermost()
4831
4832 def outer():
4833 inner()
4834
4835 records = self.recording.records
4836 outer()
4837 self.assertEqual(records[-1].funcName, 'innermost')
4838 lineno = records[-1].lineno
4839 the_level += 1
4840 outer()
4841 self.assertEqual(records[-1].funcName, 'inner')
4842 self.assertGreater(records[-1].lineno, lineno)
4843 lineno = records[-1].lineno
4844 the_level += 1
4845 outer()
4846 self.assertEqual(records[-1].funcName, 'outer')
4847 self.assertGreater(records[-1].lineno, lineno)
4848 lineno = records[-1].lineno
4849 the_level += 1
4850 outer()
4851 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
4852 self.assertGreater(records[-1].lineno, lineno)
4853
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004854 def test_make_record_with_extra_overwrite(self):
4855 name = 'my record'
4856 level = 13
4857 fn = lno = msg = args = exc_info = func = sinfo = None
4858 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
4859 exc_info, func, sinfo)
4860
4861 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
4862 extra = {key: 'some value'}
4863 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
4864 fn, lno, msg, args, exc_info,
4865 extra=extra, sinfo=sinfo)
4866
4867 def test_make_record_with_extra_no_overwrite(self):
4868 name = 'my record'
4869 level = 13
4870 fn = lno = msg = args = exc_info = func = sinfo = None
4871 extra = {'valid_key': 'some value'}
4872 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
4873 exc_info, extra=extra, sinfo=sinfo)
4874 self.assertIn('valid_key', result.__dict__)
4875
4876 def test_has_handlers(self):
4877 self.assertTrue(self.logger.hasHandlers())
4878
4879 for handler in self.logger.handlers:
4880 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004881 self.assertFalse(self.logger.hasHandlers())
4882
4883 def test_has_handlers_no_propagate(self):
4884 child_logger = logging.getLogger('blah.child')
4885 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004886 self.assertFalse(child_logger.hasHandlers())
4887
4888 def test_is_enabled_for(self):
4889 old_disable = self.logger.manager.disable
4890 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004891 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004892 self.assertFalse(self.logger.isEnabledFor(22))
4893
Timo Furrer6e3ca642018-06-01 09:29:46 +02004894 def test_is_enabled_for_disabled_logger(self):
4895 old_disabled = self.logger.disabled
4896 old_disable = self.logger.manager.disable
4897
4898 self.logger.disabled = True
4899 self.logger.manager.disable = 21
4900
4901 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
4902 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
4903
4904 self.assertFalse(self.logger.isEnabledFor(22))
4905
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004906 def test_root_logger_aliases(self):
4907 root = logging.getLogger()
4908 self.assertIs(root, logging.root)
4909 self.assertIs(root, logging.getLogger(None))
4910 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01004911 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004912 self.assertIs(root, logging.getLogger('foo').root)
4913 self.assertIs(root, logging.getLogger('foo.bar').root)
4914 self.assertIs(root, logging.getLogger('foo').parent)
4915
4916 self.assertIsNot(root, logging.getLogger('\0'))
4917 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
4918
4919 def test_invalid_names(self):
4920 self.assertRaises(TypeError, logging.getLogger, any)
4921 self.assertRaises(TypeError, logging.getLogger, b'foo')
4922
Vinay Sajip6260d9f2017-06-06 16:34:29 +01004923 def test_pickling(self):
4924 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
4925 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
4926 logger = logging.getLogger(name)
4927 s = pickle.dumps(logger, proto)
4928 unpickled = pickle.loads(s)
4929 self.assertIs(unpickled, logger)
4930
Avram Lubkin78c18a92017-07-30 05:36:33 -04004931 def test_caching(self):
4932 root = self.root_logger
4933 logger1 = logging.getLogger("abc")
4934 logger2 = logging.getLogger("abc.def")
4935
4936 # Set root logger level and ensure cache is empty
4937 root.setLevel(logging.ERROR)
4938 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
4939 self.assertEqual(logger2._cache, {})
4940
4941 # Ensure cache is populated and calls are consistent
4942 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4943 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
4944 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
4945 self.assertEqual(root._cache, {})
4946 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4947
4948 # Ensure root cache gets populated
4949 self.assertEqual(root._cache, {})
4950 self.assertTrue(root.isEnabledFor(logging.ERROR))
4951 self.assertEqual(root._cache, {logging.ERROR: True})
4952
4953 # Set parent logger level and ensure caches are emptied
4954 logger1.setLevel(logging.CRITICAL)
4955 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4956 self.assertEqual(logger2._cache, {})
4957
4958 # Ensure logger2 uses parent logger's effective level
4959 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4960
4961 # Set level to NOTSET and ensure caches are empty
4962 logger2.setLevel(logging.NOTSET)
4963 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4964 self.assertEqual(logger2._cache, {})
4965 self.assertEqual(logger1._cache, {})
4966 self.assertEqual(root._cache, {})
4967
4968 # Verify logger2 follows parent and not root
4969 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4970 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
4971 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
4972 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
4973 self.assertTrue(root.isEnabledFor(logging.ERROR))
4974
4975 # Disable logging in manager and ensure caches are clear
4976 logging.disable()
4977 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4978 self.assertEqual(logger2._cache, {})
4979 self.assertEqual(logger1._cache, {})
4980 self.assertEqual(root._cache, {})
4981
4982 # Ensure no loggers are enabled
4983 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
4984 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
4985 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
4986
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004987
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004988class BaseFileTest(BaseTest):
4989 "Base class for handler tests that write log files"
4990
4991 def setUp(self):
4992 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00004993 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
4994 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004995 self.rmfiles = []
4996
4997 def tearDown(self):
4998 for fn in self.rmfiles:
4999 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005000 if os.path.exists(self.fn):
5001 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005002 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005003
5004 def assertLogFile(self, filename):
5005 "Assert a log file is there and register it for deletion"
5006 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005007 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005008 self.rmfiles.append(filename)
5009
5010
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005011class FileHandlerTest(BaseFileTest):
5012 def test_delay(self):
5013 os.unlink(self.fn)
5014 fh = logging.FileHandler(self.fn, delay=True)
5015 self.assertIsNone(fh.stream)
5016 self.assertFalse(os.path.exists(self.fn))
5017 fh.handle(logging.makeLogRecord({}))
5018 self.assertIsNotNone(fh.stream)
5019 self.assertTrue(os.path.exists(self.fn))
5020 fh.close()
5021
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005022class RotatingFileHandlerTest(BaseFileTest):
5023 def next_rec(self):
5024 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5025 self.next_message(), None, None, None)
5026
5027 def test_should_not_rollover(self):
5028 # If maxbytes is zero rollover never occurs
5029 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
5030 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005031 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005032
5033 def test_should_rollover(self):
5034 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
5035 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005036 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005037
5038 def test_file_created(self):
5039 # checks that the file is created and assumes it was created
5040 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005041 rh = logging.handlers.RotatingFileHandler(self.fn)
5042 rh.emit(self.next_rec())
5043 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005044 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005045
5046 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005047 def namer(name):
5048 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005049 rh = logging.handlers.RotatingFileHandler(
5050 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005051 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005052 rh.emit(self.next_rec())
5053 self.assertLogFile(self.fn)
5054 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005055 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005056 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005057 self.assertLogFile(namer(self.fn + ".2"))
5058 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5059 rh.close()
5060
l0rb519cb872019-11-06 22:21:40 +01005061 def test_namer_rotator_inheritance(self):
5062 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5063 def namer(self, name):
5064 return name + ".test"
5065
5066 def rotator(self, source, dest):
5067 if os.path.exists(source):
5068 os.rename(source, dest + ".rotated")
5069
5070 rh = HandlerWithNamerAndRotator(
5071 self.fn, backupCount=2, maxBytes=1)
5072 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5073 rh.emit(self.next_rec())
5074 self.assertLogFile(self.fn)
5075 rh.emit(self.next_rec())
5076 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5077 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5078 rh.close()
5079
Hai Shia3ec3ad2020-05-19 06:02:57 +08005080 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005081 def test_rotator(self):
5082 def namer(name):
5083 return name + ".gz"
5084
5085 def rotator(source, dest):
5086 with open(source, "rb") as sf:
5087 data = sf.read()
5088 compressed = zlib.compress(data, 9)
5089 with open(dest, "wb") as df:
5090 df.write(compressed)
5091 os.remove(source)
5092
5093 rh = logging.handlers.RotatingFileHandler(
5094 self.fn, backupCount=2, maxBytes=1)
5095 rh.rotator = rotator
5096 rh.namer = namer
5097 m1 = self.next_rec()
5098 rh.emit(m1)
5099 self.assertLogFile(self.fn)
5100 m2 = self.next_rec()
5101 rh.emit(m2)
5102 fn = namer(self.fn + ".1")
5103 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005104 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005105 with open(fn, "rb") as f:
5106 compressed = f.read()
5107 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005108 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005109 rh.emit(self.next_rec())
5110 fn = namer(self.fn + ".2")
5111 self.assertLogFile(fn)
5112 with open(fn, "rb") as f:
5113 compressed = f.read()
5114 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005115 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005116 rh.emit(self.next_rec())
5117 fn = namer(self.fn + ".2")
5118 with open(fn, "rb") as f:
5119 compressed = f.read()
5120 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005121 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005122 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005123 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005124
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005125class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005126 # other test methods added below
5127 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01005128 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
5129 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005130 fmt = logging.Formatter('%(asctime)s %(message)s')
5131 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005132 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005133 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005134 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005135 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005136 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005137 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005138 fh.close()
5139 # At this point, we should have a recent rotated file which we
5140 # can test for the existence of. However, in practice, on some
5141 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005142 # in time to go to look for the log file. So, we go back a fair
5143 # bit, and stop as soon as we see a rotated file. In theory this
5144 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005145 found = False
5146 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005147 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005148 for secs in range(GO_BACK):
5149 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005150 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5151 found = os.path.exists(fn)
5152 if found:
5153 self.rmfiles.append(fn)
5154 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005155 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5156 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005157 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005158 dn, fn = os.path.split(self.fn)
5159 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005160 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5161 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005162 for f in files:
5163 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005164 path = os.path.join(dn, f)
5165 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005166 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005167 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005168
Vinay Sajip0372e102011-05-05 12:59:14 +01005169 def test_invalid(self):
5170 assertRaises = self.assertRaises
5171 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005172 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005173 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005174 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005175 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005176 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005177
Vinay Sajipa7130792013-04-12 17:04:23 +01005178 def test_compute_rollover_daily_attime(self):
5179 currentTime = 0
5180 atTime = datetime.time(12, 0, 0)
5181 rh = logging.handlers.TimedRotatingFileHandler(
5182 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
5183 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005184 try:
5185 actual = rh.computeRollover(currentTime)
5186 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005187
Vinay Sajipd86ac962013-04-14 12:20:46 +01005188 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5189 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5190 finally:
5191 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005192
Vinay Sajip10e8c492013-05-18 10:19:54 -07005193 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005194 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005195 currentTime = int(time.time())
5196 today = currentTime - currentTime % 86400
5197
Vinay Sajipa7130792013-04-12 17:04:23 +01005198 atTime = datetime.time(12, 0, 0)
5199
Vinay Sajip10e8c492013-05-18 10:19:54 -07005200 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005201 for day in range(7):
5202 rh = logging.handlers.TimedRotatingFileHandler(
5203 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
5204 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005205 try:
5206 if wday > day:
5207 # The rollover day has already passed this week, so we
5208 # go over into next week
5209 expected = (7 - wday + day)
5210 else:
5211 expected = (day - wday)
5212 # At this point expected is in days from now, convert to seconds
5213 expected *= 24 * 60 * 60
5214 # Add in the rollover time
5215 expected += 12 * 60 * 60
5216 # Add in adjustment for today
5217 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005218 actual = rh.computeRollover(today)
5219 if actual != expected:
5220 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005221 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005222 self.assertEqual(actual, expected)
5223 if day == wday:
5224 # goes into following week
5225 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005226 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005227 if actual != expected:
5228 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005229 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005230 self.assertEqual(actual, expected)
5231 finally:
5232 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005233
5234
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005235def secs(**kw):
5236 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5237
5238for when, exp in (('S', 1),
5239 ('M', 60),
5240 ('H', 60 * 60),
5241 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005242 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005243 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005244 ('W0', secs(days=4, hours=24)),
5245 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005246 def test_compute_rollover(self, when=when, exp=exp):
5247 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00005248 self.fn, when=when, interval=1, backupCount=0, utc=True)
5249 currentTime = 0.0
5250 actual = rh.computeRollover(currentTime)
5251 if exp != actual:
5252 # Failures occur on some systems for MIDNIGHT and W0.
5253 # Print detailed calculation for MIDNIGHT so we can try to see
5254 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005255 if when == 'MIDNIGHT':
5256 try:
5257 if rh.utc:
5258 t = time.gmtime(currentTime)
5259 else:
5260 t = time.localtime(currentTime)
5261 currentHour = t[3]
5262 currentMinute = t[4]
5263 currentSecond = t[5]
5264 # r is the number of seconds left between now and midnight
5265 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5266 currentMinute) * 60 +
5267 currentSecond)
5268 result = currentTime + r
5269 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5270 print('currentHour: %s' % currentHour, file=sys.stderr)
5271 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5272 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5273 print('r: %s' % r, file=sys.stderr)
5274 print('result: %s' % result, file=sys.stderr)
5275 except Exception:
5276 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5277 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005278 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005279 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5280
Vinay Sajip60ccd822011-05-09 17:32:09 +01005281
Vinay Sajip223349c2015-10-01 20:37:54 +01005282@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005283class NTEventLogHandlerTest(BaseTest):
5284 def test_basic(self):
5285 logtype = 'Application'
5286 elh = win32evtlog.OpenEventLog(None, logtype)
5287 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005288
5289 try:
5290 h = logging.handlers.NTEventLogHandler('test_logging')
5291 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005292 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005293 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005294 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005295
Vinay Sajip60ccd822011-05-09 17:32:09 +01005296 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5297 h.handle(r)
5298 h.close()
5299 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005300 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005301 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5302 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5303 found = False
5304 GO_BACK = 100
5305 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5306 for e in events:
5307 if e.SourceName != 'test_logging':
5308 continue
5309 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5310 if msg != 'Test Log Message\r\n':
5311 continue
5312 found = True
5313 break
5314 msg = 'Record not found in event log, went back %d records' % GO_BACK
5315 self.assertTrue(found, msg=msg)
5316
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005317
5318class MiscTestCase(unittest.TestCase):
5319 def test__all__(self):
5320 blacklist = {'logThreads', 'logMultiprocessing',
5321 'logProcesses', 'currentframe',
5322 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5323 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger',
Martin Panter94332cb2016-10-20 05:10:44 +00005324 'root', 'threading'}
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005325 support.check__all__(self, logging, blacklist=blacklist)
5326
5327
Christian Heimes180510d2008-03-03 19:15:45 +00005328# Set the locale to the platform-dependent default. I have no idea
5329# why the test does this, but in any case we save the current locale
5330# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005331@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005332def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005333 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005334 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5335 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5336 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5337 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5338 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5339 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5340 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5341 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005342 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005343 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005344 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005345 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005346 ]
5347 if hasattr(logging.handlers, 'QueueListener'):
5348 tests.append(QueueListenerTest)
5349 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005350
Christian Heimes180510d2008-03-03 19:15:45 +00005351if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005352 test_main()