blob: eb5b926908a192445622024610542c7873c834aa [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
Hai Shi3ddc6342020-06-30 21:46:06 +080045from test.support import os_helper
Serhiy Storchaka16994912020-04-25 10:06:29 +030046from test.support import socket_helper
Hai Shie80697d2020-05-28 06:10:27 +080047from test.support import threading_helper
Hai Shi3ddc6342020-06-30 21:46:06 +080048from test.support import warnings_helper
Serhiy Storchaka515fce42020-04-25 11:35:18 +030049from test.support.logging_helper import TestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000050import textwrap
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020051import threading
Vinay Sajip37eb3382011-04-26 20:26:41 +010052import time
Christian Heimes180510d2008-03-03 19:15:45 +000053import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000054import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000055import weakref
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020056
57import asyncore
58from http.server import HTTPServer, BaseHTTPRequestHandler
59import smtpd
60from urllib.parse import urlparse, parse_qs
61from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
62 ThreadingTCPServer, StreamRequestHandler)
63
Vinay Sajip60ccd822011-05-09 17:32:09 +010064try:
Vinay Sajip223349c2015-10-01 20:37:54 +010065 import win32evtlog, win32evtlogutil, pywintypes
Vinay Sajip60ccd822011-05-09 17:32:09 +010066except ImportError:
Vinay Sajip223349c2015-10-01 20:37:54 +010067 win32evtlog = win32evtlogutil = pywintypes = None
68
Eric V. Smith851cad72012-03-11 22:46:04 -070069try:
70 import zlib
71except ImportError:
72 pass
Christian Heimes18c66892008-02-17 13:31:39 +000073
Christian Heimes180510d2008-03-03 19:15:45 +000074class BaseTest(unittest.TestCase):
75
76 """Base class for logging tests."""
77
78 log_format = "%(name)s -> %(levelname)s: %(message)s"
Ezio Melotti3ffd29b2012-10-02 19:45:00 +030079 expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +000080 message_num = 0
81
82 def setUp(self):
83 """Setup the default logging stream to an internal StringIO instance,
84 so that we can examine log output as we want."""
Hai Shie80697d2020-05-28 06:10:27 +080085 self._threading_key = threading_helper.threading_setup()
Victor Stinner69669602017-08-18 23:47:54 +020086
Christian Heimes180510d2008-03-03 19:15:45 +000087 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000088 logging._acquireLock()
89 try:
Christian Heimes180510d2008-03-03 19:15:45 +000090 self.saved_handlers = logging._handlers.copy()
91 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000092 self.saved_loggers = saved_loggers = logger_dict.copy()
Vinay Sajip3b84eae2013-05-25 03:20:34 -070093 self.saved_name_to_level = logging._nameToLevel.copy()
94 self.saved_level_to_name = logging._levelToName.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000095 self.logger_states = logger_states = {}
96 for name in saved_loggers:
97 logger_states[name] = getattr(saved_loggers[name],
98 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000099 finally:
100 logging._releaseLock()
101
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100102 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000103 self.logger1 = logging.getLogger("\xab\xd7\xbb")
104 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000105
Christian Heimes180510d2008-03-03 19:15:45 +0000106 self.root_logger = logging.getLogger("")
107 self.original_logging_level = self.root_logger.getEffectiveLevel()
108
109 self.stream = io.StringIO()
110 self.root_logger.setLevel(logging.DEBUG)
111 self.root_hdlr = logging.StreamHandler(self.stream)
112 self.root_formatter = logging.Formatter(self.log_format)
113 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000114 if self.logger1.hasHandlers():
115 hlist = self.logger1.handlers + self.root_logger.handlers
116 raise AssertionError('Unexpected handlers: %s' % hlist)
117 if self.logger2.hasHandlers():
118 hlist = self.logger2.handlers + self.root_logger.handlers
119 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000120 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000121 self.assertTrue(self.logger1.hasHandlers())
122 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000123
124 def tearDown(self):
125 """Remove our logging stream, and restore the original logging
126 level."""
127 self.stream.close()
128 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000129 while self.root_logger.handlers:
130 h = self.root_logger.handlers[0]
131 self.root_logger.removeHandler(h)
132 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000133 self.root_logger.setLevel(self.original_logging_level)
134 logging._acquireLock()
135 try:
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700136 logging._levelToName.clear()
137 logging._levelToName.update(self.saved_level_to_name)
138 logging._nameToLevel.clear()
139 logging._nameToLevel.update(self.saved_name_to_level)
Christian Heimes180510d2008-03-03 19:15:45 +0000140 logging._handlers.clear()
141 logging._handlers.update(self.saved_handlers)
142 logging._handlerList[:] = self.saved_handler_list
Avram Lubkin78c18a92017-07-30 05:36:33 -0400143 manager = logging.getLogger().manager
144 manager.disable = 0
145 loggerDict = manager.loggerDict
Christian Heimes180510d2008-03-03 19:15:45 +0000146 loggerDict.clear()
147 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000148 logger_states = self.logger_states
149 for name in self.logger_states:
150 if logger_states[name] is not None:
151 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000152 finally:
153 logging._releaseLock()
154
Victor Stinner69669602017-08-18 23:47:54 +0200155 self.doCleanups()
Hai Shie80697d2020-05-28 06:10:27 +0800156 threading_helper.threading_cleanup(*self._threading_key)
Victor Stinner69669602017-08-18 23:47:54 +0200157
Vinay Sajip4ded5512012-10-02 15:56:16 +0100158 def assert_log_lines(self, expected_values, stream=None, pat=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000159 """Match the collected log lines against the regular expression
160 self.expected_log_pat, and compare the extracted group values to
161 the expected_values list of tuples."""
162 stream = stream or self.stream
Vinay Sajip4ded5512012-10-02 15:56:16 +0100163 pat = re.compile(pat or self.expected_log_pat)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300164 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100165 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000166 for actual, expected in zip(actual_lines, expected_values):
167 match = pat.search(actual)
168 if not match:
169 self.fail("Log line does not match expected pattern:\n" +
170 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000171 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000172 s = stream.read()
173 if s:
174 self.fail("Remaining output at end of log stream:\n" + s)
175
176 def next_message(self):
177 """Generate a message consisting solely of an auto-incrementing
178 integer."""
179 self.message_num += 1
180 return "%d" % self.message_num
181
182
183class BuiltinLevelsTest(BaseTest):
184 """Test builtin levels and their inheritance."""
185
186 def test_flat(self):
Mike53f7a7c2017-12-14 14:04:53 +0300187 # Logging levels in a flat logger namespace.
Christian Heimes180510d2008-03-03 19:15:45 +0000188 m = self.next_message
189
190 ERR = logging.getLogger("ERR")
191 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000192 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000193 INF.setLevel(logging.INFO)
194 DEB = logging.getLogger("DEB")
195 DEB.setLevel(logging.DEBUG)
196
197 # These should log.
198 ERR.log(logging.CRITICAL, m())
199 ERR.error(m())
200
201 INF.log(logging.CRITICAL, m())
202 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100203 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000204 INF.info(m())
205
206 DEB.log(logging.CRITICAL, m())
207 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100208 DEB.warning(m())
209 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000210 DEB.debug(m())
211
212 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100213 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000214 ERR.info(m())
215 ERR.debug(m())
216
217 INF.debug(m())
218
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000219 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000220 ('ERR', 'CRITICAL', '1'),
221 ('ERR', 'ERROR', '2'),
222 ('INF', 'CRITICAL', '3'),
223 ('INF', 'ERROR', '4'),
224 ('INF', 'WARNING', '5'),
225 ('INF', 'INFO', '6'),
226 ('DEB', 'CRITICAL', '7'),
227 ('DEB', 'ERROR', '8'),
228 ('DEB', 'WARNING', '9'),
229 ('DEB', 'INFO', '10'),
230 ('DEB', 'DEBUG', '11'),
231 ])
232
233 def test_nested_explicit(self):
234 # Logging levels in a nested namespace, all explicitly set.
235 m = self.next_message
236
237 INF = logging.getLogger("INF")
238 INF.setLevel(logging.INFO)
239 INF_ERR = logging.getLogger("INF.ERR")
240 INF_ERR.setLevel(logging.ERROR)
241
242 # These should log.
243 INF_ERR.log(logging.CRITICAL, m())
244 INF_ERR.error(m())
245
246 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100247 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000248 INF_ERR.info(m())
249 INF_ERR.debug(m())
250
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000251 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000252 ('INF.ERR', 'CRITICAL', '1'),
253 ('INF.ERR', 'ERROR', '2'),
254 ])
255
256 def test_nested_inherited(self):
Mike53f7a7c2017-12-14 14:04:53 +0300257 # Logging levels in a nested namespace, inherited from parent loggers.
Christian Heimes180510d2008-03-03 19:15:45 +0000258 m = self.next_message
259
260 INF = logging.getLogger("INF")
261 INF.setLevel(logging.INFO)
262 INF_ERR = logging.getLogger("INF.ERR")
263 INF_ERR.setLevel(logging.ERROR)
264 INF_UNDEF = logging.getLogger("INF.UNDEF")
265 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
266 UNDEF = logging.getLogger("UNDEF")
267
268 # These should log.
269 INF_UNDEF.log(logging.CRITICAL, m())
270 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100271 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000272 INF_UNDEF.info(m())
273 INF_ERR_UNDEF.log(logging.CRITICAL, m())
274 INF_ERR_UNDEF.error(m())
275
276 # These should not log.
277 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100278 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000279 INF_ERR_UNDEF.info(m())
280 INF_ERR_UNDEF.debug(m())
281
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000282 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000283 ('INF.UNDEF', 'CRITICAL', '1'),
284 ('INF.UNDEF', 'ERROR', '2'),
285 ('INF.UNDEF', 'WARNING', '3'),
286 ('INF.UNDEF', 'INFO', '4'),
287 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
288 ('INF.ERR.UNDEF', 'ERROR', '6'),
289 ])
290
291 def test_nested_with_virtual_parent(self):
292 # Logging levels when some parent does not exist yet.
293 m = self.next_message
294
295 INF = logging.getLogger("INF")
296 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
297 CHILD = logging.getLogger("INF.BADPARENT")
298 INF.setLevel(logging.INFO)
299
300 # These should log.
301 GRANDCHILD.log(logging.FATAL, m())
302 GRANDCHILD.info(m())
303 CHILD.log(logging.FATAL, m())
304 CHILD.info(m())
305
306 # These should not log.
307 GRANDCHILD.debug(m())
308 CHILD.debug(m())
309
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000310 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000311 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
312 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
313 ('INF.BADPARENT', 'CRITICAL', '3'),
314 ('INF.BADPARENT', 'INFO', '4'),
315 ])
316
Vinay Sajipd1d4fbf2014-09-11 23:06:09 +0100317 def test_regression_22386(self):
318 """See issue #22386 for more information."""
319 self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
320 self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
Christian Heimes180510d2008-03-03 19:15:45 +0000321
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100322 def test_issue27935(self):
323 fatal = logging.getLevelName('FATAL')
324 self.assertEqual(fatal, logging.FATAL)
325
Vinay Sajip924aaae2017-01-11 17:35:36 +0000326 def test_regression_29220(self):
327 """See issue #29220 for more information."""
328 logging.addLevelName(logging.INFO, '')
329 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
330 self.assertEqual(logging.getLevelName(logging.INFO), '')
331 self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
332 self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
333
Christian Heimes180510d2008-03-03 19:15:45 +0000334class BasicFilterTest(BaseTest):
335
336 """Test the bundled Filter class."""
337
338 def test_filter(self):
339 # Only messages satisfying the specified criteria pass through the
340 # filter.
341 filter_ = logging.Filter("spam.eggs")
342 handler = self.root_logger.handlers[0]
343 try:
344 handler.addFilter(filter_)
345 spam = logging.getLogger("spam")
346 spam_eggs = logging.getLogger("spam.eggs")
347 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
348 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
349
350 spam.info(self.next_message())
351 spam_eggs.info(self.next_message()) # Good.
352 spam_eggs_fish.info(self.next_message()) # Good.
353 spam_bakedbeans.info(self.next_message())
354
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000355 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000356 ('spam.eggs', 'INFO', '2'),
357 ('spam.eggs.fish', 'INFO', '3'),
358 ])
359 finally:
360 handler.removeFilter(filter_)
361
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000362 def test_callable_filter(self):
363 # Only messages satisfying the specified criteria pass through the
364 # filter.
365
366 def filterfunc(record):
367 parts = record.name.split('.')
368 prefix = '.'.join(parts[:2])
369 return prefix == 'spam.eggs'
370
371 handler = self.root_logger.handlers[0]
372 try:
373 handler.addFilter(filterfunc)
374 spam = logging.getLogger("spam")
375 spam_eggs = logging.getLogger("spam.eggs")
376 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
377 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
378
379 spam.info(self.next_message())
380 spam_eggs.info(self.next_message()) # Good.
381 spam_eggs_fish.info(self.next_message()) # Good.
382 spam_bakedbeans.info(self.next_message())
383
384 self.assert_log_lines([
385 ('spam.eggs', 'INFO', '2'),
386 ('spam.eggs.fish', 'INFO', '3'),
387 ])
388 finally:
389 handler.removeFilter(filterfunc)
390
Vinay Sajip985ef872011-04-26 19:34:04 +0100391 def test_empty_filter(self):
392 f = logging.Filter()
393 r = logging.makeLogRecord({'name': 'spam.eggs'})
394 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000395
396#
397# First, we define our levels. There can be as many as you want - the only
398# limitations are that they should be integers, the lowest should be > 0 and
399# larger values mean less information being logged. If you need specific
400# level values which do not fit into these limitations, you can use a
401# mapping dictionary to convert between your application levels and the
402# logging system.
403#
404SILENT = 120
405TACITURN = 119
406TERSE = 118
407EFFUSIVE = 117
408SOCIABLE = 116
409VERBOSE = 115
410TALKATIVE = 114
411GARRULOUS = 113
412CHATTERBOX = 112
413BORING = 111
414
415LEVEL_RANGE = range(BORING, SILENT + 1)
416
417#
418# Next, we define names for our levels. You don't need to do this - in which
419# case the system will use "Level n" to denote the text for the level.
420#
421my_logging_levels = {
422 SILENT : 'Silent',
423 TACITURN : 'Taciturn',
424 TERSE : 'Terse',
425 EFFUSIVE : 'Effusive',
426 SOCIABLE : 'Sociable',
427 VERBOSE : 'Verbose',
428 TALKATIVE : 'Talkative',
429 GARRULOUS : 'Garrulous',
430 CHATTERBOX : 'Chatterbox',
431 BORING : 'Boring',
432}
433
434class GarrulousFilter(logging.Filter):
435
436 """A filter which blocks garrulous messages."""
437
438 def filter(self, record):
439 return record.levelno != GARRULOUS
440
441class VerySpecificFilter(logging.Filter):
442
443 """A filter which blocks sociable and taciturn messages."""
444
445 def filter(self, record):
446 return record.levelno not in [SOCIABLE, TACITURN]
447
448
449class CustomLevelsAndFiltersTest(BaseTest):
450
451 """Test various filtering possibilities with custom logging levels."""
452
453 # Skip the logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +0300454 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +0000455
456 def setUp(self):
457 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000458 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000459 logging.addLevelName(k, v)
460
461 def log_at_all_levels(self, logger):
462 for lvl in LEVEL_RANGE:
463 logger.log(lvl, self.next_message())
464
465 def test_logger_filter(self):
466 # Filter at logger level.
467 self.root_logger.setLevel(VERBOSE)
468 # Levels >= 'Verbose' are good.
469 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000470 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000471 ('Verbose', '5'),
472 ('Sociable', '6'),
473 ('Effusive', '7'),
474 ('Terse', '8'),
475 ('Taciturn', '9'),
476 ('Silent', '10'),
477 ])
478
479 def test_handler_filter(self):
480 # Filter at handler level.
481 self.root_logger.handlers[0].setLevel(SOCIABLE)
482 try:
483 # Levels >= 'Sociable' are good.
484 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000485 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000486 ('Sociable', '6'),
487 ('Effusive', '7'),
488 ('Terse', '8'),
489 ('Taciturn', '9'),
490 ('Silent', '10'),
491 ])
492 finally:
493 self.root_logger.handlers[0].setLevel(logging.NOTSET)
494
495 def test_specific_filters(self):
496 # Set a specific filter object on the handler, and then add another
497 # filter object on the logger itself.
498 handler = self.root_logger.handlers[0]
499 specific_filter = None
500 garr = GarrulousFilter()
501 handler.addFilter(garr)
502 try:
503 self.log_at_all_levels(self.root_logger)
504 first_lines = [
505 # Notice how 'Garrulous' is missing
506 ('Boring', '1'),
507 ('Chatterbox', '2'),
508 ('Talkative', '4'),
509 ('Verbose', '5'),
510 ('Sociable', '6'),
511 ('Effusive', '7'),
512 ('Terse', '8'),
513 ('Taciturn', '9'),
514 ('Silent', '10'),
515 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000516 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000517
518 specific_filter = VerySpecificFilter()
519 self.root_logger.addFilter(specific_filter)
520 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000521 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000522 # Not only 'Garrulous' is still missing, but also 'Sociable'
523 # and 'Taciturn'
524 ('Boring', '11'),
525 ('Chatterbox', '12'),
526 ('Talkative', '14'),
527 ('Verbose', '15'),
528 ('Effusive', '17'),
529 ('Terse', '18'),
530 ('Silent', '20'),
531 ])
532 finally:
533 if specific_filter:
534 self.root_logger.removeFilter(specific_filter)
535 handler.removeFilter(garr)
536
537
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100538class HandlerTest(BaseTest):
539 def test_name(self):
540 h = logging.Handler()
541 h.name = 'generic'
542 self.assertEqual(h.name, 'generic')
543 h.name = 'anothergeneric'
544 self.assertEqual(h.name, 'anothergeneric')
545 self.assertRaises(NotImplementedError, h.emit, None)
546
Vinay Sajip5a35b062011-04-27 11:31:14 +0100547 def test_builtin_handlers(self):
548 # We can't actually *use* too many handlers in the tests,
549 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200550 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100551 for existing in (True, False):
552 fd, fn = tempfile.mkstemp()
553 os.close(fd)
554 if not existing:
555 os.unlink(fn)
556 h = logging.handlers.WatchedFileHandler(fn, delay=True)
557 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100558 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100559 self.assertEqual(dev, -1)
560 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100561 r = logging.makeLogRecord({'msg': 'Test'})
562 h.handle(r)
563 # Now remove the file.
564 os.unlink(fn)
565 self.assertFalse(os.path.exists(fn))
566 # The next call should recreate the file.
567 h.handle(r)
568 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100569 else:
570 self.assertEqual(h.dev, -1)
571 self.assertEqual(h.ino, -1)
572 h.close()
573 if existing:
574 os.unlink(fn)
575 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100576 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100577 else:
578 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100579 try:
580 h = logging.handlers.SysLogHandler(sockname)
581 self.assertEqual(h.facility, h.LOG_USER)
582 self.assertTrue(h.unixsocket)
583 h.close()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200584 except OSError: # syslogd might not be available
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100585 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100586 for method in ('GET', 'POST', 'PUT'):
587 if method == 'PUT':
588 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
589 'localhost', '/log', method)
590 else:
591 h = logging.handlers.HTTPHandler('localhost', '/log', method)
592 h.close()
593 h = logging.handlers.BufferingHandler(0)
594 r = logging.makeLogRecord({})
595 self.assertTrue(h.shouldFlush(r))
596 h.close()
597 h = logging.handlers.BufferingHandler(1)
598 self.assertFalse(h.shouldFlush(r))
599 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100600
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100601 def test_path_objects(self):
602 """
603 Test that Path objects are accepted as filename arguments to handlers.
604
605 See Issue #27493.
606 """
607 fd, fn = tempfile.mkstemp()
608 os.close(fd)
609 os.unlink(fn)
610 pfn = pathlib.Path(fn)
611 cases = (
612 (logging.FileHandler, (pfn, 'w')),
613 (logging.handlers.RotatingFileHandler, (pfn, 'a')),
614 (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
615 )
616 if sys.platform in ('linux', 'darwin'):
617 cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
618 for cls, args in cases:
619 h = cls(*args)
620 self.assertTrue(os.path.exists(fn))
621 h.close()
622 os.unlink(fn)
623
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100624 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100625 def test_race(self):
626 # Issue #14632 refers.
627 def remove_loop(fname, tries):
628 for _ in range(tries):
629 try:
630 os.unlink(fname)
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000631 self.deletion_time = time.time()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100632 except OSError:
633 pass
634 time.sleep(0.004 * random.randint(0, 4))
635
Vinay Sajipc94871a2012-04-25 10:51:35 +0100636 del_count = 500
637 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100638
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000639 self.handle_time = None
640 self.deletion_time = None
641
Vinay Sajipa5798de2012-04-24 23:33:33 +0100642 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100643 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
644 os.close(fd)
645 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
646 remover.daemon = True
647 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100648 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100649 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
650 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100651 try:
652 for _ in range(log_count):
653 time.sleep(0.005)
654 r = logging.makeLogRecord({'msg': 'testing' })
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000655 try:
656 self.handle_time = time.time()
657 h.handle(r)
658 except Exception:
659 print('Deleted at %s, '
660 'opened at %s' % (self.deletion_time,
661 self.handle_time))
662 raise
Vinay Sajipc94871a2012-04-25 10:51:35 +0100663 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100664 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100665 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100666 if os.path.exists(fn):
667 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100668
Gregory P. Smith19003842018-09-13 22:08:31 -0700669 # The implementation relies on os.register_at_fork existing, but we test
670 # based on os.fork existing because that is what users and this test use.
671 # This helps ensure that when fork exists (the important concept) that the
672 # register_at_fork mechanism is also present and used.
673 @unittest.skipIf(not hasattr(os, 'fork'), 'Test requires os.fork().')
674 def test_post_fork_child_no_deadlock(self):
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400675 """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
676 class _OurHandler(logging.Handler):
677 def __init__(self):
678 super().__init__()
679 self.sub_handler = logging.StreamHandler(
680 stream=open('/dev/null', 'wt'))
681
682 def emit(self, record):
683 self.sub_handler.acquire()
684 try:
685 self.sub_handler.emit(record)
686 finally:
687 self.sub_handler.release()
688
689 self.assertEqual(len(logging._handlers), 0)
690 refed_h = _OurHandler()
Xtreak2c105382019-05-13 20:18:52 +0530691 self.addCleanup(refed_h.sub_handler.stream.close)
Gregory P. Smith19003842018-09-13 22:08:31 -0700692 refed_h.name = 'because we need at least one for this test'
693 self.assertGreater(len(logging._handlers), 0)
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400694 self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
695 test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
696 test_logger.addHandler(refed_h)
697 test_logger.setLevel(logging.DEBUG)
Gregory P. Smith19003842018-09-13 22:08:31 -0700698
699 locks_held__ready_to_fork = threading.Event()
700 fork_happened__release_locks_and_end_thread = threading.Event()
701
702 def lock_holder_thread_fn():
703 logging._acquireLock()
704 try:
705 refed_h.acquire()
706 try:
707 # Tell the main thread to do the fork.
708 locks_held__ready_to_fork.set()
709
710 # If the deadlock bug exists, the fork will happen
711 # without dealing with the locks we hold, deadlocking
712 # the child.
713
714 # Wait for a successful fork or an unreasonable amount of
715 # time before releasing our locks. To avoid a timing based
716 # test we'd need communication from os.fork() as to when it
717 # has actually happened. Given this is a regression test
718 # for a fixed issue, potentially less reliably detecting
719 # regression via timing is acceptable for simplicity.
720 # The test will always take at least this long. :(
721 fork_happened__release_locks_and_end_thread.wait(0.5)
722 finally:
723 refed_h.release()
724 finally:
725 logging._releaseLock()
726
727 lock_holder_thread = threading.Thread(
728 target=lock_holder_thread_fn,
729 name='test_post_fork_child_no_deadlock lock holder')
730 lock_holder_thread.start()
731
732 locks_held__ready_to_fork.wait()
733 pid = os.fork()
Victor Stinner278c1e12020-03-31 20:08:12 +0200734 if pid == 0:
735 # Child process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400736 try:
737 test_logger.info(r'Child process did not deadlock. \o/')
738 finally:
739 os._exit(0)
Victor Stinner278c1e12020-03-31 20:08:12 +0200740 else:
741 # Parent process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400742 test_logger.info(r'Parent process returned from fork. \o/')
Gregory P. Smith19003842018-09-13 22:08:31 -0700743 fork_happened__release_locks_and_end_thread.set()
744 lock_holder_thread.join()
Victor Stinner278c1e12020-03-31 20:08:12 +0200745
746 support.wait_process(pid, exitcode=0)
Gregory P. Smith19003842018-09-13 22:08:31 -0700747
Vinay Sajipa5798de2012-04-24 23:33:33 +0100748
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100749class BadStream(object):
750 def write(self, data):
751 raise RuntimeError('deliberate mistake')
752
753class TestStreamHandler(logging.StreamHandler):
754 def handleError(self, record):
755 self.error_record = record
756
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200757class StreamWithIntName(object):
758 level = logging.NOTSET
759 name = 2
760
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100761class StreamHandlerTest(BaseTest):
762 def test_error_handling(self):
763 h = TestStreamHandler(BadStream())
764 r = logging.makeLogRecord({})
765 old_raise = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +0100766
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100767 try:
768 h.handle(r)
769 self.assertIs(h.error_record, r)
Vinay Sajip1feedb42014-05-31 08:19:12 +0100770
Vinay Sajip985ef872011-04-26 19:34:04 +0100771 h = logging.StreamHandler(BadStream())
Vinay Sajip1feedb42014-05-31 08:19:12 +0100772 with support.captured_stderr() as stderr:
773 h.handle(r)
774 msg = '\nRuntimeError: deliberate mistake\n'
775 self.assertIn(msg, stderr.getvalue())
776
Vinay Sajip985ef872011-04-26 19:34:04 +0100777 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +0100778 with support.captured_stderr() as stderr:
779 h.handle(r)
780 self.assertEqual('', stderr.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100781 finally:
782 logging.raiseExceptions = old_raise
783
Vinay Sajip2543f502017-07-30 10:41:45 +0100784 def test_stream_setting(self):
785 """
786 Test setting the handler's stream
787 """
788 h = logging.StreamHandler()
789 stream = io.StringIO()
790 old = h.setStream(stream)
791 self.assertIs(old, sys.stderr)
792 actual = h.setStream(old)
793 self.assertIs(actual, stream)
794 # test that setting to existing value returns None
795 actual = h.setStream(old)
796 self.assertIsNone(actual)
797
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200798 def test_can_represent_stream_with_int_name(self):
799 h = logging.StreamHandler(StreamWithIntName())
800 self.assertEqual(repr(h), '<StreamHandler 2 (NOTSET)>')
801
Vinay Sajip7367d082011-05-02 13:17:27 +0100802# -- The following section could be moved into a server_helper.py module
803# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100804
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200805class TestSMTPServer(smtpd.SMTPServer):
806 """
807 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100808
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200809 :param addr: A (host, port) tuple which the server listens on.
810 You can specify a port value of zero: the server's
811 *port* attribute will hold the actual port number
812 used, which can be used in client connections.
813 :param handler: A callable which will be called to process
814 incoming messages. The handler will be passed
815 the client address tuple, who the message is from,
816 a list of recipients and the message data.
817 :param poll_interval: The interval, in seconds, used in the underlying
818 :func:`select` or :func:`poll` call by
819 :func:`asyncore.loop`.
820 :param sockmap: A dictionary which will be used to hold
821 :class:`asyncore.dispatcher` instances used by
822 :func:`asyncore.loop`. This avoids changing the
823 :mod:`asyncore` module's global state.
824 """
825
826 def __init__(self, addr, handler, poll_interval, sockmap):
827 smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
828 decode_data=True)
829 self.port = self.socket.getsockname()[1]
830 self._handler = handler
831 self._thread = None
832 self.poll_interval = poll_interval
833
834 def process_message(self, peer, mailfrom, rcpttos, data):
835 """
836 Delegates to the handler passed in to the server's constructor.
837
838 Typically, this will be a test case method.
839 :param peer: The client (host, port) tuple.
840 :param mailfrom: The address of the sender.
841 :param rcpttos: The addresses of the recipients.
842 :param data: The message.
843 """
844 self._handler(peer, mailfrom, rcpttos, data)
845
846 def start(self):
847 """
848 Start the server running on a separate daemon thread.
849 """
850 self._thread = t = threading.Thread(target=self.serve_forever,
851 args=(self.poll_interval,))
852 t.setDaemon(True)
853 t.start()
854
855 def serve_forever(self, poll_interval):
856 """
857 Run the :mod:`asyncore` loop until normal termination
858 conditions arise.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100859 :param poll_interval: The interval, in seconds, used in the underlying
860 :func:`select` or :func:`poll` call by
861 :func:`asyncore.loop`.
862 """
Victor Stinner13ff2452018-01-22 18:32:50 +0100863 asyncore.loop(poll_interval, map=self._map)
Vinay Sajipce7c9782011-05-17 07:15:53 +0100864
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100865 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200866 """
867 Stop the thread by closing the server instance.
868 Wait for the server thread to terminate.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200869 """
870 self.close()
Hai Shie80697d2020-05-28 06:10:27 +0800871 threading_helper.join_thread(self._thread)
Victor Stinnerb9b69002017-09-14 14:40:56 -0700872 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200873 asyncore.close_all(map=self._map, ignore_all=True)
Vinay Sajipa463d252011-04-30 21:52:48 +0100874
Vinay Sajip7367d082011-05-02 13:17:27 +0100875
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200876class ControlMixin(object):
877 """
878 This mixin is used to start a server on a separate thread, and
879 shut it down programmatically. Request handling is simplified - instead
880 of needing to derive a suitable RequestHandler subclass, you just
881 provide a callable which will be passed each received request to be
882 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100883
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200884 :param handler: A handler callable which will be called with a
885 single parameter - the request - in order to
886 process the request. This handler is called on the
887 server thread, effectively meaning that requests are
888 processed serially. While not quite Web scale ;-),
889 this should be fine for testing applications.
890 :param poll_interval: The polling interval in seconds.
891 """
892 def __init__(self, handler, poll_interval):
893 self._thread = None
894 self.poll_interval = poll_interval
895 self._handler = handler
896 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100897
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200898 def start(self):
899 """
900 Create a daemon thread to run the server, and start it.
901 """
902 self._thread = t = threading.Thread(target=self.serve_forever,
903 args=(self.poll_interval,))
904 t.setDaemon(True)
905 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100906
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200907 def serve_forever(self, poll_interval):
908 """
909 Run the server. Set the ready flag before entering the
910 service loop.
911 """
912 self.ready.set()
913 super(ControlMixin, self).serve_forever(poll_interval)
914
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100915 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200916 """
917 Tell the server thread to stop, and wait for it to do so.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200918 """
919 self.shutdown()
920 if self._thread is not None:
Hai Shie80697d2020-05-28 06:10:27 +0800921 threading_helper.join_thread(self._thread)
Vinay Sajip7367d082011-05-02 13:17:27 +0100922 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200923 self.server_close()
924 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100925
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200926class TestHTTPServer(ControlMixin, HTTPServer):
927 """
928 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100929
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200930 :param addr: A tuple with the IP address and port to listen on.
931 :param handler: A handler callable which will be called with a
932 single parameter - the request - in order to
933 process the request.
934 :param poll_interval: The polling interval in seconds.
935 :param log: Pass ``True`` to enable log messages.
936 """
937 def __init__(self, addr, handler, poll_interval=0.5,
938 log=False, sslctx=None):
939 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
940 def __getattr__(self, name, default=None):
941 if name.startswith('do_'):
942 return self.process_request
943 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100944
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200945 def process_request(self):
946 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100947
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200948 def log_message(self, format, *args):
949 if log:
950 super(DelegatingHTTPRequestHandler,
951 self).log_message(format, *args)
952 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
953 ControlMixin.__init__(self, handler, poll_interval)
954 self.sslctx = sslctx
Vinay Sajip7367d082011-05-02 13:17:27 +0100955
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200956 def get_request(self):
957 try:
958 sock, addr = self.socket.accept()
959 if self.sslctx:
960 sock = self.sslctx.wrap_socket(sock, server_side=True)
961 except OSError as e:
962 # socket errors are silenced by the caller, print them here
963 sys.stderr.write("Got an error:\n%s\n" % e)
964 raise
965 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100966
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200967class TestTCPServer(ControlMixin, ThreadingTCPServer):
968 """
969 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100970
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200971 :param addr: A tuple with the IP address and port to listen on.
972 :param handler: A handler callable which will be called with a single
973 parameter - the request - in order to process the request.
974 :param poll_interval: The polling interval in seconds.
975 :bind_and_activate: If True (the default), binds the server and starts it
976 listening. If False, you need to call
977 :meth:`server_bind` and :meth:`server_activate` at
978 some later time before calling :meth:`start`, so that
979 the server will set up the socket and listen on it.
980 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100981
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200982 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100983
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200984 def __init__(self, addr, handler, poll_interval=0.5,
985 bind_and_activate=True):
986 class DelegatingTCPRequestHandler(StreamRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +0100987
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200988 def handle(self):
989 self.server._handler(self)
990 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
991 bind_and_activate)
992 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100993
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200994 def server_bind(self):
995 super(TestTCPServer, self).server_bind()
996 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100997
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200998class TestUDPServer(ControlMixin, ThreadingUDPServer):
999 """
1000 A UDP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +01001001
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001002 :param addr: A tuple with the IP address and port to listen on.
1003 :param handler: A handler callable which will be called with a
1004 single parameter - the request - in order to
1005 process the request.
1006 :param poll_interval: The polling interval for shutdown requests,
1007 in seconds.
1008 :bind_and_activate: If True (the default), binds the server and
1009 starts it listening. If False, you need to
1010 call :meth:`server_bind` and
1011 :meth:`server_activate` at some later time
1012 before calling :meth:`start`, so that the server will
1013 set up the socket and listen on it.
1014 """
1015 def __init__(self, addr, handler, poll_interval=0.5,
1016 bind_and_activate=True):
1017 class DelegatingUDPRequestHandler(DatagramRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +01001018
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001019 def handle(self):
1020 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001021
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001022 def finish(self):
1023 data = self.wfile.getvalue()
1024 if data:
1025 try:
1026 super(DelegatingUDPRequestHandler, self).finish()
1027 except OSError:
1028 if not self.server._closed:
1029 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +01001030
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001031 ThreadingUDPServer.__init__(self, addr,
1032 DelegatingUDPRequestHandler,
1033 bind_and_activate)
1034 ControlMixin.__init__(self, handler, poll_interval)
1035 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +01001036
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001037 def server_bind(self):
1038 super(TestUDPServer, self).server_bind()
1039 self.port = self.socket.getsockname()[1]
Vinay Sajipce7c9782011-05-17 07:15:53 +01001040
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001041 def server_close(self):
1042 super(TestUDPServer, self).server_close()
1043 self._closed = True
Vinay Sajipce7c9782011-05-17 07:15:53 +01001044
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001045if hasattr(socket, "AF_UNIX"):
1046 class TestUnixStreamServer(TestTCPServer):
1047 address_family = socket.AF_UNIX
Vinay Sajipce7c9782011-05-17 07:15:53 +01001048
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001049 class TestUnixDatagramServer(TestUDPServer):
1050 address_family = socket.AF_UNIX
Vinay Sajip5421f352013-09-27 18:18:28 +01001051
Vinay Sajip7367d082011-05-02 13:17:27 +01001052# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +01001053
1054class SMTPHandlerTest(BaseTest):
Victor Stinnerc98b0192019-12-10 21:12:26 +01001055 # bpo-14314, bpo-19665, bpo-34092: don't wait forever
1056 TIMEOUT = support.LONG_TIMEOUT
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02001057
Vinay Sajipa463d252011-04-30 21:52:48 +01001058 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +01001059 sockmap = {}
Serhiy Storchaka16994912020-04-25 10:06:29 +03001060 server = TestSMTPServer((socket_helper.HOST, 0), self.process_message, 0.001,
Vinay Sajip7367d082011-05-02 13:17:27 +01001061 sockmap)
1062 server.start()
Serhiy Storchaka16994912020-04-25 10:06:29 +03001063 addr = (socket_helper.HOST, server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +00001064 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
1065 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001066 self.assertEqual(h.toaddrs, ['you'])
1067 self.messages = []
Vinay Sajip277640a2015-10-17 16:13:10 +01001068 r = logging.makeLogRecord({'msg': 'Hello \u2713'})
Vinay Sajip7367d082011-05-02 13:17:27 +01001069 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +01001070 h.handle(r)
Victor Stinner31b50b82018-07-11 12:35:38 +02001071 self.handled.wait(self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001072 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +00001073 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +01001074 self.assertEqual(len(self.messages), 1)
1075 peer, mailfrom, rcpttos, data = self.messages[0]
1076 self.assertEqual(mailfrom, 'me')
1077 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001078 self.assertIn('\nSubject: Log\n', data)
Vinay Sajip277640a2015-10-17 16:13:10 +01001079 self.assertTrue(data.endswith('\n\nHello \u2713'))
Vinay Sajipa463d252011-04-30 21:52:48 +01001080 h.close()
1081
1082 def process_message(self, *args):
1083 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +01001084 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +01001085
Christian Heimes180510d2008-03-03 19:15:45 +00001086class MemoryHandlerTest(BaseTest):
1087
1088 """Tests for the MemoryHandler."""
1089
1090 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001091 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001092
1093 def setUp(self):
1094 BaseTest.setUp(self)
1095 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001096 self.root_hdlr)
Christian Heimes180510d2008-03-03 19:15:45 +00001097 self.mem_logger = logging.getLogger('mem')
1098 self.mem_logger.propagate = 0
1099 self.mem_logger.addHandler(self.mem_hdlr)
1100
1101 def tearDown(self):
1102 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001103 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001104
1105 def test_flush(self):
1106 # The memory handler flushes to its target handler based on specific
1107 # criteria (message count and message level).
1108 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001109 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001110 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001111 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001112 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001113 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001114 lines = [
1115 ('DEBUG', '1'),
1116 ('INFO', '2'),
1117 ('WARNING', '3'),
1118 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001119 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001120 for n in (4, 14):
1121 for i in range(9):
1122 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001123 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001124 # This will flush because it's the 10th message since the last
1125 # flush.
1126 self.mem_logger.debug(self.next_message())
1127 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001128 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001129
1130 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001131 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001132
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001133 def test_flush_on_close(self):
1134 """
1135 Test that the flush-on-close configuration works as expected.
1136 """
1137 self.mem_logger.debug(self.next_message())
1138 self.assert_log_lines([])
1139 self.mem_logger.info(self.next_message())
1140 self.assert_log_lines([])
1141 self.mem_logger.removeHandler(self.mem_hdlr)
1142 # Default behaviour is to flush on close. Check that it happens.
1143 self.mem_hdlr.close()
1144 lines = [
1145 ('DEBUG', '1'),
1146 ('INFO', '2'),
1147 ]
1148 self.assert_log_lines(lines)
1149 # Now configure for flushing not to be done on close.
1150 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1151 self.root_hdlr,
1152 False)
1153 self.mem_logger.addHandler(self.mem_hdlr)
1154 self.mem_logger.debug(self.next_message())
1155 self.assert_log_lines(lines) # no change
1156 self.mem_logger.info(self.next_message())
1157 self.assert_log_lines(lines) # no change
1158 self.mem_logger.removeHandler(self.mem_hdlr)
1159 self.mem_hdlr.close()
1160 # assert that no new lines have been added
1161 self.assert_log_lines(lines) # no change
1162
Christian Heimes180510d2008-03-03 19:15:45 +00001163
1164class ExceptionFormatter(logging.Formatter):
1165 """A special exception formatter."""
1166 def formatException(self, ei):
1167 return "Got a [%s]" % ei[0].__name__
1168
1169
1170class ConfigFileTest(BaseTest):
1171
1172 """Reading logging config from a .ini-style config file."""
1173
Hai Shi3ddc6342020-06-30 21:46:06 +08001174 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001175 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001176
1177 # config0 is a standard configuration.
1178 config0 = """
1179 [loggers]
1180 keys=root
1181
1182 [handlers]
1183 keys=hand1
1184
1185 [formatters]
1186 keys=form1
1187
1188 [logger_root]
1189 level=WARNING
1190 handlers=hand1
1191
1192 [handler_hand1]
1193 class=StreamHandler
1194 level=NOTSET
1195 formatter=form1
1196 args=(sys.stdout,)
1197
1198 [formatter_form1]
1199 format=%(levelname)s ++ %(message)s
1200 datefmt=
1201 """
1202
1203 # config1 adds a little to the standard configuration.
1204 config1 = """
1205 [loggers]
1206 keys=root,parser
1207
1208 [handlers]
1209 keys=hand1
1210
1211 [formatters]
1212 keys=form1
1213
1214 [logger_root]
1215 level=WARNING
1216 handlers=
1217
1218 [logger_parser]
1219 level=DEBUG
1220 handlers=hand1
1221 propagate=1
1222 qualname=compiler.parser
1223
1224 [handler_hand1]
1225 class=StreamHandler
1226 level=NOTSET
1227 formatter=form1
1228 args=(sys.stdout,)
1229
1230 [formatter_form1]
1231 format=%(levelname)s ++ %(message)s
1232 datefmt=
1233 """
1234
Vinay Sajip3f84b072011-03-07 17:49:33 +00001235 # config1a moves the handler to the root.
1236 config1a = """
1237 [loggers]
1238 keys=root,parser
1239
1240 [handlers]
1241 keys=hand1
1242
1243 [formatters]
1244 keys=form1
1245
1246 [logger_root]
1247 level=WARNING
1248 handlers=hand1
1249
1250 [logger_parser]
1251 level=DEBUG
1252 handlers=
1253 propagate=1
1254 qualname=compiler.parser
1255
1256 [handler_hand1]
1257 class=StreamHandler
1258 level=NOTSET
1259 formatter=form1
1260 args=(sys.stdout,)
1261
1262 [formatter_form1]
1263 format=%(levelname)s ++ %(message)s
1264 datefmt=
1265 """
1266
Christian Heimes180510d2008-03-03 19:15:45 +00001267 # config2 has a subtle configuration error that should be reported
1268 config2 = config1.replace("sys.stdout", "sys.stbout")
1269
1270 # config3 has a less subtle configuration error
1271 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1272
1273 # config4 specifies a custom formatter class to be loaded
1274 config4 = """
1275 [loggers]
1276 keys=root
1277
1278 [handlers]
1279 keys=hand1
1280
1281 [formatters]
1282 keys=form1
1283
1284 [logger_root]
1285 level=NOTSET
1286 handlers=hand1
1287
1288 [handler_hand1]
1289 class=StreamHandler
1290 level=NOTSET
1291 formatter=form1
1292 args=(sys.stdout,)
1293
1294 [formatter_form1]
1295 class=""" + __name__ + """.ExceptionFormatter
1296 format=%(levelname)s:%(name)s:%(message)s
1297 datefmt=
1298 """
1299
Georg Brandl3dbca812008-07-23 16:10:53 +00001300 # config5 specifies a custom handler class to be loaded
1301 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1302
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001303 # config6 uses ', ' delimiters in the handlers and formatters sections
1304 config6 = """
1305 [loggers]
1306 keys=root,parser
1307
1308 [handlers]
1309 keys=hand1, hand2
1310
1311 [formatters]
1312 keys=form1, form2
1313
1314 [logger_root]
1315 level=WARNING
1316 handlers=
1317
1318 [logger_parser]
1319 level=DEBUG
1320 handlers=hand1
1321 propagate=1
1322 qualname=compiler.parser
1323
1324 [handler_hand1]
1325 class=StreamHandler
1326 level=NOTSET
1327 formatter=form1
1328 args=(sys.stdout,)
1329
1330 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001331 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001332 level=NOTSET
1333 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001334 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001335
1336 [formatter_form1]
1337 format=%(levelname)s ++ %(message)s
1338 datefmt=
1339
1340 [formatter_form2]
1341 format=%(message)s
1342 datefmt=
1343 """
1344
Preston Landers6ea56d22017-08-02 15:44:28 -05001345 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001346 config7 = """
1347 [loggers]
1348 keys=root,parser,compiler
1349
1350 [handlers]
1351 keys=hand1
1352
1353 [formatters]
1354 keys=form1
1355
1356 [logger_root]
1357 level=WARNING
1358 handlers=hand1
1359
1360 [logger_compiler]
1361 level=DEBUG
1362 handlers=
1363 propagate=1
1364 qualname=compiler
1365
1366 [logger_parser]
1367 level=DEBUG
1368 handlers=
1369 propagate=1
1370 qualname=compiler.parser
1371
1372 [handler_hand1]
1373 class=StreamHandler
1374 level=NOTSET
1375 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001376 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001377
1378 [formatter_form1]
1379 format=%(levelname)s ++ %(message)s
1380 datefmt=
1381 """
1382
Xtreak087570a2018-07-02 14:27:46 +05301383 # config 8, check for resource warning
1384 config8 = r"""
1385 [loggers]
1386 keys=root
1387
1388 [handlers]
1389 keys=file
1390
1391 [formatters]
1392 keys=
1393
1394 [logger_root]
1395 level=DEBUG
1396 handlers=file
1397
1398 [handler_file]
1399 class=FileHandler
1400 level=DEBUG
1401 args=("{tempfile}",)
1402 """
1403
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001404 disable_test = """
1405 [loggers]
1406 keys=root
1407
1408 [handlers]
1409 keys=screen
1410
1411 [formatters]
1412 keys=
1413
1414 [logger_root]
1415 level=DEBUG
1416 handlers=screen
1417
1418 [handler_screen]
1419 level=DEBUG
1420 class=StreamHandler
1421 args=(sys.stdout,)
1422 formatter=
1423 """
1424
1425 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001426 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001427 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001428
1429 def test_config0_ok(self):
1430 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001431 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001432 self.apply_config(self.config0)
1433 logger = logging.getLogger()
1434 # Won't output anything
1435 logger.info(self.next_message())
1436 # Outputs a message
1437 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001438 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001439 ('ERROR', '2'),
1440 ], stream=output)
1441 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001442 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001443
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001444 def test_config0_using_cp_ok(self):
1445 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001446 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001447 file = io.StringIO(textwrap.dedent(self.config0))
1448 cp = configparser.ConfigParser()
1449 cp.read_file(file)
1450 logging.config.fileConfig(cp)
1451 logger = logging.getLogger()
1452 # Won't output anything
1453 logger.info(self.next_message())
1454 # Outputs a message
1455 logger.error(self.next_message())
1456 self.assert_log_lines([
1457 ('ERROR', '2'),
1458 ], stream=output)
1459 # Original logger output is empty.
1460 self.assert_log_lines([])
1461
Georg Brandl3dbca812008-07-23 16:10:53 +00001462 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001463 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001464 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001465 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001466 logger = logging.getLogger("compiler.parser")
1467 # Both will output a message
1468 logger.info(self.next_message())
1469 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001470 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001471 ('INFO', '1'),
1472 ('ERROR', '2'),
1473 ], stream=output)
1474 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001475 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001476
1477 def test_config2_failure(self):
1478 # A simple config file which overrides the default settings.
1479 self.assertRaises(Exception, self.apply_config, self.config2)
1480
1481 def test_config3_failure(self):
1482 # A simple config file which overrides the default settings.
1483 self.assertRaises(Exception, self.apply_config, self.config3)
1484
1485 def test_config4_ok(self):
1486 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001487 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001488 self.apply_config(self.config4)
1489 logger = logging.getLogger()
1490 try:
1491 raise RuntimeError()
1492 except RuntimeError:
1493 logging.exception("just testing")
1494 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001495 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001496 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1497 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001498 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001499
Georg Brandl3dbca812008-07-23 16:10:53 +00001500 def test_config5_ok(self):
1501 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001502
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001503 def test_config6_ok(self):
1504 self.test_config1_ok(config=self.config6)
1505
Vinay Sajip3f84b072011-03-07 17:49:33 +00001506 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001507 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001508 self.apply_config(self.config1a)
1509 logger = logging.getLogger("compiler.parser")
1510 # See issue #11424. compiler-hyphenated sorts
1511 # between compiler and compiler.xyz and this
1512 # was preventing compiler.xyz from being included
1513 # in the child loggers of compiler because of an
1514 # overzealous loop termination condition.
1515 hyphenated = logging.getLogger('compiler-hyphenated')
1516 # All will output a message
1517 logger.info(self.next_message())
1518 logger.error(self.next_message())
1519 hyphenated.critical(self.next_message())
1520 self.assert_log_lines([
1521 ('INFO', '1'),
1522 ('ERROR', '2'),
1523 ('CRITICAL', '3'),
1524 ], stream=output)
1525 # Original logger output is empty.
1526 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001527 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001528 self.apply_config(self.config7)
1529 logger = logging.getLogger("compiler.parser")
1530 self.assertFalse(logger.disabled)
1531 # Both will output a message
1532 logger.info(self.next_message())
1533 logger.error(self.next_message())
1534 logger = logging.getLogger("compiler.lexer")
1535 # Both will output a message
1536 logger.info(self.next_message())
1537 logger.error(self.next_message())
1538 # Will not appear
1539 hyphenated.critical(self.next_message())
1540 self.assert_log_lines([
1541 ('INFO', '4'),
1542 ('ERROR', '5'),
1543 ('INFO', '6'),
1544 ('ERROR', '7'),
1545 ], stream=output)
1546 # Original logger output is empty.
1547 self.assert_log_lines([])
1548
Xtreak087570a2018-07-02 14:27:46 +05301549 def test_config8_ok(self):
1550
1551 def cleanup(h1, fn):
1552 h1.close()
1553 os.remove(fn)
1554
1555 with self.check_no_resource_warning():
1556 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1557 os.close(fd)
1558
1559 # Replace single backslash with double backslash in windows
1560 # to avoid unicode error during string formatting
1561 if os.name == "nt":
1562 fn = fn.replace("\\", "\\\\")
1563
1564 config8 = self.config8.format(tempfile=fn)
1565
1566 self.apply_config(config8)
1567 self.apply_config(config8)
1568
1569 handler = logging.root.handlers[0]
1570 self.addCleanup(cleanup, handler, fn)
1571
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001572 def test_logger_disabling(self):
1573 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001574 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001575 self.assertFalse(logger.disabled)
1576 self.apply_config(self.disable_test)
1577 self.assertTrue(logger.disabled)
1578 self.apply_config(self.disable_test, disable_existing_loggers=False)
1579 self.assertFalse(logger.disabled)
1580
Lucas Cimonb15100f2019-10-31 09:06:25 +01001581 def test_config_set_handler_names(self):
1582 test_config = """
1583 [loggers]
1584 keys=root
1585
1586 [handlers]
1587 keys=hand1
1588
1589 [formatters]
1590 keys=form1
1591
1592 [logger_root]
1593 handlers=hand1
1594
1595 [handler_hand1]
1596 class=StreamHandler
1597 formatter=form1
1598
1599 [formatter_form1]
1600 format=%(levelname)s ++ %(message)s
1601 """
1602 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001603 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001604
Łukasz Langa214f18e2018-06-08 04:02:48 -07001605 def test_defaults_do_no_interpolation(self):
1606 """bpo-33802 defaults should not get interpolated"""
1607 ini = textwrap.dedent("""
1608 [formatters]
1609 keys=default
1610
1611 [formatter_default]
1612
1613 [handlers]
1614 keys=console
1615
1616 [handler_console]
1617 class=logging.StreamHandler
1618 args=tuple()
1619
1620 [loggers]
1621 keys=root
1622
1623 [logger_root]
1624 formatter=default
1625 handlers=console
1626 """).strip()
1627 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1628 try:
1629 os.write(fd, ini.encode('ascii'))
1630 os.close(fd)
1631 logging.config.fileConfig(
1632 fn,
1633 defaults=dict(
1634 version=1,
1635 disable_existing_loggers=False,
1636 formatters={
1637 "generic": {
1638 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1639 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1640 "class": "logging.Formatter"
1641 },
1642 },
1643 )
1644 )
1645 finally:
1646 os.unlink(fn)
1647
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001648
Christian Heimes180510d2008-03-03 19:15:45 +00001649class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001650
Christian Heimes180510d2008-03-03 19:15:45 +00001651 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001652
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001653 server_class = TestTCPServer
1654 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001655
Christian Heimes180510d2008-03-03 19:15:45 +00001656 def setUp(self):
1657 """Set up a TCP server to receive log messages, and a SocketHandler
1658 pointing to that server's address and port."""
1659 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001660 # Issue #29177: deal with errors that happen during setup
1661 self.server = self.sock_hdlr = self.server_exception = None
1662 try:
1663 self.server = server = self.server_class(self.address,
1664 self.handle_socket, 0.01)
1665 server.start()
1666 # Uncomment next line to test error recovery in setUp()
1667 # raise OSError('dummy error raised')
1668 except OSError as e:
1669 self.server_exception = e
1670 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001671 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001672 hcls = logging.handlers.SocketHandler
1673 if isinstance(server.server_address, tuple):
1674 self.sock_hdlr = hcls('localhost', server.port)
1675 else:
1676 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001677 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001678 self.root_logger.removeHandler(self.root_logger.handlers[0])
1679 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001680 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001681
Christian Heimes180510d2008-03-03 19:15:45 +00001682 def tearDown(self):
1683 """Shutdown the TCP server."""
1684 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001685 if self.sock_hdlr:
1686 self.root_logger.removeHandler(self.sock_hdlr)
1687 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001688 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001689 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001690 finally:
1691 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001692
Vinay Sajip7367d082011-05-02 13:17:27 +01001693 def handle_socket(self, request):
1694 conn = request.connection
1695 while True:
1696 chunk = conn.recv(4)
1697 if len(chunk) < 4:
1698 break
1699 slen = struct.unpack(">L", chunk)[0]
1700 chunk = conn.recv(slen)
1701 while len(chunk) < slen:
1702 chunk = chunk + conn.recv(slen - len(chunk))
1703 obj = pickle.loads(chunk)
1704 record = logging.makeLogRecord(obj)
1705 self.log_output += record.msg + '\n'
1706 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001707
Christian Heimes180510d2008-03-03 19:15:45 +00001708 def test_output(self):
1709 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001710 if self.server_exception:
1711 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001712 logger = logging.getLogger("tcp")
1713 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001714 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001715 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001716 self.handled.acquire()
1717 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001718
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001719 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001720 if self.server_exception:
1721 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001722 # Avoid timing-related failures due to SocketHandler's own hard-wired
1723 # one-second timeout on socket.create_connection() (issue #16264).
1724 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001725 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001726 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001727 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001728 try:
1729 raise RuntimeError('Deliberate mistake')
1730 except RuntimeError:
1731 self.root_logger.exception('Never sent')
1732 self.root_logger.error('Never sent, either')
1733 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001734 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001735 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1736 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001737
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001738def _get_temp_domain_socket():
1739 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1740 os.close(fd)
1741 # just need a name - file can't be present, or we'll get an
1742 # 'address already in use' error.
1743 os.remove(fn)
1744 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001745
Victor Stinnerec5a8602014-06-02 14:41:51 +02001746@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001747class UnixSocketHandlerTest(SocketHandlerTest):
1748
1749 """Test for SocketHandler with unix sockets."""
1750
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001751 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001752 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001753
1754 def setUp(self):
1755 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001756 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001757 SocketHandlerTest.setUp(self)
1758
1759 def tearDown(self):
1760 SocketHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001761 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001762
Vinay Sajip7367d082011-05-02 13:17:27 +01001763class DatagramHandlerTest(BaseTest):
1764
1765 """Test for DatagramHandler."""
1766
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001767 server_class = TestUDPServer
1768 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001769
Vinay Sajip7367d082011-05-02 13:17:27 +01001770 def setUp(self):
1771 """Set up a UDP server to receive log messages, and a DatagramHandler
1772 pointing to that server's address and port."""
1773 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001774 # Issue #29177: deal with errors that happen during setup
1775 self.server = self.sock_hdlr = self.server_exception = None
1776 try:
1777 self.server = server = self.server_class(self.address,
1778 self.handle_datagram, 0.01)
1779 server.start()
1780 # Uncomment next line to test error recovery in setUp()
1781 # raise OSError('dummy error raised')
1782 except OSError as e:
1783 self.server_exception = e
1784 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001785 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001786 hcls = logging.handlers.DatagramHandler
1787 if isinstance(server.server_address, tuple):
1788 self.sock_hdlr = hcls('localhost', server.port)
1789 else:
1790 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001791 self.log_output = ''
1792 self.root_logger.removeHandler(self.root_logger.handlers[0])
1793 self.root_logger.addHandler(self.sock_hdlr)
1794 self.handled = threading.Event()
1795
1796 def tearDown(self):
1797 """Shutdown the UDP server."""
1798 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001799 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001800 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001801 if self.sock_hdlr:
1802 self.root_logger.removeHandler(self.sock_hdlr)
1803 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001804 finally:
1805 BaseTest.tearDown(self)
1806
1807 def handle_datagram(self, request):
1808 slen = struct.pack('>L', 0) # length of prefix
1809 packet = request.packet[len(slen):]
1810 obj = pickle.loads(packet)
1811 record = logging.makeLogRecord(obj)
1812 self.log_output += record.msg + '\n'
1813 self.handled.set()
1814
1815 def test_output(self):
1816 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001817 if self.server_exception:
1818 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001819 logger = logging.getLogger("udp")
1820 logger.error("spam")
1821 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001822 self.handled.clear()
1823 logger.error("eggs")
1824 self.handled.wait()
1825 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001826
Victor Stinnerec5a8602014-06-02 14:41:51 +02001827@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001828class UnixDatagramHandlerTest(DatagramHandlerTest):
1829
1830 """Test for DatagramHandler using Unix sockets."""
1831
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001832 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001833 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001834
1835 def setUp(self):
1836 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001837 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001838 DatagramHandlerTest.setUp(self)
1839
1840 def tearDown(self):
1841 DatagramHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001842 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001843
Vinay Sajip7367d082011-05-02 13:17:27 +01001844class SysLogHandlerTest(BaseTest):
1845
1846 """Test for SysLogHandler using UDP."""
1847
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001848 server_class = TestUDPServer
1849 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001850
Vinay Sajip7367d082011-05-02 13:17:27 +01001851 def setUp(self):
1852 """Set up a UDP server to receive log messages, and a SysLogHandler
1853 pointing to that server's address and port."""
1854 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001855 # Issue #29177: deal with errors that happen during setup
1856 self.server = self.sl_hdlr = self.server_exception = None
1857 try:
1858 self.server = server = self.server_class(self.address,
1859 self.handle_datagram, 0.01)
1860 server.start()
1861 # Uncomment next line to test error recovery in setUp()
1862 # raise OSError('dummy error raised')
1863 except OSError as e:
1864 self.server_exception = e
1865 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001866 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001867 hcls = logging.handlers.SysLogHandler
1868 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001869 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001870 else:
1871 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001872 self.log_output = ''
1873 self.root_logger.removeHandler(self.root_logger.handlers[0])
1874 self.root_logger.addHandler(self.sl_hdlr)
1875 self.handled = threading.Event()
1876
1877 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001878 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001879 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001880 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001881 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001882 if self.sl_hdlr:
1883 self.root_logger.removeHandler(self.sl_hdlr)
1884 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001885 finally:
1886 BaseTest.tearDown(self)
1887
1888 def handle_datagram(self, request):
1889 self.log_output = request.packet
1890 self.handled.set()
1891
1892 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001893 if self.server_exception:
1894 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001895 # The log message sent to the SysLogHandler is properly received.
1896 logger = logging.getLogger("slh")
1897 logger.error("sp\xe4m")
1898 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001899 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001900 self.handled.clear()
1901 self.sl_hdlr.append_nul = False
1902 logger.error("sp\xe4m")
1903 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001904 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001905 self.handled.clear()
1906 self.sl_hdlr.ident = "h\xe4m-"
1907 logger.error("sp\xe4m")
1908 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001909 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001910
Victor Stinnerec5a8602014-06-02 14:41:51 +02001911@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001912class UnixSysLogHandlerTest(SysLogHandlerTest):
1913
1914 """Test for SysLogHandler with Unix sockets."""
1915
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001916 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001917 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001918
1919 def setUp(self):
1920 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001921 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001922 SysLogHandlerTest.setUp(self)
1923
1924 def tearDown(self):
1925 SysLogHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001926 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001927
Serhiy Storchaka16994912020-04-25 10:06:29 +03001928@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001929 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001930class IPv6SysLogHandlerTest(SysLogHandlerTest):
1931
1932 """Test for SysLogHandler with IPv6 host."""
1933
1934 server_class = TestUDPServer
1935 address = ('::1', 0)
1936
1937 def setUp(self):
1938 self.server_class.address_family = socket.AF_INET6
1939 super(IPv6SysLogHandlerTest, self).setUp()
1940
1941 def tearDown(self):
1942 self.server_class.address_family = socket.AF_INET
1943 super(IPv6SysLogHandlerTest, self).tearDown()
1944
Vinay Sajip7367d082011-05-02 13:17:27 +01001945class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001946 """Test for HTTPHandler."""
1947
1948 def setUp(self):
1949 """Set up an HTTP server to receive log messages, and a HTTPHandler
1950 pointing to that server's address and port."""
1951 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001952 self.handled = threading.Event()
1953
Vinay Sajip7367d082011-05-02 13:17:27 +01001954 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001955 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001956 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001957 if self.command == 'POST':
1958 try:
1959 rlen = int(request.headers['Content-Length'])
1960 self.post_data = request.rfile.read(rlen)
1961 except:
1962 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001963 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001964 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001965 self.handled.set()
1966
1967 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001968 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001969 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001970 root_logger = self.root_logger
1971 root_logger.removeHandler(self.root_logger.handlers[0])
1972 for secure in (False, True):
1973 addr = ('localhost', 0)
1974 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001975 try:
1976 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01001977 except ImportError:
1978 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001979 else:
1980 here = os.path.dirname(__file__)
1981 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02001982 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001983 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06001984
1985 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01001986 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001987 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06001988 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01001989 self.server = server = TestHTTPServer(addr, self.handle_request,
1990 0.01, sslctx=sslctx)
1991 server.start()
1992 server.ready.wait()
1993 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001994 secure_client = secure and sslctx
1995 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06001996 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01001997 context=context,
1998 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01001999 self.log_data = None
2000 root_logger.addHandler(self.h_hdlr)
2001
2002 for method in ('GET', 'POST'):
2003 self.h_hdlr.method = method
2004 self.handled.clear()
2005 msg = "sp\xe4m"
2006 logger.error(msg)
2007 self.handled.wait()
2008 self.assertEqual(self.log_data.path, '/frob')
2009 self.assertEqual(self.command, method)
2010 if method == 'GET':
2011 d = parse_qs(self.log_data.query)
2012 else:
2013 d = parse_qs(self.post_data.decode('utf-8'))
2014 self.assertEqual(d['name'], ['http'])
2015 self.assertEqual(d['funcName'], ['test_output'])
2016 self.assertEqual(d['msg'], [msg])
2017
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002018 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002019 self.root_logger.removeHandler(self.h_hdlr)
2020 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002021
Christian Heimes180510d2008-03-03 19:15:45 +00002022class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002023
Christian Heimes180510d2008-03-03 19:15:45 +00002024 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002025
Christian Heimes180510d2008-03-03 19:15:45 +00002026 def setUp(self):
2027 """Create a dict to remember potentially destroyed objects."""
2028 BaseTest.setUp(self)
2029 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002030
Christian Heimes180510d2008-03-03 19:15:45 +00002031 def _watch_for_survival(self, *args):
2032 """Watch the given objects for survival, by creating weakrefs to
2033 them."""
2034 for obj in args:
2035 key = id(obj), repr(obj)
2036 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002037
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002038 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002039 """Assert that all objects watched for survival have survived."""
2040 # Trigger cycle breaking.
2041 gc.collect()
2042 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002043 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002044 if ref() is None:
2045 dead.append(repr_)
2046 if dead:
2047 self.fail("%d objects should have survived "
2048 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002049
Christian Heimes180510d2008-03-03 19:15:45 +00002050 def test_persistent_loggers(self):
2051 # Logger objects are persistent and retain their configuration, even
2052 # if visible references are destroyed.
2053 self.root_logger.setLevel(logging.INFO)
2054 foo = logging.getLogger("foo")
2055 self._watch_for_survival(foo)
2056 foo.setLevel(logging.DEBUG)
2057 self.root_logger.debug(self.next_message())
2058 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002059 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002060 ('foo', 'DEBUG', '2'),
2061 ])
2062 del foo
2063 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002064 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002065 # foo has retained its settings.
2066 bar = logging.getLogger("foo")
2067 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002068 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002069 ('foo', 'DEBUG', '2'),
2070 ('foo', 'DEBUG', '3'),
2071 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002072
Benjamin Petersonf91df042009-02-13 02:50:59 +00002073
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002074class EncodingTest(BaseTest):
2075 def test_encoding_plain_file(self):
2076 # In Python 2.x, a plain file object is treated as having no encoding.
2077 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002078 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2079 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002080 # the non-ascii data we write to the log.
2081 data = "foo\x80"
2082 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002083 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002084 log.addHandler(handler)
2085 try:
2086 # write non-ascii data to the log.
2087 log.warning(data)
2088 finally:
2089 log.removeHandler(handler)
2090 handler.close()
2091 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002092 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002093 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002094 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002095 finally:
2096 f.close()
2097 finally:
2098 if os.path.isfile(fn):
2099 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002100
Benjamin Petersonf91df042009-02-13 02:50:59 +00002101 def test_encoding_cyrillic_unicode(self):
2102 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002103 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002104 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002105 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002106 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002107 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002108 stream = io.BytesIO()
2109 writer = writer_class(stream, 'strict')
2110 handler = logging.StreamHandler(writer)
2111 log.addHandler(handler)
2112 try:
2113 log.warning(message)
2114 finally:
2115 log.removeHandler(handler)
2116 handler.close()
2117 # check we wrote exactly those bytes, ignoring trailing \n etc
2118 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002119 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002120 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2121
2122
Georg Brandlf9734072008-12-07 15:30:06 +00002123class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002124
Georg Brandlf9734072008-12-07 15:30:06 +00002125 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002126 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002127 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002128 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002129 warnings.filterwarnings("always", category=UserWarning)
2130 stream = io.StringIO()
2131 h = logging.StreamHandler(stream)
2132 logger = logging.getLogger("py.warnings")
2133 logger.addHandler(h)
2134 warnings.warn("I'm warning you...")
2135 logger.removeHandler(h)
2136 s = stream.getvalue()
2137 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002138 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002139
Mike53f7a7c2017-12-14 14:04:53 +03002140 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002141 a_file = io.StringIO()
2142 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2143 a_file, "Dummy line")
2144 s = a_file.getvalue()
2145 a_file.close()
2146 self.assertEqual(s,
2147 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2148
2149 def test_warnings_no_handlers(self):
2150 with warnings.catch_warnings():
2151 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002152 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002153
2154 # confirm our assumption: no loggers are set
2155 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002156 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002157
2158 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002159 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002160 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002161
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002162
2163def formatFunc(format, datefmt=None):
2164 return logging.Formatter(format, datefmt)
2165
BNMetrics18fb1fb2018-10-15 19:41:36 +01002166class myCustomFormatter:
2167 def __init__(self, fmt, datefmt=None):
2168 pass
2169
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002170def handlerFunc():
2171 return logging.StreamHandler()
2172
2173class CustomHandler(logging.StreamHandler):
2174 pass
2175
2176class ConfigDictTest(BaseTest):
2177
2178 """Reading logging config from a dictionary."""
2179
Hai Shi3ddc6342020-06-30 21:46:06 +08002180 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002181 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002182
2183 # config0 is a standard configuration.
2184 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002185 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002186 'formatters': {
2187 'form1' : {
2188 'format' : '%(levelname)s ++ %(message)s',
2189 },
2190 },
2191 'handlers' : {
2192 'hand1' : {
2193 'class' : 'logging.StreamHandler',
2194 'formatter' : 'form1',
2195 'level' : 'NOTSET',
2196 'stream' : 'ext://sys.stdout',
2197 },
2198 },
2199 'root' : {
2200 'level' : 'WARNING',
2201 'handlers' : ['hand1'],
2202 },
2203 }
2204
2205 # config1 adds a little to the standard configuration.
2206 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002207 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002208 'formatters': {
2209 'form1' : {
2210 'format' : '%(levelname)s ++ %(message)s',
2211 },
2212 },
2213 'handlers' : {
2214 'hand1' : {
2215 'class' : 'logging.StreamHandler',
2216 'formatter' : 'form1',
2217 'level' : 'NOTSET',
2218 'stream' : 'ext://sys.stdout',
2219 },
2220 },
2221 'loggers' : {
2222 'compiler.parser' : {
2223 'level' : 'DEBUG',
2224 'handlers' : ['hand1'],
2225 },
2226 },
2227 'root' : {
2228 'level' : 'WARNING',
2229 },
2230 }
2231
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002232 # config1a moves the handler to the root. Used with config8a
2233 config1a = {
2234 'version': 1,
2235 'formatters': {
2236 'form1' : {
2237 'format' : '%(levelname)s ++ %(message)s',
2238 },
2239 },
2240 'handlers' : {
2241 'hand1' : {
2242 'class' : 'logging.StreamHandler',
2243 'formatter' : 'form1',
2244 'level' : 'NOTSET',
2245 'stream' : 'ext://sys.stdout',
2246 },
2247 },
2248 'loggers' : {
2249 'compiler.parser' : {
2250 'level' : 'DEBUG',
2251 },
2252 },
2253 'root' : {
2254 'level' : 'WARNING',
2255 'handlers' : ['hand1'],
2256 },
2257 }
2258
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002259 # config2 has a subtle configuration error that should be reported
2260 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002261 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002262 'formatters': {
2263 'form1' : {
2264 'format' : '%(levelname)s ++ %(message)s',
2265 },
2266 },
2267 'handlers' : {
2268 'hand1' : {
2269 'class' : 'logging.StreamHandler',
2270 'formatter' : 'form1',
2271 'level' : 'NOTSET',
2272 'stream' : 'ext://sys.stdbout',
2273 },
2274 },
2275 'loggers' : {
2276 'compiler.parser' : {
2277 'level' : 'DEBUG',
2278 'handlers' : ['hand1'],
2279 },
2280 },
2281 'root' : {
2282 'level' : 'WARNING',
2283 },
2284 }
2285
Mike53f7a7c2017-12-14 14:04:53 +03002286 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002287 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002288 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002289 'formatters': {
2290 'form1' : {
2291 'format' : '%(levelname)s ++ %(message)s',
2292 },
2293 },
2294 'handlers' : {
2295 'hand1' : {
2296 'class' : 'logging.StreamHandler',
2297 'formatter' : 'form1',
2298 'level' : 'NTOSET',
2299 'stream' : 'ext://sys.stdout',
2300 },
2301 },
2302 'loggers' : {
2303 'compiler.parser' : {
2304 'level' : 'DEBUG',
2305 'handlers' : ['hand1'],
2306 },
2307 },
2308 'root' : {
2309 'level' : 'WARNING',
2310 },
2311 }
2312
2313
Mike53f7a7c2017-12-14 14:04:53 +03002314 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002315 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002316 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002317 'formatters': {
2318 'form1' : {
2319 'format' : '%(levelname)s ++ %(message)s',
2320 },
2321 },
2322 'handlers' : {
2323 'hand1' : {
2324 'class' : 'logging.StreamHandler',
2325 'formatter' : 'form1',
2326 'level' : 'NOTSET',
2327 'stream' : 'ext://sys.stdout',
2328 },
2329 },
2330 'loggers' : {
2331 'compiler.parser' : {
2332 'level' : 'DEBUG',
2333 'handlers' : ['hand1'],
2334 },
2335 },
2336 'root' : {
2337 'level' : 'WRANING',
2338 },
2339 }
2340
2341 # config3 has a less subtle configuration error
2342 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002343 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002344 'formatters': {
2345 'form1' : {
2346 'format' : '%(levelname)s ++ %(message)s',
2347 },
2348 },
2349 'handlers' : {
2350 'hand1' : {
2351 'class' : 'logging.StreamHandler',
2352 'formatter' : 'misspelled_name',
2353 'level' : 'NOTSET',
2354 'stream' : 'ext://sys.stdout',
2355 },
2356 },
2357 'loggers' : {
2358 'compiler.parser' : {
2359 'level' : 'DEBUG',
2360 'handlers' : ['hand1'],
2361 },
2362 },
2363 'root' : {
2364 'level' : 'WARNING',
2365 },
2366 }
2367
2368 # config4 specifies a custom formatter class to be loaded
2369 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002370 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002371 'formatters': {
2372 'form1' : {
2373 '()' : __name__ + '.ExceptionFormatter',
2374 'format' : '%(levelname)s:%(name)s:%(message)s',
2375 },
2376 },
2377 'handlers' : {
2378 'hand1' : {
2379 'class' : 'logging.StreamHandler',
2380 'formatter' : 'form1',
2381 'level' : 'NOTSET',
2382 'stream' : 'ext://sys.stdout',
2383 },
2384 },
2385 'root' : {
2386 'level' : 'NOTSET',
2387 'handlers' : ['hand1'],
2388 },
2389 }
2390
2391 # As config4 but using an actual callable rather than a string
2392 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002393 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002394 'formatters': {
2395 'form1' : {
2396 '()' : ExceptionFormatter,
2397 'format' : '%(levelname)s:%(name)s:%(message)s',
2398 },
2399 'form2' : {
2400 '()' : __name__ + '.formatFunc',
2401 'format' : '%(levelname)s:%(name)s:%(message)s',
2402 },
2403 'form3' : {
2404 '()' : formatFunc,
2405 'format' : '%(levelname)s:%(name)s:%(message)s',
2406 },
2407 },
2408 'handlers' : {
2409 'hand1' : {
2410 'class' : 'logging.StreamHandler',
2411 'formatter' : 'form1',
2412 'level' : 'NOTSET',
2413 'stream' : 'ext://sys.stdout',
2414 },
2415 'hand2' : {
2416 '()' : handlerFunc,
2417 },
2418 },
2419 'root' : {
2420 'level' : 'NOTSET',
2421 'handlers' : ['hand1'],
2422 },
2423 }
2424
2425 # config5 specifies a custom handler class to be loaded
2426 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002427 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002428 'formatters': {
2429 'form1' : {
2430 'format' : '%(levelname)s ++ %(message)s',
2431 },
2432 },
2433 'handlers' : {
2434 'hand1' : {
2435 'class' : __name__ + '.CustomHandler',
2436 'formatter' : 'form1',
2437 'level' : 'NOTSET',
2438 'stream' : 'ext://sys.stdout',
2439 },
2440 },
2441 'loggers' : {
2442 'compiler.parser' : {
2443 'level' : 'DEBUG',
2444 'handlers' : ['hand1'],
2445 },
2446 },
2447 'root' : {
2448 'level' : 'WARNING',
2449 },
2450 }
2451
2452 # config6 specifies a custom handler class to be loaded
2453 # but has bad arguments
2454 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002455 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002456 'formatters': {
2457 'form1' : {
2458 'format' : '%(levelname)s ++ %(message)s',
2459 },
2460 },
2461 'handlers' : {
2462 'hand1' : {
2463 'class' : __name__ + '.CustomHandler',
2464 'formatter' : 'form1',
2465 'level' : 'NOTSET',
2466 'stream' : 'ext://sys.stdout',
2467 '9' : 'invalid parameter name',
2468 },
2469 },
2470 'loggers' : {
2471 'compiler.parser' : {
2472 'level' : 'DEBUG',
2473 'handlers' : ['hand1'],
2474 },
2475 },
2476 'root' : {
2477 'level' : 'WARNING',
2478 },
2479 }
2480
Mike53f7a7c2017-12-14 14:04:53 +03002481 # config 7 does not define compiler.parser but defines compiler.lexer
2482 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002483 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002484 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002485 'formatters': {
2486 'form1' : {
2487 'format' : '%(levelname)s ++ %(message)s',
2488 },
2489 },
2490 'handlers' : {
2491 'hand1' : {
2492 'class' : 'logging.StreamHandler',
2493 'formatter' : 'form1',
2494 'level' : 'NOTSET',
2495 'stream' : 'ext://sys.stdout',
2496 },
2497 },
2498 'loggers' : {
2499 'compiler.lexer' : {
2500 'level' : 'DEBUG',
2501 'handlers' : ['hand1'],
2502 },
2503 },
2504 'root' : {
2505 'level' : 'WARNING',
2506 },
2507 }
2508
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002509 # config8 defines both compiler and compiler.lexer
2510 # so compiler.parser should not be disabled (since
2511 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002512 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002513 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002514 'disable_existing_loggers' : False,
2515 'formatters': {
2516 'form1' : {
2517 'format' : '%(levelname)s ++ %(message)s',
2518 },
2519 },
2520 'handlers' : {
2521 'hand1' : {
2522 'class' : 'logging.StreamHandler',
2523 'formatter' : 'form1',
2524 'level' : 'NOTSET',
2525 'stream' : 'ext://sys.stdout',
2526 },
2527 },
2528 'loggers' : {
2529 'compiler' : {
2530 'level' : 'DEBUG',
2531 'handlers' : ['hand1'],
2532 },
2533 'compiler.lexer' : {
2534 },
2535 },
2536 'root' : {
2537 'level' : 'WARNING',
2538 },
2539 }
2540
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002541 # config8a disables existing loggers
2542 config8a = {
2543 'version': 1,
2544 'disable_existing_loggers' : True,
2545 'formatters': {
2546 'form1' : {
2547 'format' : '%(levelname)s ++ %(message)s',
2548 },
2549 },
2550 'handlers' : {
2551 'hand1' : {
2552 'class' : 'logging.StreamHandler',
2553 'formatter' : 'form1',
2554 'level' : 'NOTSET',
2555 'stream' : 'ext://sys.stdout',
2556 },
2557 },
2558 'loggers' : {
2559 'compiler' : {
2560 'level' : 'DEBUG',
2561 'handlers' : ['hand1'],
2562 },
2563 'compiler.lexer' : {
2564 },
2565 },
2566 'root' : {
2567 'level' : 'WARNING',
2568 },
2569 }
2570
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002571 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002572 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002573 'formatters': {
2574 'form1' : {
2575 'format' : '%(levelname)s ++ %(message)s',
2576 },
2577 },
2578 'handlers' : {
2579 'hand1' : {
2580 'class' : 'logging.StreamHandler',
2581 'formatter' : 'form1',
2582 'level' : 'WARNING',
2583 'stream' : 'ext://sys.stdout',
2584 },
2585 },
2586 'loggers' : {
2587 'compiler.parser' : {
2588 'level' : 'WARNING',
2589 'handlers' : ['hand1'],
2590 },
2591 },
2592 'root' : {
2593 'level' : 'NOTSET',
2594 },
2595 }
2596
2597 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002598 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002599 'incremental' : True,
2600 'handlers' : {
2601 'hand1' : {
2602 'level' : 'WARNING',
2603 },
2604 },
2605 'loggers' : {
2606 'compiler.parser' : {
2607 'level' : 'INFO',
2608 },
2609 },
2610 }
2611
2612 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002613 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002614 'incremental' : True,
2615 'handlers' : {
2616 'hand1' : {
2617 'level' : 'INFO',
2618 },
2619 },
2620 'loggers' : {
2621 'compiler.parser' : {
2622 'level' : 'INFO',
2623 },
2624 },
2625 }
2626
Mike53f7a7c2017-12-14 14:04:53 +03002627 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002628 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002629 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002630 'formatters': {
2631 'form1' : {
2632 'format' : '%(levelname)s ++ %(message)s',
2633 },
2634 },
2635 'filters' : {
2636 'filt1' : {
2637 'name' : 'compiler.parser',
2638 },
2639 },
2640 'handlers' : {
2641 'hand1' : {
2642 'class' : 'logging.StreamHandler',
2643 'formatter' : 'form1',
2644 'level' : 'NOTSET',
2645 'stream' : 'ext://sys.stdout',
2646 'filters' : ['filt1'],
2647 },
2648 },
2649 'loggers' : {
2650 'compiler.parser' : {
2651 'level' : 'DEBUG',
2652 'filters' : ['filt1'],
2653 },
2654 },
2655 'root' : {
2656 'level' : 'WARNING',
2657 'handlers' : ['hand1'],
2658 },
2659 }
2660
Mike53f7a7c2017-12-14 14:04:53 +03002661 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002662 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002663 'version': 1,
2664 'true_formatters': {
2665 'form1' : {
2666 'format' : '%(levelname)s ++ %(message)s',
2667 },
2668 },
2669 'handler_configs': {
2670 'hand1' : {
2671 'class' : 'logging.StreamHandler',
2672 'formatter' : 'form1',
2673 'level' : 'NOTSET',
2674 'stream' : 'ext://sys.stdout',
2675 },
2676 },
2677 'formatters' : 'cfg://true_formatters',
2678 'handlers' : {
2679 'hand1' : 'cfg://handler_configs[hand1]',
2680 },
2681 'loggers' : {
2682 'compiler.parser' : {
2683 'level' : 'DEBUG',
2684 'handlers' : ['hand1'],
2685 },
2686 },
2687 'root' : {
2688 'level' : 'WARNING',
2689 },
2690 }
2691
Mike53f7a7c2017-12-14 14:04:53 +03002692 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002693 config12 = {
2694 'true_formatters': {
2695 'form1' : {
2696 'format' : '%(levelname)s ++ %(message)s',
2697 },
2698 },
2699 'handler_configs': {
2700 'hand1' : {
2701 'class' : 'logging.StreamHandler',
2702 'formatter' : 'form1',
2703 'level' : 'NOTSET',
2704 'stream' : 'ext://sys.stdout',
2705 },
2706 },
2707 'formatters' : 'cfg://true_formatters',
2708 'handlers' : {
2709 'hand1' : 'cfg://handler_configs[hand1]',
2710 },
2711 'loggers' : {
2712 'compiler.parser' : {
2713 'level' : 'DEBUG',
2714 'handlers' : ['hand1'],
2715 },
2716 },
2717 'root' : {
2718 'level' : 'WARNING',
2719 },
2720 }
2721
Mike53f7a7c2017-12-14 14:04:53 +03002722 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002723 config13 = {
2724 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002725 'true_formatters': {
2726 'form1' : {
2727 'format' : '%(levelname)s ++ %(message)s',
2728 },
2729 },
2730 'handler_configs': {
2731 'hand1' : {
2732 'class' : 'logging.StreamHandler',
2733 'formatter' : 'form1',
2734 'level' : 'NOTSET',
2735 'stream' : 'ext://sys.stdout',
2736 },
2737 },
2738 'formatters' : 'cfg://true_formatters',
2739 'handlers' : {
2740 'hand1' : 'cfg://handler_configs[hand1]',
2741 },
2742 'loggers' : {
2743 'compiler.parser' : {
2744 'level' : 'DEBUG',
2745 'handlers' : ['hand1'],
2746 },
2747 },
2748 'root' : {
2749 'level' : 'WARNING',
2750 },
2751 }
2752
Vinay Sajip8d270232012-11-15 14:20:18 +00002753 # As config0, but with properties
2754 config14 = {
2755 'version': 1,
2756 'formatters': {
2757 'form1' : {
2758 'format' : '%(levelname)s ++ %(message)s',
2759 },
2760 },
2761 'handlers' : {
2762 'hand1' : {
2763 'class' : 'logging.StreamHandler',
2764 'formatter' : 'form1',
2765 'level' : 'NOTSET',
2766 'stream' : 'ext://sys.stdout',
2767 '.': {
2768 'foo': 'bar',
2769 'terminator': '!\n',
2770 }
2771 },
2772 },
2773 'root' : {
2774 'level' : 'WARNING',
2775 'handlers' : ['hand1'],
2776 },
2777 }
2778
Vinay Sajip3f885b52013-03-22 15:19:54 +00002779 out_of_order = {
2780 "version": 1,
2781 "formatters": {
2782 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002783 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2784 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002785 }
2786 },
2787 "handlers": {
2788 "fileGlobal": {
2789 "class": "logging.StreamHandler",
2790 "level": "DEBUG",
2791 "formatter": "mySimpleFormatter"
2792 },
2793 "bufferGlobal": {
2794 "class": "logging.handlers.MemoryHandler",
2795 "capacity": 5,
2796 "formatter": "mySimpleFormatter",
2797 "target": "fileGlobal",
2798 "level": "DEBUG"
2799 }
2800 },
2801 "loggers": {
2802 "mymodule": {
2803 "level": "DEBUG",
2804 "handlers": ["bufferGlobal"],
2805 "propagate": "true"
2806 }
2807 }
2808 }
2809
BNMetrics18fb1fb2018-10-15 19:41:36 +01002810 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2811 custom_formatter_class_validate = {
2812 'version': 1,
2813 'formatters': {
2814 'form1': {
2815 '()': __name__ + '.ExceptionFormatter',
2816 'format': '%(levelname)s:%(name)s:%(message)s',
2817 'validate': False,
2818 },
2819 },
2820 'handlers' : {
2821 'hand1' : {
2822 'class': 'logging.StreamHandler',
2823 'formatter': 'form1',
2824 'level': 'NOTSET',
2825 'stream': 'ext://sys.stdout',
2826 },
2827 },
2828 "loggers": {
2829 "my_test_logger_custom_formatter": {
2830 "level": "DEBUG",
2831 "handlers": ["hand1"],
2832 "propagate": "true"
2833 }
2834 }
2835 }
2836
2837 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2838 custom_formatter_class_validate2 = {
2839 'version': 1,
2840 'formatters': {
2841 'form1': {
2842 'class': __name__ + '.ExceptionFormatter',
2843 'format': '%(levelname)s:%(name)s:%(message)s',
2844 'validate': False,
2845 },
2846 },
2847 'handlers' : {
2848 'hand1' : {
2849 'class': 'logging.StreamHandler',
2850 'formatter': 'form1',
2851 'level': 'NOTSET',
2852 'stream': 'ext://sys.stdout',
2853 },
2854 },
2855 "loggers": {
2856 "my_test_logger_custom_formatter": {
2857 "level": "DEBUG",
2858 "handlers": ["hand1"],
2859 "propagate": "true"
2860 }
2861 }
2862 }
2863
2864 # Configuration with custom class that is not inherited from logging.Formatter
2865 custom_formatter_class_validate3 = {
2866 'version': 1,
2867 'formatters': {
2868 'form1': {
2869 'class': __name__ + '.myCustomFormatter',
2870 'format': '%(levelname)s:%(name)s:%(message)s',
2871 'validate': False,
2872 },
2873 },
2874 'handlers' : {
2875 'hand1' : {
2876 'class': 'logging.StreamHandler',
2877 'formatter': 'form1',
2878 'level': 'NOTSET',
2879 'stream': 'ext://sys.stdout',
2880 },
2881 },
2882 "loggers": {
2883 "my_test_logger_custom_formatter": {
2884 "level": "DEBUG",
2885 "handlers": ["hand1"],
2886 "propagate": "true"
2887 }
2888 }
2889 }
2890
2891 # Configuration with custom function and 'validate' set to False
2892 custom_formatter_with_function = {
2893 'version': 1,
2894 'formatters': {
2895 'form1': {
2896 '()': formatFunc,
2897 'format': '%(levelname)s:%(name)s:%(message)s',
2898 'validate': False,
2899 },
2900 },
2901 'handlers' : {
2902 'hand1' : {
2903 'class': 'logging.StreamHandler',
2904 'formatter': 'form1',
2905 'level': 'NOTSET',
2906 'stream': 'ext://sys.stdout',
2907 },
2908 },
2909 "loggers": {
2910 "my_test_logger_custom_formatter": {
2911 "level": "DEBUG",
2912 "handlers": ["hand1"],
2913 "propagate": "true"
2914 }
2915 }
2916 }
2917
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002918 def apply_config(self, conf):
2919 logging.config.dictConfig(conf)
2920
2921 def test_config0_ok(self):
2922 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002923 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002924 self.apply_config(self.config0)
2925 logger = logging.getLogger()
2926 # Won't output anything
2927 logger.info(self.next_message())
2928 # Outputs a message
2929 logger.error(self.next_message())
2930 self.assert_log_lines([
2931 ('ERROR', '2'),
2932 ], stream=output)
2933 # Original logger output is empty.
2934 self.assert_log_lines([])
2935
2936 def test_config1_ok(self, config=config1):
2937 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002938 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002939 self.apply_config(config)
2940 logger = logging.getLogger("compiler.parser")
2941 # Both will output a message
2942 logger.info(self.next_message())
2943 logger.error(self.next_message())
2944 self.assert_log_lines([
2945 ('INFO', '1'),
2946 ('ERROR', '2'),
2947 ], stream=output)
2948 # Original logger output is empty.
2949 self.assert_log_lines([])
2950
2951 def test_config2_failure(self):
2952 # A simple config which overrides the default settings.
2953 self.assertRaises(Exception, self.apply_config, self.config2)
2954
2955 def test_config2a_failure(self):
2956 # A simple config which overrides the default settings.
2957 self.assertRaises(Exception, self.apply_config, self.config2a)
2958
2959 def test_config2b_failure(self):
2960 # A simple config which overrides the default settings.
2961 self.assertRaises(Exception, self.apply_config, self.config2b)
2962
2963 def test_config3_failure(self):
2964 # A simple config which overrides the default settings.
2965 self.assertRaises(Exception, self.apply_config, self.config3)
2966
2967 def test_config4_ok(self):
2968 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002969 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002970 self.apply_config(self.config4)
2971 #logger = logging.getLogger()
2972 try:
2973 raise RuntimeError()
2974 except RuntimeError:
2975 logging.exception("just testing")
2976 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002977 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002978 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2979 # Original logger output is empty
2980 self.assert_log_lines([])
2981
2982 def test_config4a_ok(self):
2983 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002984 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002985 self.apply_config(self.config4a)
2986 #logger = logging.getLogger()
2987 try:
2988 raise RuntimeError()
2989 except RuntimeError:
2990 logging.exception("just testing")
2991 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002992 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002993 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2994 # Original logger output is empty
2995 self.assert_log_lines([])
2996
2997 def test_config5_ok(self):
2998 self.test_config1_ok(config=self.config5)
2999
3000 def test_config6_failure(self):
3001 self.assertRaises(Exception, self.apply_config, self.config6)
3002
3003 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003004 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003005 self.apply_config(self.config1)
3006 logger = logging.getLogger("compiler.parser")
3007 # Both will output a message
3008 logger.info(self.next_message())
3009 logger.error(self.next_message())
3010 self.assert_log_lines([
3011 ('INFO', '1'),
3012 ('ERROR', '2'),
3013 ], stream=output)
3014 # Original logger output is empty.
3015 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003016 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003017 self.apply_config(self.config7)
3018 logger = logging.getLogger("compiler.parser")
3019 self.assertTrue(logger.disabled)
3020 logger = logging.getLogger("compiler.lexer")
3021 # Both will output a message
3022 logger.info(self.next_message())
3023 logger.error(self.next_message())
3024 self.assert_log_lines([
3025 ('INFO', '3'),
3026 ('ERROR', '4'),
3027 ], stream=output)
3028 # Original logger output is empty.
3029 self.assert_log_lines([])
3030
Mike53f7a7c2017-12-14 14:04:53 +03003031 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003032 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003033 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003034 self.apply_config(self.config1)
3035 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003036 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003037 logger.info(self.next_message())
3038 logger.error(self.next_message())
3039 self.assert_log_lines([
3040 ('INFO', '1'),
3041 ('ERROR', '2'),
3042 ], stream=output)
3043 # Original logger output is empty.
3044 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003045 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003046 self.apply_config(self.config8)
3047 logger = logging.getLogger("compiler.parser")
3048 self.assertFalse(logger.disabled)
3049 # Both will output a message
3050 logger.info(self.next_message())
3051 logger.error(self.next_message())
3052 logger = logging.getLogger("compiler.lexer")
3053 # Both will output a message
3054 logger.info(self.next_message())
3055 logger.error(self.next_message())
3056 self.assert_log_lines([
3057 ('INFO', '3'),
3058 ('ERROR', '4'),
3059 ('INFO', '5'),
3060 ('ERROR', '6'),
3061 ], stream=output)
3062 # Original logger output is empty.
3063 self.assert_log_lines([])
3064
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003065 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003066 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003067 self.apply_config(self.config1a)
3068 logger = logging.getLogger("compiler.parser")
3069 # See issue #11424. compiler-hyphenated sorts
3070 # between compiler and compiler.xyz and this
3071 # was preventing compiler.xyz from being included
3072 # in the child loggers of compiler because of an
3073 # overzealous loop termination condition.
3074 hyphenated = logging.getLogger('compiler-hyphenated')
3075 # All will output a message
3076 logger.info(self.next_message())
3077 logger.error(self.next_message())
3078 hyphenated.critical(self.next_message())
3079 self.assert_log_lines([
3080 ('INFO', '1'),
3081 ('ERROR', '2'),
3082 ('CRITICAL', '3'),
3083 ], stream=output)
3084 # Original logger output is empty.
3085 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003086 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003087 self.apply_config(self.config8a)
3088 logger = logging.getLogger("compiler.parser")
3089 self.assertFalse(logger.disabled)
3090 # Both will output a message
3091 logger.info(self.next_message())
3092 logger.error(self.next_message())
3093 logger = logging.getLogger("compiler.lexer")
3094 # Both will output a message
3095 logger.info(self.next_message())
3096 logger.error(self.next_message())
3097 # Will not appear
3098 hyphenated.critical(self.next_message())
3099 self.assert_log_lines([
3100 ('INFO', '4'),
3101 ('ERROR', '5'),
3102 ('INFO', '6'),
3103 ('ERROR', '7'),
3104 ], stream=output)
3105 # Original logger output is empty.
3106 self.assert_log_lines([])
3107
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003108 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003109 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003110 self.apply_config(self.config9)
3111 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003112 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003113 logger.info(self.next_message())
3114 self.assert_log_lines([], stream=output)
3115 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003116 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003117 logger.info(self.next_message())
3118 self.assert_log_lines([], stream=output)
3119 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003120 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003121 logger.info(self.next_message())
3122 self.assert_log_lines([
3123 ('INFO', '3'),
3124 ], stream=output)
3125
3126 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003127 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003128 self.apply_config(self.config10)
3129 logger = logging.getLogger("compiler.parser")
3130 logger.warning(self.next_message())
3131 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003132 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003133 logger.warning(self.next_message())
3134 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003135 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003136 logger.warning(self.next_message())
3137 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003138 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003139 logger.error(self.next_message())
3140 self.assert_log_lines([
3141 ('WARNING', '1'),
3142 ('ERROR', '4'),
3143 ], stream=output)
3144
3145 def test_config11_ok(self):
3146 self.test_config1_ok(self.config11)
3147
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003148 def test_config12_failure(self):
3149 self.assertRaises(Exception, self.apply_config, self.config12)
3150
3151 def test_config13_failure(self):
3152 self.assertRaises(Exception, self.apply_config, self.config13)
3153
Vinay Sajip8d270232012-11-15 14:20:18 +00003154 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003155 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003156 self.apply_config(self.config14)
3157 h = logging._handlers['hand1']
3158 self.assertEqual(h.foo, 'bar')
3159 self.assertEqual(h.terminator, '!\n')
3160 logging.warning('Exclamation')
3161 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3162
Xtreak087570a2018-07-02 14:27:46 +05303163 def test_config15_ok(self):
3164
3165 def cleanup(h1, fn):
3166 h1.close()
3167 os.remove(fn)
3168
3169 with self.check_no_resource_warning():
3170 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3171 os.close(fd)
3172
3173 config = {
3174 "version": 1,
3175 "handlers": {
3176 "file": {
3177 "class": "logging.FileHandler",
3178 "filename": fn
3179 }
3180 },
3181 "root": {
3182 "handlers": ["file"]
3183 }
3184 }
3185
3186 self.apply_config(config)
3187 self.apply_config(config)
3188
3189 handler = logging.root.handlers[0]
3190 self.addCleanup(cleanup, handler, fn)
3191
Vinay Sajip4ded5512012-10-02 15:56:16 +01003192 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003193 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003194 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003195 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003196 t.start()
3197 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003198 # Now get the port allocated
3199 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003200 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003201 try:
3202 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3203 sock.settimeout(2.0)
3204 sock.connect(('localhost', port))
3205
3206 slen = struct.pack('>L', len(text))
3207 s = slen + text
3208 sentsofar = 0
3209 left = len(s)
3210 while left > 0:
3211 sent = sock.send(s[sentsofar:])
3212 sentsofar += sent
3213 left -= sent
3214 sock.close()
3215 finally:
3216 t.ready.wait(2.0)
3217 logging.config.stopListening()
Hai Shie80697d2020-05-28 06:10:27 +08003218 threading_helper.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003219
3220 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003221 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003222 self.setup_via_listener(json.dumps(self.config10))
3223 logger = logging.getLogger("compiler.parser")
3224 logger.warning(self.next_message())
3225 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003226 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003227 logger.warning(self.next_message())
3228 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003229 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003230 logger.warning(self.next_message())
3231 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003232 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003233 logger.error(self.next_message())
3234 self.assert_log_lines([
3235 ('WARNING', '1'),
3236 ('ERROR', '4'),
3237 ], stream=output)
3238
3239 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003240 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003241 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3242 logger = logging.getLogger("compiler.parser")
3243 # Both will output a message
3244 logger.info(self.next_message())
3245 logger.error(self.next_message())
3246 self.assert_log_lines([
3247 ('INFO', '1'),
3248 ('ERROR', '2'),
3249 ], stream=output)
3250 # Original logger output is empty.
3251 self.assert_log_lines([])
3252
Vinay Sajip4ded5512012-10-02 15:56:16 +01003253 def test_listen_verify(self):
3254
3255 def verify_fail(stuff):
3256 return None
3257
3258 def verify_reverse(stuff):
3259 return stuff[::-1]
3260
3261 logger = logging.getLogger("compiler.parser")
3262 to_send = textwrap.dedent(ConfigFileTest.config1)
3263 # First, specify a verification function that will fail.
3264 # We expect to see no output, since our configuration
3265 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003266 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003267 self.setup_via_listener(to_send, verify_fail)
3268 # Both will output a message
3269 logger.info(self.next_message())
3270 logger.error(self.next_message())
3271 self.assert_log_lines([], stream=output)
3272 # Original logger output has the stuff we logged.
3273 self.assert_log_lines([
3274 ('INFO', '1'),
3275 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003276 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003277
3278 # Now, perform no verification. Our configuration
3279 # should take effect.
3280
Vinay Sajip1feedb42014-05-31 08:19:12 +01003281 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003282 self.setup_via_listener(to_send) # no verify callable specified
3283 logger = logging.getLogger("compiler.parser")
3284 # Both will output a message
3285 logger.info(self.next_message())
3286 logger.error(self.next_message())
3287 self.assert_log_lines([
3288 ('INFO', '3'),
3289 ('ERROR', '4'),
3290 ], stream=output)
3291 # Original logger output still has the stuff we logged before.
3292 self.assert_log_lines([
3293 ('INFO', '1'),
3294 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003295 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003296
3297 # Now, perform verification which transforms the bytes.
3298
Vinay Sajip1feedb42014-05-31 08:19:12 +01003299 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003300 self.setup_via_listener(to_send[::-1], verify_reverse)
3301 logger = logging.getLogger("compiler.parser")
3302 # Both will output a message
3303 logger.info(self.next_message())
3304 logger.error(self.next_message())
3305 self.assert_log_lines([
3306 ('INFO', '5'),
3307 ('ERROR', '6'),
3308 ], stream=output)
3309 # Original logger output still has the stuff we logged before.
3310 self.assert_log_lines([
3311 ('INFO', '1'),
3312 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003313 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003314
Vinay Sajip3f885b52013-03-22 15:19:54 +00003315 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003316 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3317
3318 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003319 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003320 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3321
3322 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003323 handler = logging.getLogger('mymodule').handlers[0]
3324 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003325 self.assertIsInstance(handler.formatter._style,
3326 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003327
BNMetrics18fb1fb2018-10-15 19:41:36 +01003328 def test_custom_formatter_class_with_validate(self):
3329 self.apply_config(self.custom_formatter_class_validate)
3330 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3331 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3332
3333 def test_custom_formatter_class_with_validate2(self):
3334 self.apply_config(self.custom_formatter_class_validate2)
3335 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3336 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3337
3338 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3339 config = self.custom_formatter_class_validate.copy()
3340 config['formatters']['form1']['style'] = "$"
3341
3342 # Exception should not be raise as we have configured 'validate' to False
3343 self.apply_config(config)
3344 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3345 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3346
3347 def test_custom_formatter_class_with_validate3(self):
3348 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3349
3350 def test_custom_formatter_function_with_validate(self):
3351 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3352
Vinay Sajip373baef2011-04-26 20:05:24 +01003353 def test_baseconfig(self):
3354 d = {
3355 'atuple': (1, 2, 3),
3356 'alist': ['a', 'b', 'c'],
3357 'adict': {'d': 'e', 'f': 3 },
3358 'nest1': ('g', ('h', 'i'), 'j'),
3359 'nest2': ['k', ['l', 'm'], 'n'],
3360 'nest3': ['o', 'cfg://alist', 'p'],
3361 }
3362 bc = logging.config.BaseConfigurator(d)
3363 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3364 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3365 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3366 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3367 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3368 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3369 v = bc.convert('cfg://nest3')
3370 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3371 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3372 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3373 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003374
Vinay Sajip46abfc12020-01-01 19:32:11 +00003375 def test_namedtuple(self):
3376 # see bpo-39142
3377 from collections import namedtuple
3378
3379 class MyHandler(logging.StreamHandler):
3380 def __init__(self, resource, *args, **kwargs):
3381 super().__init__(*args, **kwargs)
3382 self.resource: namedtuple = resource
3383
3384 def emit(self, record):
3385 record.msg += f' {self.resource.type}'
3386 return super().emit(record)
3387
3388 Resource = namedtuple('Resource', ['type', 'labels'])
3389 resource = Resource(type='my_type', labels=['a'])
3390
3391 config = {
3392 'version': 1,
3393 'handlers': {
3394 'myhandler': {
3395 '()': MyHandler,
3396 'resource': resource
3397 }
3398 },
3399 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3400 }
3401 with support.captured_stderr() as stderr:
3402 self.apply_config(config)
3403 logging.info('some log')
3404 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3405
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003406class ManagerTest(BaseTest):
3407 def test_manager_loggerclass(self):
3408 logged = []
3409
3410 class MyLogger(logging.Logger):
3411 def _log(self, level, msg, args, exc_info=None, extra=None):
3412 logged.append(msg)
3413
3414 man = logging.Manager(None)
3415 self.assertRaises(TypeError, man.setLoggerClass, int)
3416 man.setLoggerClass(MyLogger)
3417 logger = man.getLogger('test')
3418 logger.warning('should appear in logged')
3419 logging.warning('should not appear in logged')
3420
3421 self.assertEqual(logged, ['should appear in logged'])
3422
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003423 def test_set_log_record_factory(self):
3424 man = logging.Manager(None)
3425 expected = object()
3426 man.setLogRecordFactory(expected)
3427 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003428
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003429class ChildLoggerTest(BaseTest):
3430 def test_child_loggers(self):
3431 r = logging.getLogger()
3432 l1 = logging.getLogger('abc')
3433 l2 = logging.getLogger('def.ghi')
3434 c1 = r.getChild('xyz')
3435 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003436 self.assertIs(c1, logging.getLogger('xyz'))
3437 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003438 c1 = l1.getChild('def')
3439 c2 = c1.getChild('ghi')
3440 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003441 self.assertIs(c1, logging.getLogger('abc.def'))
3442 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3443 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003444
3445
Vinay Sajip6fac8172010-10-19 20:44:14 +00003446class DerivedLogRecord(logging.LogRecord):
3447 pass
3448
Vinay Sajip61561522010-12-03 11:50:38 +00003449class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003450
3451 def setUp(self):
3452 class CheckingFilter(logging.Filter):
3453 def __init__(self, cls):
3454 self.cls = cls
3455
3456 def filter(self, record):
3457 t = type(record)
3458 if t is not self.cls:
3459 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3460 self.cls)
3461 raise TypeError(msg)
3462 return True
3463
3464 BaseTest.setUp(self)
3465 self.filter = CheckingFilter(DerivedLogRecord)
3466 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003467 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003468
3469 def tearDown(self):
3470 self.root_logger.removeFilter(self.filter)
3471 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003472 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003473
3474 def test_logrecord_class(self):
3475 self.assertRaises(TypeError, self.root_logger.warning,
3476 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003477 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003478 self.root_logger.error(self.next_message())
3479 self.assert_log_lines([
3480 ('root', 'ERROR', '2'),
3481 ])
3482
3483
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003484class QueueHandlerTest(BaseTest):
3485 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003486 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003487
3488 def setUp(self):
3489 BaseTest.setUp(self)
3490 self.queue = queue.Queue(-1)
3491 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003492 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003493 self.que_logger = logging.getLogger('que')
3494 self.que_logger.propagate = False
3495 self.que_logger.setLevel(logging.WARNING)
3496 self.que_logger.addHandler(self.que_hdlr)
3497
3498 def tearDown(self):
3499 self.que_hdlr.close()
3500 BaseTest.tearDown(self)
3501
3502 def test_queue_handler(self):
3503 self.que_logger.debug(self.next_message())
3504 self.assertRaises(queue.Empty, self.queue.get_nowait)
3505 self.que_logger.info(self.next_message())
3506 self.assertRaises(queue.Empty, self.queue.get_nowait)
3507 msg = self.next_message()
3508 self.que_logger.warning(msg)
3509 data = self.queue.get_nowait()
3510 self.assertTrue(isinstance(data, logging.LogRecord))
3511 self.assertEqual(data.name, self.que_logger.name)
3512 self.assertEqual((data.msg, data.args), (msg, None))
3513
favlladfe3442017-08-01 20:12:26 +02003514 def test_formatting(self):
3515 msg = self.next_message()
3516 levelname = logging.getLevelName(logging.WARNING)
3517 log_format_str = '{name} -> {levelname}: {message}'
3518 formatted_msg = log_format_str.format(name=self.name,
3519 levelname=levelname, message=msg)
3520 formatter = logging.Formatter(self.log_format)
3521 self.que_hdlr.setFormatter(formatter)
3522 self.que_logger.warning(msg)
3523 log_record = self.queue.get_nowait()
3524 self.assertEqual(formatted_msg, log_record.msg)
3525 self.assertEqual(formatted_msg, log_record.message)
3526
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003527 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3528 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003529 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003530 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003531 listener = logging.handlers.QueueListener(self.queue, handler)
3532 listener.start()
3533 try:
3534 self.que_logger.warning(self.next_message())
3535 self.que_logger.error(self.next_message())
3536 self.que_logger.critical(self.next_message())
3537 finally:
3538 listener.stop()
3539 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3540 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3541 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003542 handler.close()
3543
3544 # Now test with respect_handler_level set
3545
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003546 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003547 handler.setLevel(logging.CRITICAL)
3548 listener = logging.handlers.QueueListener(self.queue, handler,
3549 respect_handler_level=True)
3550 listener.start()
3551 try:
3552 self.que_logger.warning(self.next_message())
3553 self.que_logger.error(self.next_message())
3554 self.que_logger.critical(self.next_message())
3555 finally:
3556 listener.stop()
3557 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3558 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3559 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003560 handler.close()
3561
3562 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3563 'logging.handlers.QueueListener required for this test')
3564 def test_queue_listener_with_StreamHandler(self):
3565 # Test that traceback only appends once (bpo-34334).
3566 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3567 listener.start()
3568 try:
3569 1 / 0
3570 except ZeroDivisionError as e:
3571 exc = e
3572 self.que_logger.exception(self.next_message(), exc_info=exc)
3573 listener.stop()
3574 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003575
Xtreak2dad9602019-04-07 13:21:27 +05303576 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3577 'logging.handlers.QueueListener required for this test')
3578 def test_queue_listener_with_multiple_handlers(self):
3579 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3580 self.que_hdlr.setFormatter(self.root_formatter)
3581 self.que_logger.addHandler(self.root_hdlr)
3582
3583 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3584 listener.start()
3585 self.que_logger.error("error")
3586 listener.stop()
3587 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3588
Vinay Sajipd61910c2016-09-08 01:13:39 +01003589if hasattr(logging.handlers, 'QueueListener'):
3590 import multiprocessing
3591 from unittest.mock import patch
3592
3593 class QueueListenerTest(BaseTest):
3594 """
3595 Tests based on patch submitted for issue #27930. Ensure that
3596 QueueListener handles all log messages.
3597 """
3598
3599 repeat = 20
3600
3601 @staticmethod
3602 def setup_and_log(log_queue, ident):
3603 """
3604 Creates a logger with a QueueHandler that logs to a queue read by a
3605 QueueListener. Starts the listener, logs five messages, and stops
3606 the listener.
3607 """
3608 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3609 logger.setLevel(logging.DEBUG)
3610 handler = logging.handlers.QueueHandler(log_queue)
3611 logger.addHandler(handler)
3612 listener = logging.handlers.QueueListener(log_queue)
3613 listener.start()
3614
3615 logger.info('one')
3616 logger.info('two')
3617 logger.info('three')
3618 logger.info('four')
3619 logger.info('five')
3620
3621 listener.stop()
3622 logger.removeHandler(handler)
3623 handler.close()
3624
3625 @patch.object(logging.handlers.QueueListener, 'handle')
3626 def test_handle_called_with_queue_queue(self, mock_handle):
3627 for i in range(self.repeat):
3628 log_queue = queue.Queue()
3629 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3630 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3631 'correct number of handled log messages')
3632
3633 @patch.object(logging.handlers.QueueListener, 'handle')
3634 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003635 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003636 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003637 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003638 for i in range(self.repeat):
3639 log_queue = multiprocessing.Queue()
3640 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003641 log_queue.close()
3642 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003643 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3644 'correct number of handled log messages')
3645
3646 @staticmethod
3647 def get_all_from_queue(log_queue):
3648 try:
3649 while True:
3650 yield log_queue.get_nowait()
3651 except queue.Empty:
3652 return []
3653
3654 def test_no_messages_in_queue_after_stop(self):
3655 """
3656 Five messages are logged then the QueueListener is stopped. This
3657 test then gets everything off the queue. Failure of this test
3658 indicates that messages were not registered on the queue until
3659 _after_ the QueueListener stopped.
3660 """
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003661 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003662 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003663 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003664 for i in range(self.repeat):
3665 queue = multiprocessing.Queue()
3666 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3667 # time.sleep(1)
3668 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003669 queue.close()
3670 queue.join_thread()
3671
Vinay Sajipd61910c2016-09-08 01:13:39 +01003672 expected = [[], [logging.handlers.QueueListener._sentinel]]
3673 self.assertIn(items, expected,
3674 'Found unexpected messages in queue: %s' % (
3675 [m.msg if isinstance(m, logging.LogRecord)
3676 else m for m in items]))
3677
Bar Harel6b282e12019-06-01 12:19:09 +03003678 def test_calls_task_done_after_stop(self):
3679 # Issue 36813: Make sure queue.join does not deadlock.
3680 log_queue = queue.Queue()
3681 listener = logging.handlers.QueueListener(log_queue)
3682 listener.start()
3683 listener.stop()
3684 with self.assertRaises(ValueError):
3685 # Make sure all tasks are done and .join won't block.
3686 log_queue.task_done()
3687
Vinay Sajipe723e962011-04-15 22:27:17 +01003688
Vinay Sajip37eb3382011-04-26 20:26:41 +01003689ZERO = datetime.timedelta(0)
3690
3691class UTC(datetime.tzinfo):
3692 def utcoffset(self, dt):
3693 return ZERO
3694
3695 dst = utcoffset
3696
3697 def tzname(self, dt):
3698 return 'UTC'
3699
3700utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003701
Vinay Sajipa39c5712010-10-25 13:57:39 +00003702class FormatterTest(unittest.TestCase):
3703 def setUp(self):
3704 self.common = {
3705 'name': 'formatter.test',
3706 'level': logging.DEBUG,
3707 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3708 'lineno': 42,
3709 'exc_info': None,
3710 'func': None,
3711 'msg': 'Message with %d %s',
3712 'args': (2, 'placeholders'),
3713 }
3714 self.variants = {
Bar Harel8f192d12020-06-18 17:18:58 +03003715 'custom': {
3716 'custom': 1234
3717 }
Vinay Sajipa39c5712010-10-25 13:57:39 +00003718 }
3719
3720 def get_record(self, name=None):
3721 result = dict(self.common)
3722 if name is not None:
3723 result.update(self.variants[name])
3724 return logging.makeLogRecord(result)
3725
BNMetrics18fb1fb2018-10-15 19:41:36 +01003726 def assert_error_message(self, exception, message, *args, **kwargs):
3727 try:
3728 self.assertRaises(exception, *args, **kwargs)
3729 except exception as e:
3730 self.assertEqual(message, e.message)
3731
Vinay Sajipa39c5712010-10-25 13:57:39 +00003732 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003733 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003734 r = self.get_record()
3735 f = logging.Formatter('${%(message)s}')
3736 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3737 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003738 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003739 self.assertFalse(f.usesTime())
3740 f = logging.Formatter('%(asctime)s')
3741 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003742 f = logging.Formatter('%(asctime)-15s')
3743 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003744 f = logging.Formatter('%(asctime)#15s')
3745 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003746
3747 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003748 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003749 r = self.get_record()
3750 f = logging.Formatter('$%{message}%$', style='{')
3751 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3752 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003753 self.assertRaises(ValueError, f.format, r)
3754 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003755 self.assertFalse(f.usesTime())
3756 f = logging.Formatter('{asctime}', style='{')
3757 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003758 f = logging.Formatter('{asctime!s:15}', style='{')
3759 self.assertTrue(f.usesTime())
3760 f = logging.Formatter('{asctime:15}', style='{')
3761 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003762
3763 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003764 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003765 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003766 f = logging.Formatter('${message}', style='$')
3767 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003768 f = logging.Formatter('$message', style='$')
3769 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3770 f = logging.Formatter('$$%${message}%$$', style='$')
3771 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3772 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003773 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003774 self.assertFalse(f.usesTime())
3775 f = logging.Formatter('${asctime}', style='$')
3776 self.assertTrue(f.usesTime())
3777 f = logging.Formatter('$asctime', style='$')
3778 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003779 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003780 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003781 f = logging.Formatter('${asctime}--', style='$')
3782 self.assertTrue(f.usesTime())
3783
3784 def test_format_validate(self):
3785 # Check correct formatting
3786 # Percentage style
3787 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3788 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3789 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3790 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3791 f = logging.Formatter("%(process)#+027.23X")
3792 self.assertEqual(f._fmt, "%(process)#+027.23X")
3793 f = logging.Formatter("%(foo)#.*g")
3794 self.assertEqual(f._fmt, "%(foo)#.*g")
3795
3796 # StrFormat Style
3797 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3798 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3799 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3800 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3801 f = logging.Formatter("{customfield!s:#<30}", style="{")
3802 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3803 f = logging.Formatter("{message!r}", style="{")
3804 self.assertEqual(f._fmt, "{message!r}")
3805 f = logging.Formatter("{message!s}", style="{")
3806 self.assertEqual(f._fmt, "{message!s}")
3807 f = logging.Formatter("{message!a}", style="{")
3808 self.assertEqual(f._fmt, "{message!a}")
3809 f = logging.Formatter("{process!r:4.2}", style="{")
3810 self.assertEqual(f._fmt, "{process!r:4.2}")
3811 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3812 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3813 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3814 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3815 f = logging.Formatter("{foo:12.{p}}", style="{")
3816 self.assertEqual(f._fmt, "{foo:12.{p}}")
3817 f = logging.Formatter("{foo:{w}.6}", style="{")
3818 self.assertEqual(f._fmt, "{foo:{w}.6}")
3819 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3820 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3821 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3822 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3823 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3824 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3825
3826 # Dollar style
3827 f = logging.Formatter("${asctime} - $message", style="$")
3828 self.assertEqual(f._fmt, "${asctime} - $message")
3829 f = logging.Formatter("$bar $$", style="$")
3830 self.assertEqual(f._fmt, "$bar $$")
3831 f = logging.Formatter("$bar $$$$", style="$")
3832 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3833
3834 # Testing when ValueError being raised from incorrect format
3835 # Percentage Style
3836 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3837 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3838 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3839 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3840 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3841 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3842 self.assertRaises(ValueError, logging.Formatter, '${message}')
3843 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3844 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3845
3846 # StrFormat Style
3847 # Testing failure for '-' in field name
3848 self.assert_error_message(
3849 ValueError,
3850 "invalid field name/expression: 'name-thing'",
3851 logging.Formatter, "{name-thing}", style="{"
3852 )
3853 # Testing failure for style mismatch
3854 self.assert_error_message(
3855 ValueError,
3856 "invalid format: no fields",
3857 logging.Formatter, '%(asctime)s', style='{'
3858 )
3859 # Testing failure for invalid conversion
3860 self.assert_error_message(
3861 ValueError,
3862 "invalid conversion: 'Z'"
3863 )
3864 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3865 self.assert_error_message(
3866 ValueError,
3867 "invalid format: expected ':' after conversion specifier",
3868 logging.Formatter, '{asctime!aa:15}', style='{'
3869 )
3870 # Testing failure for invalid spec
3871 self.assert_error_message(
3872 ValueError,
3873 "bad specifier: '.2ff'",
3874 logging.Formatter, '{process:.2ff}', style='{'
3875 )
3876 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3877 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3878 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3879 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3880 # Testing failure for mismatch braces
3881 self.assert_error_message(
3882 ValueError,
3883 "invalid format: unmatched '{' in format spec",
3884 logging.Formatter, '{process', style='{'
3885 )
3886 self.assert_error_message(
3887 ValueError,
3888 "invalid format: unmatched '{' in format spec",
3889 logging.Formatter, 'process}', style='{'
3890 )
3891 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3892 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3893 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3894 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3895 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3896 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3897 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3898 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3899 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3900
3901 # Dollar style
3902 # Testing failure for mismatch bare $
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, 'bar $', style='$'
3912 )
3913 self.assert_error_message(
3914 ValueError,
3915 "invalid format: bare \'$\' not allowed",
3916 logging.Formatter, 'foo $.', style='$'
3917 )
3918 # Testing failure for mismatch style
3919 self.assert_error_message(
3920 ValueError,
3921 "invalid format: no fields",
3922 logging.Formatter, '{asctime}', style='$'
3923 )
3924 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3925
3926 # Testing failure for incorrect fields
3927 self.assert_error_message(
3928 ValueError,
3929 "invalid format: no fields",
3930 logging.Formatter, 'foo', style='$'
3931 )
3932 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003933
Bar Harel8f192d12020-06-18 17:18:58 +03003934 def test_defaults_parameter(self):
3935 fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message']
3936 styles = ['%', '{', '$']
3937 for fmt, style in zip(fmts, styles):
3938 f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'})
3939 r = self.get_record()
3940 self.assertEqual(f.format(r), 'Default Message with 2 placeholders')
3941 r = self.get_record("custom")
3942 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3943
3944 # Without default
3945 f = logging.Formatter(fmt, style=style)
3946 r = self.get_record()
3947 self.assertRaises(ValueError, f.format, r)
3948
3949 # Non-existing default is ignored
3950 f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'})
3951 r = self.get_record("custom")
3952 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3953
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003954 def test_invalid_style(self):
3955 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3956
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003957 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003958 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003959 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3960 # We use None to indicate we want the local timezone
3961 # We're essentially converting a UTC time to local time
3962 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003963 r.msecs = 123
3964 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003965 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003966 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3967 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003968 f.format(r)
3969 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
3970
Mariusz Felisiak06a35542020-04-17 18:02:47 +02003971 def test_default_msec_format_none(self):
3972 class NoMsecFormatter(logging.Formatter):
3973 default_msec_format = None
3974 default_time_format = '%d/%m/%Y %H:%M:%S'
3975
3976 r = self.get_record()
3977 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
3978 r.created = time.mktime(dt.astimezone(None).timetuple())
3979 f = NoMsecFormatter()
3980 f.converter = time.gmtime
3981 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
3982
3983
Vinay Sajip985ef872011-04-26 19:34:04 +01003984class TestBufferingFormatter(logging.BufferingFormatter):
3985 def formatHeader(self, records):
3986 return '[(%d)' % len(records)
3987
3988 def formatFooter(self, records):
3989 return '(%d)]' % len(records)
3990
3991class BufferingFormatterTest(unittest.TestCase):
3992 def setUp(self):
3993 self.records = [
3994 logging.makeLogRecord({'msg': 'one'}),
3995 logging.makeLogRecord({'msg': 'two'}),
3996 ]
3997
3998 def test_default(self):
3999 f = logging.BufferingFormatter()
4000 self.assertEqual('', f.format([]))
4001 self.assertEqual('onetwo', f.format(self.records))
4002
4003 def test_custom(self):
4004 f = TestBufferingFormatter()
4005 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
4006 lf = logging.Formatter('<%(message)s>')
4007 f = TestBufferingFormatter(lf)
4008 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004009
4010class ExceptionTest(BaseTest):
4011 def test_formatting(self):
4012 r = self.root_logger
4013 h = RecordingHandler()
4014 r.addHandler(h)
4015 try:
4016 raise RuntimeError('deliberate mistake')
4017 except:
4018 logging.exception('failed', stack_info=True)
4019 r.removeHandler(h)
4020 h.close()
4021 r = h.records[0]
4022 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
4023 'call last):\n'))
4024 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4025 'deliberate mistake'))
4026 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4027 'call last):\n'))
4028 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4029 'stack_info=True)'))
4030
4031
Vinay Sajip5a27d402010-12-10 11:42:57 +00004032class LastResortTest(BaseTest):
4033 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004034 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004035 root = self.root_logger
4036 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004037 old_lastresort = logging.lastResort
4038 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004039
Vinay Sajip5a27d402010-12-10 11:42:57 +00004040 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004041 with support.captured_stderr() as stderr:
4042 root.debug('This should not appear')
4043 self.assertEqual(stderr.getvalue(), '')
4044 root.warning('Final chance!')
4045 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4046
4047 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004048 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004049 with support.captured_stderr() as stderr:
4050 root.warning('Final chance!')
4051 msg = 'No handlers could be found for logger "root"\n'
4052 self.assertEqual(stderr.getvalue(), msg)
4053
Vinay Sajip5a27d402010-12-10 11:42:57 +00004054 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004055 with support.captured_stderr() as stderr:
4056 root.warning('Final chance!')
4057 self.assertEqual(stderr.getvalue(), '')
4058
4059 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004060 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004061 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004062 with support.captured_stderr() as stderr:
4063 root.warning('Final chance!')
4064 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004065 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004066 root.addHandler(self.root_hdlr)
4067 logging.lastResort = old_lastresort
4068 logging.raiseExceptions = old_raise_exceptions
4069
4070
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004071class FakeHandler:
4072
4073 def __init__(self, identifier, called):
4074 for method in ('acquire', 'flush', 'close', 'release'):
4075 setattr(self, method, self.record_call(identifier, method, called))
4076
4077 def record_call(self, identifier, method_name, called):
4078 def inner():
4079 called.append('{} - {}'.format(identifier, method_name))
4080 return inner
4081
4082
4083class RecordingHandler(logging.NullHandler):
4084
4085 def __init__(self, *args, **kwargs):
4086 super(RecordingHandler, self).__init__(*args, **kwargs)
4087 self.records = []
4088
4089 def handle(self, record):
4090 """Keep track of all the emitted records."""
4091 self.records.append(record)
4092
4093
4094class ShutdownTest(BaseTest):
4095
Vinay Sajip5e66b162011-04-20 15:41:14 +01004096 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004097
4098 def setUp(self):
4099 super(ShutdownTest, self).setUp()
4100 self.called = []
4101
4102 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004103 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004104
4105 def raise_error(self, error):
4106 def inner():
4107 raise error()
4108 return inner
4109
4110 def test_no_failure(self):
4111 # create some fake handlers
4112 handler0 = FakeHandler(0, self.called)
4113 handler1 = FakeHandler(1, self.called)
4114 handler2 = FakeHandler(2, self.called)
4115
4116 # create live weakref to those handlers
4117 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4118
4119 logging.shutdown(handlerList=list(handlers))
4120
4121 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4122 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4123 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4124 self.assertEqual(expected, self.called)
4125
4126 def _test_with_failure_in_method(self, method, error):
4127 handler = FakeHandler(0, self.called)
4128 setattr(handler, method, self.raise_error(error))
4129 handlers = [logging.weakref.ref(handler)]
4130
4131 logging.shutdown(handlerList=list(handlers))
4132
4133 self.assertEqual('0 - release', self.called[-1])
4134
4135 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004136 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004137
4138 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004139 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004140
4141 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004142 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004143
4144 def test_with_valueerror_in_acquire(self):
4145 self._test_with_failure_in_method('acquire', ValueError)
4146
4147 def test_with_valueerror_in_flush(self):
4148 self._test_with_failure_in_method('flush', ValueError)
4149
4150 def test_with_valueerror_in_close(self):
4151 self._test_with_failure_in_method('close', ValueError)
4152
4153 def test_with_other_error_in_acquire_without_raise(self):
4154 logging.raiseExceptions = False
4155 self._test_with_failure_in_method('acquire', IndexError)
4156
4157 def test_with_other_error_in_flush_without_raise(self):
4158 logging.raiseExceptions = False
4159 self._test_with_failure_in_method('flush', IndexError)
4160
4161 def test_with_other_error_in_close_without_raise(self):
4162 logging.raiseExceptions = False
4163 self._test_with_failure_in_method('close', IndexError)
4164
4165 def test_with_other_error_in_acquire_with_raise(self):
4166 logging.raiseExceptions = True
4167 self.assertRaises(IndexError, self._test_with_failure_in_method,
4168 'acquire', IndexError)
4169
4170 def test_with_other_error_in_flush_with_raise(self):
4171 logging.raiseExceptions = True
4172 self.assertRaises(IndexError, self._test_with_failure_in_method,
4173 'flush', IndexError)
4174
4175 def test_with_other_error_in_close_with_raise(self):
4176 logging.raiseExceptions = True
4177 self.assertRaises(IndexError, self._test_with_failure_in_method,
4178 'close', IndexError)
4179
4180
4181class ModuleLevelMiscTest(BaseTest):
4182
Vinay Sajip5e66b162011-04-20 15:41:14 +01004183 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004184
4185 def test_disable(self):
4186 old_disable = logging.root.manager.disable
4187 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004188 self.assertEqual(old_disable, 0)
4189 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004190
4191 logging.disable(83)
4192 self.assertEqual(logging.root.manager.disable, 83)
4193
Vinay Sajipd489ac92016-12-31 11:40:11 +00004194 # test the default value introduced in 3.7
4195 # (Issue #28524)
4196 logging.disable()
4197 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4198
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004199 def _test_log(self, method, level=None):
4200 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004201 support.patch(self, logging, 'basicConfig',
4202 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004203
4204 recording = RecordingHandler()
4205 logging.root.addHandler(recording)
4206
4207 log_method = getattr(logging, method)
4208 if level is not None:
4209 log_method(level, "test me: %r", recording)
4210 else:
4211 log_method("test me: %r", recording)
4212
4213 self.assertEqual(len(recording.records), 1)
4214 record = recording.records[0]
4215 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4216
4217 expected_level = level if level is not None else getattr(logging, method.upper())
4218 self.assertEqual(record.levelno, expected_level)
4219
4220 # basicConfig was not called!
4221 self.assertEqual(called, [])
4222
4223 def test_log(self):
4224 self._test_log('log', logging.ERROR)
4225
4226 def test_debug(self):
4227 self._test_log('debug')
4228
4229 def test_info(self):
4230 self._test_log('info')
4231
4232 def test_warning(self):
4233 self._test_log('warning')
4234
4235 def test_error(self):
4236 self._test_log('error')
4237
4238 def test_critical(self):
4239 self._test_log('critical')
4240
4241 def test_set_logger_class(self):
4242 self.assertRaises(TypeError, logging.setLoggerClass, object)
4243
4244 class MyLogger(logging.Logger):
4245 pass
4246
4247 logging.setLoggerClass(MyLogger)
4248 self.assertEqual(logging.getLoggerClass(), MyLogger)
4249
4250 logging.setLoggerClass(logging.Logger)
4251 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4252
Vinay Sajip01500012019-06-19 11:46:53 +01004253 def test_subclass_logger_cache(self):
4254 # bpo-37258
4255 message = []
4256
4257 class MyLogger(logging.getLoggerClass()):
4258 def __init__(self, name='MyLogger', level=logging.NOTSET):
4259 super().__init__(name, level)
4260 message.append('initialized')
4261
4262 logging.setLoggerClass(MyLogger)
4263 logger = logging.getLogger('just_some_logger')
4264 self.assertEqual(message, ['initialized'])
4265 stream = io.StringIO()
4266 h = logging.StreamHandler(stream)
4267 logger.addHandler(h)
4268 try:
4269 logger.setLevel(logging.DEBUG)
4270 logger.debug("hello")
4271 self.assertEqual(stream.getvalue().strip(), "hello")
4272
4273 stream.truncate(0)
4274 stream.seek(0)
4275
4276 logger.setLevel(logging.INFO)
4277 logger.debug("hello")
4278 self.assertEqual(stream.getvalue(), "")
4279 finally:
4280 logger.removeHandler(h)
4281 h.close()
4282 logging.setLoggerClass(logging.Logger)
4283
Antoine Pitrou712cb732013-12-21 15:51:54 +01004284 def test_logging_at_shutdown(self):
4285 # Issue #20037
4286 code = """if 1:
4287 import logging
4288
4289 class A:
4290 def __del__(self):
4291 try:
4292 raise ValueError("some error")
4293 except Exception:
4294 logging.exception("exception in __del__")
4295
4296 a = A()"""
4297 rc, out, err = assert_python_ok("-c", code)
4298 err = err.decode()
4299 self.assertIn("exception in __del__", err)
4300 self.assertIn("ValueError: some error", err)
4301
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004302 def test_recursion_error(self):
4303 # Issue 36272
4304 code = """if 1:
4305 import logging
4306
4307 def rec():
4308 logging.error("foo")
4309 rec()
4310
4311 rec()"""
4312 rc, out, err = assert_python_failure("-c", code)
4313 err = err.decode()
4314 self.assertNotIn("Cannot recover from stack overflow.", err)
4315 self.assertEqual(rc, 1)
4316
Antoine Pitrou712cb732013-12-21 15:51:54 +01004317
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004318class LogRecordTest(BaseTest):
4319 def test_str_rep(self):
4320 r = logging.makeLogRecord({})
4321 s = str(r)
4322 self.assertTrue(s.startswith('<LogRecord: '))
4323 self.assertTrue(s.endswith('>'))
4324
4325 def test_dict_arg(self):
4326 h = RecordingHandler()
4327 r = logging.getLogger()
4328 r.addHandler(h)
4329 d = {'less' : 'more' }
4330 logging.warning('less is %(less)s', d)
4331 self.assertIs(h.records[0].args, d)
4332 self.assertEqual(h.records[0].message, 'less is more')
4333 r.removeHandler(h)
4334 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004335
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004336 def test_multiprocessing(self):
4337 r = logging.makeLogRecord({})
4338 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004339 try:
4340 import multiprocessing as mp
4341 r = logging.makeLogRecord({})
4342 self.assertEqual(r.processName, mp.current_process().name)
4343 except ImportError:
4344 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004345
4346 def test_optional(self):
4347 r = logging.makeLogRecord({})
4348 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004349 NOT_NONE(r.thread)
4350 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004351 NOT_NONE(r.process)
4352 NOT_NONE(r.processName)
4353 log_threads = logging.logThreads
4354 log_processes = logging.logProcesses
4355 log_multiprocessing = logging.logMultiprocessing
4356 try:
4357 logging.logThreads = False
4358 logging.logProcesses = False
4359 logging.logMultiprocessing = False
4360 r = logging.makeLogRecord({})
4361 NONE = self.assertIsNone
4362 NONE(r.thread)
4363 NONE(r.threadName)
4364 NONE(r.process)
4365 NONE(r.processName)
4366 finally:
4367 logging.logThreads = log_threads
4368 logging.logProcesses = log_processes
4369 logging.logMultiprocessing = log_multiprocessing
4370
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004371class BasicConfigTest(unittest.TestCase):
4372
Vinay Sajip95bf5042011-04-20 11:50:56 +01004373 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004374
4375 def setUp(self):
4376 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004377 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004378 self.saved_handlers = logging._handlers.copy()
4379 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004380 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004381 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004382 logging.root.handlers = []
4383
4384 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004385 for h in logging.root.handlers[:]:
4386 logging.root.removeHandler(h)
4387 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004388 super(BasicConfigTest, self).tearDown()
4389
Vinay Sajip3def7e02011-04-20 10:58:06 +01004390 def cleanup(self):
4391 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004392 logging._handlers.clear()
4393 logging._handlers.update(self.saved_handlers)
4394 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004395 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004396
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004397 def test_no_kwargs(self):
4398 logging.basicConfig()
4399
4400 # handler defaults to a StreamHandler to sys.stderr
4401 self.assertEqual(len(logging.root.handlers), 1)
4402 handler = logging.root.handlers[0]
4403 self.assertIsInstance(handler, logging.StreamHandler)
4404 self.assertEqual(handler.stream, sys.stderr)
4405
4406 formatter = handler.formatter
4407 # format defaults to logging.BASIC_FORMAT
4408 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4409 # datefmt defaults to None
4410 self.assertIsNone(formatter.datefmt)
4411 # style defaults to %
4412 self.assertIsInstance(formatter._style, logging.PercentStyle)
4413
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004414 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004415 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004416
Vinay Sajip1fd12022014-01-13 21:59:56 +00004417 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004418 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004419 logging.basicConfig(stream=sys.stdout, style="{")
4420 logging.error("Log an error")
4421 sys.stdout.seek(0)
4422 self.assertEqual(output.getvalue().strip(),
4423 "ERROR:root:Log an error")
4424
4425 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004426 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004427 logging.basicConfig(stream=sys.stdout, style="$")
4428 logging.error("Log an error")
4429 sys.stdout.seek(0)
4430 self.assertEqual(output.getvalue().strip(),
4431 "ERROR:root:Log an error")
4432
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004433 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004434
4435 def cleanup(h1, h2, fn):
4436 h1.close()
4437 h2.close()
4438 os.remove(fn)
4439
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004440 logging.basicConfig(filename='test.log')
4441
4442 self.assertEqual(len(logging.root.handlers), 1)
4443 handler = logging.root.handlers[0]
4444 self.assertIsInstance(handler, logging.FileHandler)
4445
4446 expected = logging.FileHandler('test.log', 'a')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004447 self.assertEqual(handler.stream.mode, expected.stream.mode)
4448 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004449 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004450
4451 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004452
4453 def cleanup(h1, h2, fn):
4454 h1.close()
4455 h2.close()
4456 os.remove(fn)
4457
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004458 logging.basicConfig(filename='test.log', filemode='wb')
4459
4460 handler = logging.root.handlers[0]
4461 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004462 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004463 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004464
4465 def test_stream(self):
4466 stream = io.StringIO()
4467 self.addCleanup(stream.close)
4468 logging.basicConfig(stream=stream)
4469
4470 self.assertEqual(len(logging.root.handlers), 1)
4471 handler = logging.root.handlers[0]
4472 self.assertIsInstance(handler, logging.StreamHandler)
4473 self.assertEqual(handler.stream, stream)
4474
4475 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004476 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004477
4478 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004479 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004480
4481 def test_datefmt(self):
4482 logging.basicConfig(datefmt='bar')
4483
4484 formatter = logging.root.handlers[0].formatter
4485 self.assertEqual(formatter.datefmt, 'bar')
4486
4487 def test_style(self):
4488 logging.basicConfig(style='$')
4489
4490 formatter = logging.root.handlers[0].formatter
4491 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4492
4493 def test_level(self):
4494 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004495 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004496
4497 logging.basicConfig(level=57)
4498 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004499 # Test that second call has no effect
4500 logging.basicConfig(level=58)
4501 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004502
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004503 def test_incompatible(self):
4504 assertRaises = self.assertRaises
4505 handlers = [logging.StreamHandler()]
4506 stream = sys.stderr
4507 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004508 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004509 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004510 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004511 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004512 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004513 # Issue 23207: test for invalid kwargs
4514 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4515 # Should pop both filename and filemode even if filename is None
4516 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004517
4518 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004519 handlers = [
4520 logging.StreamHandler(),
4521 logging.StreamHandler(sys.stdout),
4522 logging.StreamHandler(),
4523 ]
4524 f = logging.Formatter()
4525 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004526 logging.basicConfig(handlers=handlers)
4527 self.assertIs(handlers[0], logging.root.handlers[0])
4528 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004529 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004530 self.assertIsNotNone(handlers[0].formatter)
4531 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004532 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004533 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4534
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004535 def test_force(self):
4536 old_string_io = io.StringIO()
4537 new_string_io = io.StringIO()
4538 old_handlers = [logging.StreamHandler(old_string_io)]
4539 new_handlers = [logging.StreamHandler(new_string_io)]
4540 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4541 logging.warning('warn')
4542 logging.info('info')
4543 logging.debug('debug')
4544 self.assertEqual(len(logging.root.handlers), 1)
4545 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4546 force=True)
4547 logging.warning('warn')
4548 logging.info('info')
4549 logging.debug('debug')
4550 self.assertEqual(len(logging.root.handlers), 1)
4551 self.assertEqual(old_string_io.getvalue().strip(),
4552 'WARNING:root:warn')
4553 self.assertEqual(new_string_io.getvalue().strip(),
4554 'WARNING:root:warn\nINFO:root:info')
4555
Vinay Sajipca7b5042019-06-17 17:40:52 +01004556 def test_encoding(self):
4557 try:
4558 encoding = 'utf-8'
4559 logging.basicConfig(filename='test.log', encoding=encoding,
4560 errors='strict',
4561 format='%(message)s', level=logging.DEBUG)
4562
4563 self.assertEqual(len(logging.root.handlers), 1)
4564 handler = logging.root.handlers[0]
4565 self.assertIsInstance(handler, logging.FileHandler)
4566 self.assertEqual(handler.encoding, encoding)
4567 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4568 finally:
4569 handler.close()
4570 with open('test.log', encoding='utf-8') as f:
4571 data = f.read().strip()
4572 os.remove('test.log')
4573 self.assertEqual(data,
4574 'The Øresund Bridge joins Copenhagen to Malmö')
4575
4576 def test_encoding_errors(self):
4577 try:
4578 encoding = 'ascii'
4579 logging.basicConfig(filename='test.log', encoding=encoding,
4580 errors='ignore',
4581 format='%(message)s', level=logging.DEBUG)
4582
4583 self.assertEqual(len(logging.root.handlers), 1)
4584 handler = logging.root.handlers[0]
4585 self.assertIsInstance(handler, logging.FileHandler)
4586 self.assertEqual(handler.encoding, encoding)
4587 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4588 finally:
4589 handler.close()
4590 with open('test.log', encoding='utf-8') as f:
4591 data = f.read().strip()
4592 os.remove('test.log')
4593 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4594
4595 def test_encoding_errors_default(self):
4596 try:
4597 encoding = 'ascii'
4598 logging.basicConfig(filename='test.log', encoding=encoding,
4599 format='%(message)s', level=logging.DEBUG)
4600
4601 self.assertEqual(len(logging.root.handlers), 1)
4602 handler = logging.root.handlers[0]
4603 self.assertIsInstance(handler, logging.FileHandler)
4604 self.assertEqual(handler.encoding, encoding)
4605 self.assertEqual(handler.errors, 'backslashreplace')
4606 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4607 finally:
4608 handler.close()
4609 with open('test.log', encoding='utf-8') as f:
4610 data = f.read().strip()
4611 os.remove('test.log')
4612 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4613 r'Bridge joins Copenhagen to Malm\xf6')
4614
4615 def test_encoding_errors_none(self):
4616 # Specifying None should behave as 'strict'
4617 try:
4618 encoding = 'ascii'
4619 logging.basicConfig(filename='test.log', encoding=encoding,
4620 errors=None,
4621 format='%(message)s', level=logging.DEBUG)
4622
4623 self.assertEqual(len(logging.root.handlers), 1)
4624 handler = logging.root.handlers[0]
4625 self.assertIsInstance(handler, logging.FileHandler)
4626 self.assertEqual(handler.encoding, encoding)
4627 self.assertIsNone(handler.errors)
4628
4629 message = []
4630
4631 def dummy_handle_error(record):
4632 _, v, _ = sys.exc_info()
4633 message.append(str(v))
4634
4635 handler.handleError = dummy_handle_error
4636 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4637 self.assertTrue(message)
4638 self.assertIn("'ascii' codec can't encode "
4639 "character '\\xd8' in position 4:", message[0])
4640 finally:
4641 handler.close()
4642 with open('test.log', encoding='utf-8') as f:
4643 data = f.read().strip()
4644 os.remove('test.log')
4645 # didn't write anything due to the encoding error
4646 self.assertEqual(data, r'')
4647
4648
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004649 def _test_log(self, method, level=None):
4650 # logging.root has no handlers so basicConfig should be called
4651 called = []
4652
4653 old_basic_config = logging.basicConfig
4654 def my_basic_config(*a, **kw):
4655 old_basic_config()
4656 old_level = logging.root.level
4657 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004658 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004659 called.append((a, kw))
4660
Vinay Sajip1feedb42014-05-31 08:19:12 +01004661 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004662
4663 log_method = getattr(logging, method)
4664 if level is not None:
4665 log_method(level, "test me")
4666 else:
4667 log_method("test me")
4668
4669 # basicConfig was called with no arguments
4670 self.assertEqual(called, [((), {})])
4671
4672 def test_log(self):
4673 self._test_log('log', logging.WARNING)
4674
4675 def test_debug(self):
4676 self._test_log('debug')
4677
4678 def test_info(self):
4679 self._test_log('info')
4680
4681 def test_warning(self):
4682 self._test_log('warning')
4683
4684 def test_error(self):
4685 self._test_log('error')
4686
4687 def test_critical(self):
4688 self._test_log('critical')
4689
4690
4691class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004692 def setUp(self):
4693 super(LoggerAdapterTest, self).setUp()
4694 old_handler_list = logging._handlerList[:]
4695
4696 self.recording = RecordingHandler()
4697 self.logger = logging.root
4698 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004699 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004700 self.addCleanup(self.recording.close)
4701
4702 def cleanup():
4703 logging._handlerList[:] = old_handler_list
4704
4705 self.addCleanup(cleanup)
4706 self.addCleanup(logging.shutdown)
4707 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4708
4709 def test_exception(self):
4710 msg = 'testing exception: %r'
4711 exc = None
4712 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004713 1 / 0
4714 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004715 exc = e
4716 self.adapter.exception(msg, self.recording)
4717
4718 self.assertEqual(len(self.recording.records), 1)
4719 record = self.recording.records[0]
4720 self.assertEqual(record.levelno, logging.ERROR)
4721 self.assertEqual(record.msg, msg)
4722 self.assertEqual(record.args, (self.recording,))
4723 self.assertEqual(record.exc_info,
4724 (exc.__class__, exc, exc.__traceback__))
4725
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004726 def test_exception_excinfo(self):
4727 try:
4728 1 / 0
4729 except ZeroDivisionError as e:
4730 exc = e
4731
4732 self.adapter.exception('exc_info test', exc_info=exc)
4733
4734 self.assertEqual(len(self.recording.records), 1)
4735 record = self.recording.records[0]
4736 self.assertEqual(record.exc_info,
4737 (exc.__class__, exc, exc.__traceback__))
4738
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004739 def test_critical(self):
4740 msg = 'critical test! %r'
4741 self.adapter.critical(msg, self.recording)
4742
4743 self.assertEqual(len(self.recording.records), 1)
4744 record = self.recording.records[0]
4745 self.assertEqual(record.levelno, logging.CRITICAL)
4746 self.assertEqual(record.msg, msg)
4747 self.assertEqual(record.args, (self.recording,))
4748
4749 def test_is_enabled_for(self):
4750 old_disable = self.adapter.logger.manager.disable
4751 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004752 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4753 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004754 self.assertFalse(self.adapter.isEnabledFor(32))
4755
4756 def test_has_handlers(self):
4757 self.assertTrue(self.adapter.hasHandlers())
4758
4759 for handler in self.logger.handlers:
4760 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004761
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004762 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004763 self.assertFalse(self.adapter.hasHandlers())
4764
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004765 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004766 class Adapter(logging.LoggerAdapter):
4767 prefix = 'Adapter'
4768
4769 def process(self, msg, kwargs):
4770 return f"{self.prefix} {msg}", kwargs
4771
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004772 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004773 adapter = Adapter(logger=self.logger, extra=None)
4774 adapter_adapter = Adapter(logger=adapter, extra=None)
4775 adapter_adapter.prefix = 'AdapterAdapter'
4776 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004777 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004778 self.assertEqual(len(self.recording.records), 1)
4779 record = self.recording.records[0]
4780 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004781 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004782 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004783 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004784 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004785 self.assertIs(self.logger.manager, orig_manager)
4786 temp_manager = object()
4787 try:
4788 adapter_adapter.manager = temp_manager
4789 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004790 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004791 self.assertIs(self.logger.manager, temp_manager)
4792 finally:
4793 adapter_adapter.manager = orig_manager
4794 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004795 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004796 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004797
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004798
4799class LoggerTest(BaseTest):
4800
4801 def setUp(self):
4802 super(LoggerTest, self).setUp()
4803 self.recording = RecordingHandler()
4804 self.logger = logging.Logger(name='blah')
4805 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004806 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004807 self.addCleanup(self.recording.close)
4808 self.addCleanup(logging.shutdown)
4809
4810 def test_set_invalid_level(self):
4811 self.assertRaises(TypeError, self.logger.setLevel, object())
4812
4813 def test_exception(self):
4814 msg = 'testing exception: %r'
4815 exc = None
4816 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004817 1 / 0
4818 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004819 exc = e
4820 self.logger.exception(msg, self.recording)
4821
4822 self.assertEqual(len(self.recording.records), 1)
4823 record = self.recording.records[0]
4824 self.assertEqual(record.levelno, logging.ERROR)
4825 self.assertEqual(record.msg, msg)
4826 self.assertEqual(record.args, (self.recording,))
4827 self.assertEqual(record.exc_info,
4828 (exc.__class__, exc, exc.__traceback__))
4829
4830 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004831 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004832 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004833
4834 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004835 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004836 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004837
4838 def test_find_caller_with_stack_info(self):
4839 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004840 support.patch(self, logging.traceback, 'print_stack',
4841 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004842
4843 self.logger.findCaller(stack_info=True)
4844
4845 self.assertEqual(len(called), 1)
4846 self.assertEqual('Stack (most recent call last):\n', called[0])
4847
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004848 def test_find_caller_with_stacklevel(self):
4849 the_level = 1
4850
4851 def innermost():
4852 self.logger.warning('test', stacklevel=the_level)
4853
4854 def inner():
4855 innermost()
4856
4857 def outer():
4858 inner()
4859
4860 records = self.recording.records
4861 outer()
4862 self.assertEqual(records[-1].funcName, 'innermost')
4863 lineno = records[-1].lineno
4864 the_level += 1
4865 outer()
4866 self.assertEqual(records[-1].funcName, 'inner')
4867 self.assertGreater(records[-1].lineno, lineno)
4868 lineno = records[-1].lineno
4869 the_level += 1
4870 outer()
4871 self.assertEqual(records[-1].funcName, 'outer')
4872 self.assertGreater(records[-1].lineno, lineno)
4873 lineno = records[-1].lineno
4874 the_level += 1
4875 outer()
4876 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
4877 self.assertGreater(records[-1].lineno, lineno)
4878
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004879 def test_make_record_with_extra_overwrite(self):
4880 name = 'my record'
4881 level = 13
4882 fn = lno = msg = args = exc_info = func = sinfo = None
4883 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
4884 exc_info, func, sinfo)
4885
4886 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
4887 extra = {key: 'some value'}
4888 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
4889 fn, lno, msg, args, exc_info,
4890 extra=extra, sinfo=sinfo)
4891
4892 def test_make_record_with_extra_no_overwrite(self):
4893 name = 'my record'
4894 level = 13
4895 fn = lno = msg = args = exc_info = func = sinfo = None
4896 extra = {'valid_key': 'some value'}
4897 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
4898 exc_info, extra=extra, sinfo=sinfo)
4899 self.assertIn('valid_key', result.__dict__)
4900
4901 def test_has_handlers(self):
4902 self.assertTrue(self.logger.hasHandlers())
4903
4904 for handler in self.logger.handlers:
4905 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004906 self.assertFalse(self.logger.hasHandlers())
4907
4908 def test_has_handlers_no_propagate(self):
4909 child_logger = logging.getLogger('blah.child')
4910 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004911 self.assertFalse(child_logger.hasHandlers())
4912
4913 def test_is_enabled_for(self):
4914 old_disable = self.logger.manager.disable
4915 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004916 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004917 self.assertFalse(self.logger.isEnabledFor(22))
4918
Timo Furrer6e3ca642018-06-01 09:29:46 +02004919 def test_is_enabled_for_disabled_logger(self):
4920 old_disabled = self.logger.disabled
4921 old_disable = self.logger.manager.disable
4922
4923 self.logger.disabled = True
4924 self.logger.manager.disable = 21
4925
4926 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
4927 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
4928
4929 self.assertFalse(self.logger.isEnabledFor(22))
4930
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004931 def test_root_logger_aliases(self):
4932 root = logging.getLogger()
4933 self.assertIs(root, logging.root)
4934 self.assertIs(root, logging.getLogger(None))
4935 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01004936 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004937 self.assertIs(root, logging.getLogger('foo').root)
4938 self.assertIs(root, logging.getLogger('foo.bar').root)
4939 self.assertIs(root, logging.getLogger('foo').parent)
4940
4941 self.assertIsNot(root, logging.getLogger('\0'))
4942 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
4943
4944 def test_invalid_names(self):
4945 self.assertRaises(TypeError, logging.getLogger, any)
4946 self.assertRaises(TypeError, logging.getLogger, b'foo')
4947
Vinay Sajip6260d9f2017-06-06 16:34:29 +01004948 def test_pickling(self):
4949 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
4950 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
4951 logger = logging.getLogger(name)
4952 s = pickle.dumps(logger, proto)
4953 unpickled = pickle.loads(s)
4954 self.assertIs(unpickled, logger)
4955
Avram Lubkin78c18a92017-07-30 05:36:33 -04004956 def test_caching(self):
4957 root = self.root_logger
4958 logger1 = logging.getLogger("abc")
4959 logger2 = logging.getLogger("abc.def")
4960
4961 # Set root logger level and ensure cache is empty
4962 root.setLevel(logging.ERROR)
4963 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
4964 self.assertEqual(logger2._cache, {})
4965
4966 # Ensure cache is populated and calls are consistent
4967 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4968 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
4969 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
4970 self.assertEqual(root._cache, {})
4971 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4972
4973 # Ensure root cache gets populated
4974 self.assertEqual(root._cache, {})
4975 self.assertTrue(root.isEnabledFor(logging.ERROR))
4976 self.assertEqual(root._cache, {logging.ERROR: True})
4977
4978 # Set parent logger level and ensure caches are emptied
4979 logger1.setLevel(logging.CRITICAL)
4980 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4981 self.assertEqual(logger2._cache, {})
4982
4983 # Ensure logger2 uses parent logger's effective level
4984 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4985
4986 # Set level to NOTSET and ensure caches are empty
4987 logger2.setLevel(logging.NOTSET)
4988 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4989 self.assertEqual(logger2._cache, {})
4990 self.assertEqual(logger1._cache, {})
4991 self.assertEqual(root._cache, {})
4992
4993 # Verify logger2 follows parent and not root
4994 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4995 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
4996 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
4997 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
4998 self.assertTrue(root.isEnabledFor(logging.ERROR))
4999
5000 # Disable logging in manager and ensure caches are clear
5001 logging.disable()
5002 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5003 self.assertEqual(logger2._cache, {})
5004 self.assertEqual(logger1._cache, {})
5005 self.assertEqual(root._cache, {})
5006
5007 # Ensure no loggers are enabled
5008 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5009 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5010 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5011
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005012
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005013class BaseFileTest(BaseTest):
5014 "Base class for handler tests that write log files"
5015
5016 def setUp(self):
5017 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005018 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5019 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005020 self.rmfiles = []
5021
5022 def tearDown(self):
5023 for fn in self.rmfiles:
5024 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005025 if os.path.exists(self.fn):
5026 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005027 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005028
5029 def assertLogFile(self, filename):
5030 "Assert a log file is there and register it for deletion"
5031 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005032 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005033 self.rmfiles.append(filename)
5034
5035
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005036class FileHandlerTest(BaseFileTest):
5037 def test_delay(self):
5038 os.unlink(self.fn)
5039 fh = logging.FileHandler(self.fn, delay=True)
5040 self.assertIsNone(fh.stream)
5041 self.assertFalse(os.path.exists(self.fn))
5042 fh.handle(logging.makeLogRecord({}))
5043 self.assertIsNotNone(fh.stream)
5044 self.assertTrue(os.path.exists(self.fn))
5045 fh.close()
5046
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005047class RotatingFileHandlerTest(BaseFileTest):
5048 def next_rec(self):
5049 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5050 self.next_message(), None, None, None)
5051
5052 def test_should_not_rollover(self):
5053 # If maxbytes is zero rollover never occurs
5054 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
5055 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005056 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005057
5058 def test_should_rollover(self):
5059 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
5060 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005061 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005062
5063 def test_file_created(self):
5064 # checks that the file is created and assumes it was created
5065 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005066 rh = logging.handlers.RotatingFileHandler(self.fn)
5067 rh.emit(self.next_rec())
5068 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005069 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005070
5071 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005072 def namer(name):
5073 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005074 rh = logging.handlers.RotatingFileHandler(
5075 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005076 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005077 rh.emit(self.next_rec())
5078 self.assertLogFile(self.fn)
5079 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005080 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005081 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005082 self.assertLogFile(namer(self.fn + ".2"))
5083 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5084 rh.close()
5085
l0rb519cb872019-11-06 22:21:40 +01005086 def test_namer_rotator_inheritance(self):
5087 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5088 def namer(self, name):
5089 return name + ".test"
5090
5091 def rotator(self, source, dest):
5092 if os.path.exists(source):
5093 os.rename(source, dest + ".rotated")
5094
5095 rh = HandlerWithNamerAndRotator(
5096 self.fn, backupCount=2, maxBytes=1)
5097 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5098 rh.emit(self.next_rec())
5099 self.assertLogFile(self.fn)
5100 rh.emit(self.next_rec())
5101 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5102 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5103 rh.close()
5104
Hai Shia3ec3ad2020-05-19 06:02:57 +08005105 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005106 def test_rotator(self):
5107 def namer(name):
5108 return name + ".gz"
5109
5110 def rotator(source, dest):
5111 with open(source, "rb") as sf:
5112 data = sf.read()
5113 compressed = zlib.compress(data, 9)
5114 with open(dest, "wb") as df:
5115 df.write(compressed)
5116 os.remove(source)
5117
5118 rh = logging.handlers.RotatingFileHandler(
5119 self.fn, backupCount=2, maxBytes=1)
5120 rh.rotator = rotator
5121 rh.namer = namer
5122 m1 = self.next_rec()
5123 rh.emit(m1)
5124 self.assertLogFile(self.fn)
5125 m2 = self.next_rec()
5126 rh.emit(m2)
5127 fn = namer(self.fn + ".1")
5128 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005129 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005130 with open(fn, "rb") as f:
5131 compressed = f.read()
5132 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005133 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005134 rh.emit(self.next_rec())
5135 fn = namer(self.fn + ".2")
5136 self.assertLogFile(fn)
5137 with open(fn, "rb") as f:
5138 compressed = f.read()
5139 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005140 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005141 rh.emit(self.next_rec())
5142 fn = namer(self.fn + ".2")
5143 with open(fn, "rb") as f:
5144 compressed = f.read()
5145 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005146 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005147 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005148 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005149
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005150class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005151 # other test methods added below
5152 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01005153 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
5154 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005155 fmt = logging.Formatter('%(asctime)s %(message)s')
5156 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005157 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005158 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005159 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005160 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005161 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005162 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005163 fh.close()
5164 # At this point, we should have a recent rotated file which we
5165 # can test for the existence of. However, in practice, on some
5166 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005167 # in time to go to look for the log file. So, we go back a fair
5168 # bit, and stop as soon as we see a rotated file. In theory this
5169 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005170 found = False
5171 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005172 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005173 for secs in range(GO_BACK):
5174 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005175 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5176 found = os.path.exists(fn)
5177 if found:
5178 self.rmfiles.append(fn)
5179 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005180 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5181 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005182 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005183 dn, fn = os.path.split(self.fn)
5184 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005185 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5186 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005187 for f in files:
5188 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005189 path = os.path.join(dn, f)
5190 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005191 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005192 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005193
Vinay Sajip0372e102011-05-05 12:59:14 +01005194 def test_invalid(self):
5195 assertRaises = self.assertRaises
5196 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005197 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005198 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005199 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005200 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005201 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005202
Vinay Sajipa7130792013-04-12 17:04:23 +01005203 def test_compute_rollover_daily_attime(self):
5204 currentTime = 0
5205 atTime = datetime.time(12, 0, 0)
5206 rh = logging.handlers.TimedRotatingFileHandler(
5207 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
5208 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005209 try:
5210 actual = rh.computeRollover(currentTime)
5211 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005212
Vinay Sajipd86ac962013-04-14 12:20:46 +01005213 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5214 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5215 finally:
5216 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005217
Vinay Sajip10e8c492013-05-18 10:19:54 -07005218 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005219 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005220 currentTime = int(time.time())
5221 today = currentTime - currentTime % 86400
5222
Vinay Sajipa7130792013-04-12 17:04:23 +01005223 atTime = datetime.time(12, 0, 0)
5224
Vinay Sajip10e8c492013-05-18 10:19:54 -07005225 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005226 for day in range(7):
5227 rh = logging.handlers.TimedRotatingFileHandler(
5228 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
5229 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005230 try:
5231 if wday > day:
5232 # The rollover day has already passed this week, so we
5233 # go over into next week
5234 expected = (7 - wday + day)
5235 else:
5236 expected = (day - wday)
5237 # At this point expected is in days from now, convert to seconds
5238 expected *= 24 * 60 * 60
5239 # Add in the rollover time
5240 expected += 12 * 60 * 60
5241 # Add in adjustment for today
5242 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005243 actual = rh.computeRollover(today)
5244 if actual != expected:
5245 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005246 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005247 self.assertEqual(actual, expected)
5248 if day == wday:
5249 # goes into following week
5250 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005251 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005252 if actual != expected:
5253 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005254 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005255 self.assertEqual(actual, expected)
5256 finally:
5257 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005258
5259
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005260def secs(**kw):
5261 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5262
5263for when, exp in (('S', 1),
5264 ('M', 60),
5265 ('H', 60 * 60),
5266 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005267 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005268 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005269 ('W0', secs(days=4, hours=24)),
5270 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005271 def test_compute_rollover(self, when=when, exp=exp):
5272 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00005273 self.fn, when=when, interval=1, backupCount=0, utc=True)
5274 currentTime = 0.0
5275 actual = rh.computeRollover(currentTime)
5276 if exp != actual:
5277 # Failures occur on some systems for MIDNIGHT and W0.
5278 # Print detailed calculation for MIDNIGHT so we can try to see
5279 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005280 if when == 'MIDNIGHT':
5281 try:
5282 if rh.utc:
5283 t = time.gmtime(currentTime)
5284 else:
5285 t = time.localtime(currentTime)
5286 currentHour = t[3]
5287 currentMinute = t[4]
5288 currentSecond = t[5]
5289 # r is the number of seconds left between now and midnight
5290 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5291 currentMinute) * 60 +
5292 currentSecond)
5293 result = currentTime + r
5294 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5295 print('currentHour: %s' % currentHour, file=sys.stderr)
5296 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5297 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5298 print('r: %s' % r, file=sys.stderr)
5299 print('result: %s' % result, file=sys.stderr)
5300 except Exception:
5301 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5302 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005303 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005304 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5305
Vinay Sajip60ccd822011-05-09 17:32:09 +01005306
Vinay Sajip223349c2015-10-01 20:37:54 +01005307@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005308class NTEventLogHandlerTest(BaseTest):
5309 def test_basic(self):
5310 logtype = 'Application'
5311 elh = win32evtlog.OpenEventLog(None, logtype)
5312 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005313
5314 try:
5315 h = logging.handlers.NTEventLogHandler('test_logging')
5316 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005317 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005318 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005319 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005320
Vinay Sajip60ccd822011-05-09 17:32:09 +01005321 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5322 h.handle(r)
5323 h.close()
5324 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005325 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005326 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5327 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5328 found = False
5329 GO_BACK = 100
5330 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5331 for e in events:
5332 if e.SourceName != 'test_logging':
5333 continue
5334 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5335 if msg != 'Test Log Message\r\n':
5336 continue
5337 found = True
5338 break
5339 msg = 'Record not found in event log, went back %d records' % GO_BACK
5340 self.assertTrue(found, msg=msg)
5341
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005342
5343class MiscTestCase(unittest.TestCase):
5344 def test__all__(self):
5345 blacklist = {'logThreads', 'logMultiprocessing',
5346 'logProcesses', 'currentframe',
5347 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5348 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger',
Martin Panter94332cb2016-10-20 05:10:44 +00005349 'root', 'threading'}
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005350 support.check__all__(self, logging, blacklist=blacklist)
5351
5352
Christian Heimes180510d2008-03-03 19:15:45 +00005353# Set the locale to the platform-dependent default. I have no idea
5354# why the test does this, but in any case we save the current locale
5355# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005356@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005357def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005358 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005359 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5360 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5361 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5362 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5363 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5364 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5365 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5366 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005367 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005368 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005369 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005370 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005371 ]
5372 if hasattr(logging.handlers, 'QueueListener'):
5373 tests.append(QueueListenerTest)
5374 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005375
Christian Heimes180510d2008-03-03 19:15:45 +00005376if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005377 test_main()