blob: 99e74ebff60875560c3ad6d09a88f347dfe61859 [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
Gregory P. Smith19003842018-09-13 22:08:31 -070039import signal
Christian Heimes180510d2008-03-03 19:15:45 +000040import socket
Christian Heimes180510d2008-03-03 19:15:45 +000041import struct
42import sys
43import tempfile
Rémi Lapeyre65f64b12019-03-15 07:53:34 +010044from test.support.script_helper import assert_python_ok, assert_python_failure
Vinay Sajip1feedb42014-05-31 08:19:12 +010045from test import support
Christian Heimes180510d2008-03-03 19:15:45 +000046import textwrap
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020047import threading
Vinay Sajip37eb3382011-04-26 20:26:41 +010048import time
Christian Heimes180510d2008-03-03 19:15:45 +000049import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000050import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000051import weakref
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020052
53import asyncore
54from http.server import HTTPServer, BaseHTTPRequestHandler
55import smtpd
56from urllib.parse import urlparse, parse_qs
57from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
58 ThreadingTCPServer, StreamRequestHandler)
59
Vinay Sajip60ccd822011-05-09 17:32:09 +010060try:
Vinay Sajip223349c2015-10-01 20:37:54 +010061 import win32evtlog, win32evtlogutil, pywintypes
Vinay Sajip60ccd822011-05-09 17:32:09 +010062except ImportError:
Vinay Sajip223349c2015-10-01 20:37:54 +010063 win32evtlog = win32evtlogutil = pywintypes = None
64
Eric V. Smith851cad72012-03-11 22:46:04 -070065try:
66 import zlib
67except ImportError:
68 pass
Christian Heimes18c66892008-02-17 13:31:39 +000069
Christian Heimes180510d2008-03-03 19:15:45 +000070class BaseTest(unittest.TestCase):
71
72 """Base class for logging tests."""
73
74 log_format = "%(name)s -> %(levelname)s: %(message)s"
Ezio Melotti3ffd29b2012-10-02 19:45:00 +030075 expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +000076 message_num = 0
77
78 def setUp(self):
79 """Setup the default logging stream to an internal StringIO instance,
80 so that we can examine log output as we want."""
Victor Stinner69669602017-08-18 23:47:54 +020081 self._threading_key = support.threading_setup()
82
Christian Heimes180510d2008-03-03 19:15:45 +000083 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000084 logging._acquireLock()
85 try:
Christian Heimes180510d2008-03-03 19:15:45 +000086 self.saved_handlers = logging._handlers.copy()
87 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000088 self.saved_loggers = saved_loggers = logger_dict.copy()
Vinay Sajip3b84eae2013-05-25 03:20:34 -070089 self.saved_name_to_level = logging._nameToLevel.copy()
90 self.saved_level_to_name = logging._levelToName.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000091 self.logger_states = logger_states = {}
92 for name in saved_loggers:
93 logger_states[name] = getattr(saved_loggers[name],
94 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000095 finally:
96 logging._releaseLock()
97
Florent Xicluna5252f9f2011-11-07 19:43:05 +010098 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +000099 self.logger1 = logging.getLogger("\xab\xd7\xbb")
100 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000101
Christian Heimes180510d2008-03-03 19:15:45 +0000102 self.root_logger = logging.getLogger("")
103 self.original_logging_level = self.root_logger.getEffectiveLevel()
104
105 self.stream = io.StringIO()
106 self.root_logger.setLevel(logging.DEBUG)
107 self.root_hdlr = logging.StreamHandler(self.stream)
108 self.root_formatter = logging.Formatter(self.log_format)
109 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000110 if self.logger1.hasHandlers():
111 hlist = self.logger1.handlers + self.root_logger.handlers
112 raise AssertionError('Unexpected handlers: %s' % hlist)
113 if self.logger2.hasHandlers():
114 hlist = self.logger2.handlers + self.root_logger.handlers
115 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000116 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000117 self.assertTrue(self.logger1.hasHandlers())
118 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000119
120 def tearDown(self):
121 """Remove our logging stream, and restore the original logging
122 level."""
123 self.stream.close()
124 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000125 while self.root_logger.handlers:
126 h = self.root_logger.handlers[0]
127 self.root_logger.removeHandler(h)
128 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000129 self.root_logger.setLevel(self.original_logging_level)
130 logging._acquireLock()
131 try:
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700132 logging._levelToName.clear()
133 logging._levelToName.update(self.saved_level_to_name)
134 logging._nameToLevel.clear()
135 logging._nameToLevel.update(self.saved_name_to_level)
Christian Heimes180510d2008-03-03 19:15:45 +0000136 logging._handlers.clear()
137 logging._handlers.update(self.saved_handlers)
138 logging._handlerList[:] = self.saved_handler_list
Avram Lubkin78c18a92017-07-30 05:36:33 -0400139 manager = logging.getLogger().manager
140 manager.disable = 0
141 loggerDict = manager.loggerDict
Christian Heimes180510d2008-03-03 19:15:45 +0000142 loggerDict.clear()
143 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000144 logger_states = self.logger_states
145 for name in self.logger_states:
146 if logger_states[name] is not None:
147 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000148 finally:
149 logging._releaseLock()
150
Victor Stinner69669602017-08-18 23:47:54 +0200151 self.doCleanups()
152 support.threading_cleanup(*self._threading_key)
153
Vinay Sajip4ded5512012-10-02 15:56:16 +0100154 def assert_log_lines(self, expected_values, stream=None, pat=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000155 """Match the collected log lines against the regular expression
156 self.expected_log_pat, and compare the extracted group values to
157 the expected_values list of tuples."""
158 stream = stream or self.stream
Vinay Sajip4ded5512012-10-02 15:56:16 +0100159 pat = re.compile(pat or self.expected_log_pat)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300160 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100161 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000162 for actual, expected in zip(actual_lines, expected_values):
163 match = pat.search(actual)
164 if not match:
165 self.fail("Log line does not match expected pattern:\n" +
166 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000167 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000168 s = stream.read()
169 if s:
170 self.fail("Remaining output at end of log stream:\n" + s)
171
172 def next_message(self):
173 """Generate a message consisting solely of an auto-incrementing
174 integer."""
175 self.message_num += 1
176 return "%d" % self.message_num
177
178
179class BuiltinLevelsTest(BaseTest):
180 """Test builtin levels and their inheritance."""
181
182 def test_flat(self):
Mike53f7a7c2017-12-14 14:04:53 +0300183 # Logging levels in a flat logger namespace.
Christian Heimes180510d2008-03-03 19:15:45 +0000184 m = self.next_message
185
186 ERR = logging.getLogger("ERR")
187 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000188 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000189 INF.setLevel(logging.INFO)
190 DEB = logging.getLogger("DEB")
191 DEB.setLevel(logging.DEBUG)
192
193 # These should log.
194 ERR.log(logging.CRITICAL, m())
195 ERR.error(m())
196
197 INF.log(logging.CRITICAL, m())
198 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100199 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000200 INF.info(m())
201
202 DEB.log(logging.CRITICAL, m())
203 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100204 DEB.warning(m())
205 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000206 DEB.debug(m())
207
208 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100209 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000210 ERR.info(m())
211 ERR.debug(m())
212
213 INF.debug(m())
214
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000215 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000216 ('ERR', 'CRITICAL', '1'),
217 ('ERR', 'ERROR', '2'),
218 ('INF', 'CRITICAL', '3'),
219 ('INF', 'ERROR', '4'),
220 ('INF', 'WARNING', '5'),
221 ('INF', 'INFO', '6'),
222 ('DEB', 'CRITICAL', '7'),
223 ('DEB', 'ERROR', '8'),
224 ('DEB', 'WARNING', '9'),
225 ('DEB', 'INFO', '10'),
226 ('DEB', 'DEBUG', '11'),
227 ])
228
229 def test_nested_explicit(self):
230 # Logging levels in a nested namespace, all explicitly set.
231 m = self.next_message
232
233 INF = logging.getLogger("INF")
234 INF.setLevel(logging.INFO)
235 INF_ERR = logging.getLogger("INF.ERR")
236 INF_ERR.setLevel(logging.ERROR)
237
238 # These should log.
239 INF_ERR.log(logging.CRITICAL, m())
240 INF_ERR.error(m())
241
242 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100243 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000244 INF_ERR.info(m())
245 INF_ERR.debug(m())
246
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000247 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000248 ('INF.ERR', 'CRITICAL', '1'),
249 ('INF.ERR', 'ERROR', '2'),
250 ])
251
252 def test_nested_inherited(self):
Mike53f7a7c2017-12-14 14:04:53 +0300253 # Logging levels in a nested namespace, inherited from parent loggers.
Christian Heimes180510d2008-03-03 19:15:45 +0000254 m = self.next_message
255
256 INF = logging.getLogger("INF")
257 INF.setLevel(logging.INFO)
258 INF_ERR = logging.getLogger("INF.ERR")
259 INF_ERR.setLevel(logging.ERROR)
260 INF_UNDEF = logging.getLogger("INF.UNDEF")
261 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
262 UNDEF = logging.getLogger("UNDEF")
263
264 # These should log.
265 INF_UNDEF.log(logging.CRITICAL, m())
266 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100267 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000268 INF_UNDEF.info(m())
269 INF_ERR_UNDEF.log(logging.CRITICAL, m())
270 INF_ERR_UNDEF.error(m())
271
272 # These should not log.
273 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100274 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000275 INF_ERR_UNDEF.info(m())
276 INF_ERR_UNDEF.debug(m())
277
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000278 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000279 ('INF.UNDEF', 'CRITICAL', '1'),
280 ('INF.UNDEF', 'ERROR', '2'),
281 ('INF.UNDEF', 'WARNING', '3'),
282 ('INF.UNDEF', 'INFO', '4'),
283 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
284 ('INF.ERR.UNDEF', 'ERROR', '6'),
285 ])
286
287 def test_nested_with_virtual_parent(self):
288 # Logging levels when some parent does not exist yet.
289 m = self.next_message
290
291 INF = logging.getLogger("INF")
292 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
293 CHILD = logging.getLogger("INF.BADPARENT")
294 INF.setLevel(logging.INFO)
295
296 # These should log.
297 GRANDCHILD.log(logging.FATAL, m())
298 GRANDCHILD.info(m())
299 CHILD.log(logging.FATAL, m())
300 CHILD.info(m())
301
302 # These should not log.
303 GRANDCHILD.debug(m())
304 CHILD.debug(m())
305
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000306 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000307 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
308 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
309 ('INF.BADPARENT', 'CRITICAL', '3'),
310 ('INF.BADPARENT', 'INFO', '4'),
311 ])
312
Vinay Sajipd1d4fbf2014-09-11 23:06:09 +0100313 def test_regression_22386(self):
314 """See issue #22386 for more information."""
315 self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
316 self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
Christian Heimes180510d2008-03-03 19:15:45 +0000317
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100318 def test_issue27935(self):
319 fatal = logging.getLevelName('FATAL')
320 self.assertEqual(fatal, logging.FATAL)
321
Vinay Sajip924aaae2017-01-11 17:35:36 +0000322 def test_regression_29220(self):
323 """See issue #29220 for more information."""
324 logging.addLevelName(logging.INFO, '')
325 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
326 self.assertEqual(logging.getLevelName(logging.INFO), '')
327 self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
328 self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
329
Christian Heimes180510d2008-03-03 19:15:45 +0000330class BasicFilterTest(BaseTest):
331
332 """Test the bundled Filter class."""
333
334 def test_filter(self):
335 # Only messages satisfying the specified criteria pass through the
336 # filter.
337 filter_ = logging.Filter("spam.eggs")
338 handler = self.root_logger.handlers[0]
339 try:
340 handler.addFilter(filter_)
341 spam = logging.getLogger("spam")
342 spam_eggs = logging.getLogger("spam.eggs")
343 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
344 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
345
346 spam.info(self.next_message())
347 spam_eggs.info(self.next_message()) # Good.
348 spam_eggs_fish.info(self.next_message()) # Good.
349 spam_bakedbeans.info(self.next_message())
350
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000351 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000352 ('spam.eggs', 'INFO', '2'),
353 ('spam.eggs.fish', 'INFO', '3'),
354 ])
355 finally:
356 handler.removeFilter(filter_)
357
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000358 def test_callable_filter(self):
359 # Only messages satisfying the specified criteria pass through the
360 # filter.
361
362 def filterfunc(record):
363 parts = record.name.split('.')
364 prefix = '.'.join(parts[:2])
365 return prefix == 'spam.eggs'
366
367 handler = self.root_logger.handlers[0]
368 try:
369 handler.addFilter(filterfunc)
370 spam = logging.getLogger("spam")
371 spam_eggs = logging.getLogger("spam.eggs")
372 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
373 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
374
375 spam.info(self.next_message())
376 spam_eggs.info(self.next_message()) # Good.
377 spam_eggs_fish.info(self.next_message()) # Good.
378 spam_bakedbeans.info(self.next_message())
379
380 self.assert_log_lines([
381 ('spam.eggs', 'INFO', '2'),
382 ('spam.eggs.fish', 'INFO', '3'),
383 ])
384 finally:
385 handler.removeFilter(filterfunc)
386
Vinay Sajip985ef872011-04-26 19:34:04 +0100387 def test_empty_filter(self):
388 f = logging.Filter()
389 r = logging.makeLogRecord({'name': 'spam.eggs'})
390 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000391
392#
393# First, we define our levels. There can be as many as you want - the only
394# limitations are that they should be integers, the lowest should be > 0 and
395# larger values mean less information being logged. If you need specific
396# level values which do not fit into these limitations, you can use a
397# mapping dictionary to convert between your application levels and the
398# logging system.
399#
400SILENT = 120
401TACITURN = 119
402TERSE = 118
403EFFUSIVE = 117
404SOCIABLE = 116
405VERBOSE = 115
406TALKATIVE = 114
407GARRULOUS = 113
408CHATTERBOX = 112
409BORING = 111
410
411LEVEL_RANGE = range(BORING, SILENT + 1)
412
413#
414# Next, we define names for our levels. You don't need to do this - in which
415# case the system will use "Level n" to denote the text for the level.
416#
417my_logging_levels = {
418 SILENT : 'Silent',
419 TACITURN : 'Taciturn',
420 TERSE : 'Terse',
421 EFFUSIVE : 'Effusive',
422 SOCIABLE : 'Sociable',
423 VERBOSE : 'Verbose',
424 TALKATIVE : 'Talkative',
425 GARRULOUS : 'Garrulous',
426 CHATTERBOX : 'Chatterbox',
427 BORING : 'Boring',
428}
429
430class GarrulousFilter(logging.Filter):
431
432 """A filter which blocks garrulous messages."""
433
434 def filter(self, record):
435 return record.levelno != GARRULOUS
436
437class VerySpecificFilter(logging.Filter):
438
439 """A filter which blocks sociable and taciturn messages."""
440
441 def filter(self, record):
442 return record.levelno not in [SOCIABLE, TACITURN]
443
444
445class CustomLevelsAndFiltersTest(BaseTest):
446
447 """Test various filtering possibilities with custom logging levels."""
448
449 # Skip the logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +0300450 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +0000451
452 def setUp(self):
453 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000454 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000455 logging.addLevelName(k, v)
456
457 def log_at_all_levels(self, logger):
458 for lvl in LEVEL_RANGE:
459 logger.log(lvl, self.next_message())
460
461 def test_logger_filter(self):
462 # Filter at logger level.
463 self.root_logger.setLevel(VERBOSE)
464 # Levels >= 'Verbose' are good.
465 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000466 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000467 ('Verbose', '5'),
468 ('Sociable', '6'),
469 ('Effusive', '7'),
470 ('Terse', '8'),
471 ('Taciturn', '9'),
472 ('Silent', '10'),
473 ])
474
475 def test_handler_filter(self):
476 # Filter at handler level.
477 self.root_logger.handlers[0].setLevel(SOCIABLE)
478 try:
479 # Levels >= 'Sociable' are good.
480 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000481 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000482 ('Sociable', '6'),
483 ('Effusive', '7'),
484 ('Terse', '8'),
485 ('Taciturn', '9'),
486 ('Silent', '10'),
487 ])
488 finally:
489 self.root_logger.handlers[0].setLevel(logging.NOTSET)
490
491 def test_specific_filters(self):
492 # Set a specific filter object on the handler, and then add another
493 # filter object on the logger itself.
494 handler = self.root_logger.handlers[0]
495 specific_filter = None
496 garr = GarrulousFilter()
497 handler.addFilter(garr)
498 try:
499 self.log_at_all_levels(self.root_logger)
500 first_lines = [
501 # Notice how 'Garrulous' is missing
502 ('Boring', '1'),
503 ('Chatterbox', '2'),
504 ('Talkative', '4'),
505 ('Verbose', '5'),
506 ('Sociable', '6'),
507 ('Effusive', '7'),
508 ('Terse', '8'),
509 ('Taciturn', '9'),
510 ('Silent', '10'),
511 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000512 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000513
514 specific_filter = VerySpecificFilter()
515 self.root_logger.addFilter(specific_filter)
516 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000517 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000518 # Not only 'Garrulous' is still missing, but also 'Sociable'
519 # and 'Taciturn'
520 ('Boring', '11'),
521 ('Chatterbox', '12'),
522 ('Talkative', '14'),
523 ('Verbose', '15'),
524 ('Effusive', '17'),
525 ('Terse', '18'),
526 ('Silent', '20'),
527 ])
528 finally:
529 if specific_filter:
530 self.root_logger.removeFilter(specific_filter)
531 handler.removeFilter(garr)
532
533
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100534class HandlerTest(BaseTest):
535 def test_name(self):
536 h = logging.Handler()
537 h.name = 'generic'
538 self.assertEqual(h.name, 'generic')
539 h.name = 'anothergeneric'
540 self.assertEqual(h.name, 'anothergeneric')
541 self.assertRaises(NotImplementedError, h.emit, None)
542
Vinay Sajip5a35b062011-04-27 11:31:14 +0100543 def test_builtin_handlers(self):
544 # We can't actually *use* too many handlers in the tests,
545 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200546 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100547 for existing in (True, False):
548 fd, fn = tempfile.mkstemp()
549 os.close(fd)
550 if not existing:
551 os.unlink(fn)
552 h = logging.handlers.WatchedFileHandler(fn, delay=True)
553 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100554 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100555 self.assertEqual(dev, -1)
556 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100557 r = logging.makeLogRecord({'msg': 'Test'})
558 h.handle(r)
559 # Now remove the file.
560 os.unlink(fn)
561 self.assertFalse(os.path.exists(fn))
562 # The next call should recreate the file.
563 h.handle(r)
564 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100565 else:
566 self.assertEqual(h.dev, -1)
567 self.assertEqual(h.ino, -1)
568 h.close()
569 if existing:
570 os.unlink(fn)
571 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100572 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100573 else:
574 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100575 try:
576 h = logging.handlers.SysLogHandler(sockname)
577 self.assertEqual(h.facility, h.LOG_USER)
578 self.assertTrue(h.unixsocket)
579 h.close()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200580 except OSError: # syslogd might not be available
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100581 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100582 for method in ('GET', 'POST', 'PUT'):
583 if method == 'PUT':
584 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
585 'localhost', '/log', method)
586 else:
587 h = logging.handlers.HTTPHandler('localhost', '/log', method)
588 h.close()
589 h = logging.handlers.BufferingHandler(0)
590 r = logging.makeLogRecord({})
591 self.assertTrue(h.shouldFlush(r))
592 h.close()
593 h = logging.handlers.BufferingHandler(1)
594 self.assertFalse(h.shouldFlush(r))
595 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100596
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100597 def test_path_objects(self):
598 """
599 Test that Path objects are accepted as filename arguments to handlers.
600
601 See Issue #27493.
602 """
603 fd, fn = tempfile.mkstemp()
604 os.close(fd)
605 os.unlink(fn)
606 pfn = pathlib.Path(fn)
607 cases = (
608 (logging.FileHandler, (pfn, 'w')),
609 (logging.handlers.RotatingFileHandler, (pfn, 'a')),
610 (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
611 )
612 if sys.platform in ('linux', 'darwin'):
613 cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
614 for cls, args in cases:
615 h = cls(*args)
616 self.assertTrue(os.path.exists(fn))
617 h.close()
618 os.unlink(fn)
619
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100620 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100621 def test_race(self):
622 # Issue #14632 refers.
623 def remove_loop(fname, tries):
624 for _ in range(tries):
625 try:
626 os.unlink(fname)
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000627 self.deletion_time = time.time()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100628 except OSError:
629 pass
630 time.sleep(0.004 * random.randint(0, 4))
631
Vinay Sajipc94871a2012-04-25 10:51:35 +0100632 del_count = 500
633 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100634
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000635 self.handle_time = None
636 self.deletion_time = None
637
Vinay Sajipa5798de2012-04-24 23:33:33 +0100638 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100639 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
640 os.close(fd)
641 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
642 remover.daemon = True
643 remover.start()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100644 h = logging.handlers.WatchedFileHandler(fn, delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100645 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
646 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100647 try:
648 for _ in range(log_count):
649 time.sleep(0.005)
650 r = logging.makeLogRecord({'msg': 'testing' })
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000651 try:
652 self.handle_time = time.time()
653 h.handle(r)
654 except Exception:
655 print('Deleted at %s, '
656 'opened at %s' % (self.deletion_time,
657 self.handle_time))
658 raise
Vinay Sajipc94871a2012-04-25 10:51:35 +0100659 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100660 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100661 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100662 if os.path.exists(fn):
663 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100664
Gregory P. Smith19003842018-09-13 22:08:31 -0700665 # The implementation relies on os.register_at_fork existing, but we test
666 # based on os.fork existing because that is what users and this test use.
667 # This helps ensure that when fork exists (the important concept) that the
668 # register_at_fork mechanism is also present and used.
669 @unittest.skipIf(not hasattr(os, 'fork'), 'Test requires os.fork().')
670 def test_post_fork_child_no_deadlock(self):
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400671 """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
672 class _OurHandler(logging.Handler):
673 def __init__(self):
674 super().__init__()
675 self.sub_handler = logging.StreamHandler(
676 stream=open('/dev/null', 'wt'))
677
678 def emit(self, record):
679 self.sub_handler.acquire()
680 try:
681 self.sub_handler.emit(record)
682 finally:
683 self.sub_handler.release()
684
685 self.assertEqual(len(logging._handlers), 0)
686 refed_h = _OurHandler()
Xtreak2c105382019-05-13 20:18:52 +0530687 self.addCleanup(refed_h.sub_handler.stream.close)
Gregory P. Smith19003842018-09-13 22:08:31 -0700688 refed_h.name = 'because we need at least one for this test'
689 self.assertGreater(len(logging._handlers), 0)
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400690 self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
691 test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
692 test_logger.addHandler(refed_h)
693 test_logger.setLevel(logging.DEBUG)
Gregory P. Smith19003842018-09-13 22:08:31 -0700694
695 locks_held__ready_to_fork = threading.Event()
696 fork_happened__release_locks_and_end_thread = threading.Event()
697
698 def lock_holder_thread_fn():
699 logging._acquireLock()
700 try:
701 refed_h.acquire()
702 try:
703 # Tell the main thread to do the fork.
704 locks_held__ready_to_fork.set()
705
706 # If the deadlock bug exists, the fork will happen
707 # without dealing with the locks we hold, deadlocking
708 # the child.
709
710 # Wait for a successful fork or an unreasonable amount of
711 # time before releasing our locks. To avoid a timing based
712 # test we'd need communication from os.fork() as to when it
713 # has actually happened. Given this is a regression test
714 # for a fixed issue, potentially less reliably detecting
715 # regression via timing is acceptable for simplicity.
716 # The test will always take at least this long. :(
717 fork_happened__release_locks_and_end_thread.wait(0.5)
718 finally:
719 refed_h.release()
720 finally:
721 logging._releaseLock()
722
723 lock_holder_thread = threading.Thread(
724 target=lock_holder_thread_fn,
725 name='test_post_fork_child_no_deadlock lock holder')
726 lock_holder_thread.start()
727
728 locks_held__ready_to_fork.wait()
729 pid = os.fork()
Victor Stinner278c1e12020-03-31 20:08:12 +0200730 if pid == 0:
731 # Child process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400732 try:
733 test_logger.info(r'Child process did not deadlock. \o/')
734 finally:
735 os._exit(0)
Victor Stinner278c1e12020-03-31 20:08:12 +0200736 else:
737 # Parent process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400738 test_logger.info(r'Parent process returned from fork. \o/')
Gregory P. Smith19003842018-09-13 22:08:31 -0700739 fork_happened__release_locks_and_end_thread.set()
740 lock_holder_thread.join()
Victor Stinner278c1e12020-03-31 20:08:12 +0200741
742 support.wait_process(pid, exitcode=0)
Gregory P. Smith19003842018-09-13 22:08:31 -0700743
Vinay Sajipa5798de2012-04-24 23:33:33 +0100744
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100745class BadStream(object):
746 def write(self, data):
747 raise RuntimeError('deliberate mistake')
748
749class TestStreamHandler(logging.StreamHandler):
750 def handleError(self, record):
751 self.error_record = record
752
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200753class StreamWithIntName(object):
754 level = logging.NOTSET
755 name = 2
756
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100757class StreamHandlerTest(BaseTest):
758 def test_error_handling(self):
759 h = TestStreamHandler(BadStream())
760 r = logging.makeLogRecord({})
761 old_raise = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +0100762
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100763 try:
764 h.handle(r)
765 self.assertIs(h.error_record, r)
Vinay Sajip1feedb42014-05-31 08:19:12 +0100766
Vinay Sajip985ef872011-04-26 19:34:04 +0100767 h = logging.StreamHandler(BadStream())
Vinay Sajip1feedb42014-05-31 08:19:12 +0100768 with support.captured_stderr() as stderr:
769 h.handle(r)
770 msg = '\nRuntimeError: deliberate mistake\n'
771 self.assertIn(msg, stderr.getvalue())
772
Vinay Sajip985ef872011-04-26 19:34:04 +0100773 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +0100774 with support.captured_stderr() as stderr:
775 h.handle(r)
776 self.assertEqual('', stderr.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100777 finally:
778 logging.raiseExceptions = old_raise
779
Vinay Sajip2543f502017-07-30 10:41:45 +0100780 def test_stream_setting(self):
781 """
782 Test setting the handler's stream
783 """
784 h = logging.StreamHandler()
785 stream = io.StringIO()
786 old = h.setStream(stream)
787 self.assertIs(old, sys.stderr)
788 actual = h.setStream(old)
789 self.assertIs(actual, stream)
790 # test that setting to existing value returns None
791 actual = h.setStream(old)
792 self.assertIsNone(actual)
793
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200794 def test_can_represent_stream_with_int_name(self):
795 h = logging.StreamHandler(StreamWithIntName())
796 self.assertEqual(repr(h), '<StreamHandler 2 (NOTSET)>')
797
Vinay Sajip7367d082011-05-02 13:17:27 +0100798# -- The following section could be moved into a server_helper.py module
799# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100800
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200801class TestSMTPServer(smtpd.SMTPServer):
802 """
803 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100804
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200805 :param addr: A (host, port) tuple which the server listens on.
806 You can specify a port value of zero: the server's
807 *port* attribute will hold the actual port number
808 used, which can be used in client connections.
809 :param handler: A callable which will be called to process
810 incoming messages. The handler will be passed
811 the client address tuple, who the message is from,
812 a list of recipients and the message data.
813 :param poll_interval: The interval, in seconds, used in the underlying
814 :func:`select` or :func:`poll` call by
815 :func:`asyncore.loop`.
816 :param sockmap: A dictionary which will be used to hold
817 :class:`asyncore.dispatcher` instances used by
818 :func:`asyncore.loop`. This avoids changing the
819 :mod:`asyncore` module's global state.
820 """
821
822 def __init__(self, addr, handler, poll_interval, sockmap):
823 smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
824 decode_data=True)
825 self.port = self.socket.getsockname()[1]
826 self._handler = handler
827 self._thread = None
828 self.poll_interval = poll_interval
829
830 def process_message(self, peer, mailfrom, rcpttos, data):
831 """
832 Delegates to the handler passed in to the server's constructor.
833
834 Typically, this will be a test case method.
835 :param peer: The client (host, port) tuple.
836 :param mailfrom: The address of the sender.
837 :param rcpttos: The addresses of the recipients.
838 :param data: The message.
839 """
840 self._handler(peer, mailfrom, rcpttos, data)
841
842 def start(self):
843 """
844 Start the server running on a separate daemon thread.
845 """
846 self._thread = t = threading.Thread(target=self.serve_forever,
847 args=(self.poll_interval,))
848 t.setDaemon(True)
849 t.start()
850
851 def serve_forever(self, poll_interval):
852 """
853 Run the :mod:`asyncore` loop until normal termination
854 conditions arise.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100855 :param poll_interval: The interval, in seconds, used in the underlying
856 :func:`select` or :func:`poll` call by
857 :func:`asyncore.loop`.
858 """
Victor Stinner13ff2452018-01-22 18:32:50 +0100859 asyncore.loop(poll_interval, map=self._map)
Vinay Sajipce7c9782011-05-17 07:15:53 +0100860
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100861 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200862 """
863 Stop the thread by closing the server instance.
864 Wait for the server thread to terminate.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200865 """
866 self.close()
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100867 support.join_thread(self._thread)
Victor Stinnerb9b69002017-09-14 14:40:56 -0700868 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200869 asyncore.close_all(map=self._map, ignore_all=True)
Vinay Sajipa463d252011-04-30 21:52:48 +0100870
Vinay Sajip7367d082011-05-02 13:17:27 +0100871
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200872class ControlMixin(object):
873 """
874 This mixin is used to start a server on a separate thread, and
875 shut it down programmatically. Request handling is simplified - instead
876 of needing to derive a suitable RequestHandler subclass, you just
877 provide a callable which will be passed each received request to be
878 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100879
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200880 :param handler: A handler callable which will be called with a
881 single parameter - the request - in order to
882 process the request. This handler is called on the
883 server thread, effectively meaning that requests are
884 processed serially. While not quite Web scale ;-),
885 this should be fine for testing applications.
886 :param poll_interval: The polling interval in seconds.
887 """
888 def __init__(self, handler, poll_interval):
889 self._thread = None
890 self.poll_interval = poll_interval
891 self._handler = handler
892 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100893
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200894 def start(self):
895 """
896 Create a daemon thread to run the server, and start it.
897 """
898 self._thread = t = threading.Thread(target=self.serve_forever,
899 args=(self.poll_interval,))
900 t.setDaemon(True)
901 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100902
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200903 def serve_forever(self, poll_interval):
904 """
905 Run the server. Set the ready flag before entering the
906 service loop.
907 """
908 self.ready.set()
909 super(ControlMixin, self).serve_forever(poll_interval)
910
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100911 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200912 """
913 Tell the server thread to stop, and wait for it to do so.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200914 """
915 self.shutdown()
916 if self._thread is not None:
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100917 support.join_thread(self._thread)
Vinay Sajip7367d082011-05-02 13:17:27 +0100918 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200919 self.server_close()
920 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100921
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200922class TestHTTPServer(ControlMixin, HTTPServer):
923 """
924 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100925
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200926 :param addr: A tuple with the IP address and port to listen on.
927 :param handler: A handler callable which will be called with a
928 single parameter - the request - in order to
929 process the request.
930 :param poll_interval: The polling interval in seconds.
931 :param log: Pass ``True`` to enable log messages.
932 """
933 def __init__(self, addr, handler, poll_interval=0.5,
934 log=False, sslctx=None):
935 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
936 def __getattr__(self, name, default=None):
937 if name.startswith('do_'):
938 return self.process_request
939 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100940
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200941 def process_request(self):
942 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100943
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200944 def log_message(self, format, *args):
945 if log:
946 super(DelegatingHTTPRequestHandler,
947 self).log_message(format, *args)
948 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
949 ControlMixin.__init__(self, handler, poll_interval)
950 self.sslctx = sslctx
Vinay Sajip7367d082011-05-02 13:17:27 +0100951
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200952 def get_request(self):
953 try:
954 sock, addr = self.socket.accept()
955 if self.sslctx:
956 sock = self.sslctx.wrap_socket(sock, server_side=True)
957 except OSError as e:
958 # socket errors are silenced by the caller, print them here
959 sys.stderr.write("Got an error:\n%s\n" % e)
960 raise
961 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100962
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200963class TestTCPServer(ControlMixin, ThreadingTCPServer):
964 """
965 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100966
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200967 :param addr: A tuple with the IP address and port to listen on.
968 :param handler: A handler callable which will be called with a single
969 parameter - the request - in order to process the request.
970 :param poll_interval: The polling interval in seconds.
971 :bind_and_activate: If True (the default), binds the server and starts it
972 listening. If False, you need to call
973 :meth:`server_bind` and :meth:`server_activate` at
974 some later time before calling :meth:`start`, so that
975 the server will set up the socket and listen on it.
976 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100977
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200978 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100979
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200980 def __init__(self, addr, handler, poll_interval=0.5,
981 bind_and_activate=True):
982 class DelegatingTCPRequestHandler(StreamRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +0100983
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200984 def handle(self):
985 self.server._handler(self)
986 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
987 bind_and_activate)
988 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100989
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200990 def server_bind(self):
991 super(TestTCPServer, self).server_bind()
992 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +0100993
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200994class TestUDPServer(ControlMixin, ThreadingUDPServer):
995 """
996 A UDP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100997
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200998 :param addr: A tuple with the IP address and port to listen on.
999 :param handler: A handler callable which will be called with a
1000 single parameter - the request - in order to
1001 process the request.
1002 :param poll_interval: The polling interval for shutdown requests,
1003 in seconds.
1004 :bind_and_activate: If True (the default), binds the server and
1005 starts it listening. If False, you need to
1006 call :meth:`server_bind` and
1007 :meth:`server_activate` at some later time
1008 before calling :meth:`start`, so that the server will
1009 set up the socket and listen on it.
1010 """
1011 def __init__(self, addr, handler, poll_interval=0.5,
1012 bind_and_activate=True):
1013 class DelegatingUDPRequestHandler(DatagramRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +01001014
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001015 def handle(self):
1016 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001017
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001018 def finish(self):
1019 data = self.wfile.getvalue()
1020 if data:
1021 try:
1022 super(DelegatingUDPRequestHandler, self).finish()
1023 except OSError:
1024 if not self.server._closed:
1025 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +01001026
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001027 ThreadingUDPServer.__init__(self, addr,
1028 DelegatingUDPRequestHandler,
1029 bind_and_activate)
1030 ControlMixin.__init__(self, handler, poll_interval)
1031 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +01001032
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001033 def server_bind(self):
1034 super(TestUDPServer, self).server_bind()
1035 self.port = self.socket.getsockname()[1]
Vinay Sajipce7c9782011-05-17 07:15:53 +01001036
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001037 def server_close(self):
1038 super(TestUDPServer, self).server_close()
1039 self._closed = True
Vinay Sajipce7c9782011-05-17 07:15:53 +01001040
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001041if hasattr(socket, "AF_UNIX"):
1042 class TestUnixStreamServer(TestTCPServer):
1043 address_family = socket.AF_UNIX
Vinay Sajipce7c9782011-05-17 07:15:53 +01001044
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001045 class TestUnixDatagramServer(TestUDPServer):
1046 address_family = socket.AF_UNIX
Vinay Sajip5421f352013-09-27 18:18:28 +01001047
Vinay Sajip7367d082011-05-02 13:17:27 +01001048# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +01001049
1050class SMTPHandlerTest(BaseTest):
Victor Stinnerc98b0192019-12-10 21:12:26 +01001051 # bpo-14314, bpo-19665, bpo-34092: don't wait forever
1052 TIMEOUT = support.LONG_TIMEOUT
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02001053
Vinay Sajipa463d252011-04-30 21:52:48 +01001054 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +01001055 sockmap = {}
Ned Deily1e012e62015-01-17 16:57:19 -08001056 server = TestSMTPServer((support.HOST, 0), self.process_message, 0.001,
Vinay Sajip7367d082011-05-02 13:17:27 +01001057 sockmap)
1058 server.start()
Ned Deily1e012e62015-01-17 16:57:19 -08001059 addr = (support.HOST, server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +00001060 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
1061 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001062 self.assertEqual(h.toaddrs, ['you'])
1063 self.messages = []
Vinay Sajip277640a2015-10-17 16:13:10 +01001064 r = logging.makeLogRecord({'msg': 'Hello \u2713'})
Vinay Sajip7367d082011-05-02 13:17:27 +01001065 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +01001066 h.handle(r)
Victor Stinner31b50b82018-07-11 12:35:38 +02001067 self.handled.wait(self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001068 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +00001069 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +01001070 self.assertEqual(len(self.messages), 1)
1071 peer, mailfrom, rcpttos, data = self.messages[0]
1072 self.assertEqual(mailfrom, 'me')
1073 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001074 self.assertIn('\nSubject: Log\n', data)
Vinay Sajip277640a2015-10-17 16:13:10 +01001075 self.assertTrue(data.endswith('\n\nHello \u2713'))
Vinay Sajipa463d252011-04-30 21:52:48 +01001076 h.close()
1077
1078 def process_message(self, *args):
1079 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +01001080 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +01001081
Christian Heimes180510d2008-03-03 19:15:45 +00001082class MemoryHandlerTest(BaseTest):
1083
1084 """Tests for the MemoryHandler."""
1085
1086 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001087 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001088
1089 def setUp(self):
1090 BaseTest.setUp(self)
1091 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001092 self.root_hdlr)
Christian Heimes180510d2008-03-03 19:15:45 +00001093 self.mem_logger = logging.getLogger('mem')
1094 self.mem_logger.propagate = 0
1095 self.mem_logger.addHandler(self.mem_hdlr)
1096
1097 def tearDown(self):
1098 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001099 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001100
1101 def test_flush(self):
1102 # The memory handler flushes to its target handler based on specific
1103 # criteria (message count and message level).
1104 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001105 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001106 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001107 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001108 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001109 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001110 lines = [
1111 ('DEBUG', '1'),
1112 ('INFO', '2'),
1113 ('WARNING', '3'),
1114 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001115 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001116 for n in (4, 14):
1117 for i in range(9):
1118 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001119 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001120 # This will flush because it's the 10th message since the last
1121 # flush.
1122 self.mem_logger.debug(self.next_message())
1123 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001124 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001125
1126 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001127 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001128
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001129 def test_flush_on_close(self):
1130 """
1131 Test that the flush-on-close configuration works as expected.
1132 """
1133 self.mem_logger.debug(self.next_message())
1134 self.assert_log_lines([])
1135 self.mem_logger.info(self.next_message())
1136 self.assert_log_lines([])
1137 self.mem_logger.removeHandler(self.mem_hdlr)
1138 # Default behaviour is to flush on close. Check that it happens.
1139 self.mem_hdlr.close()
1140 lines = [
1141 ('DEBUG', '1'),
1142 ('INFO', '2'),
1143 ]
1144 self.assert_log_lines(lines)
1145 # Now configure for flushing not to be done on close.
1146 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1147 self.root_hdlr,
1148 False)
1149 self.mem_logger.addHandler(self.mem_hdlr)
1150 self.mem_logger.debug(self.next_message())
1151 self.assert_log_lines(lines) # no change
1152 self.mem_logger.info(self.next_message())
1153 self.assert_log_lines(lines) # no change
1154 self.mem_logger.removeHandler(self.mem_hdlr)
1155 self.mem_hdlr.close()
1156 # assert that no new lines have been added
1157 self.assert_log_lines(lines) # no change
1158
Christian Heimes180510d2008-03-03 19:15:45 +00001159
1160class ExceptionFormatter(logging.Formatter):
1161 """A special exception formatter."""
1162 def formatException(self, ei):
1163 return "Got a [%s]" % ei[0].__name__
1164
1165
1166class ConfigFileTest(BaseTest):
1167
1168 """Reading logging config from a .ini-style config file."""
1169
Xtreak087570a2018-07-02 14:27:46 +05301170 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001171 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001172
1173 # config0 is a standard configuration.
1174 config0 = """
1175 [loggers]
1176 keys=root
1177
1178 [handlers]
1179 keys=hand1
1180
1181 [formatters]
1182 keys=form1
1183
1184 [logger_root]
1185 level=WARNING
1186 handlers=hand1
1187
1188 [handler_hand1]
1189 class=StreamHandler
1190 level=NOTSET
1191 formatter=form1
1192 args=(sys.stdout,)
1193
1194 [formatter_form1]
1195 format=%(levelname)s ++ %(message)s
1196 datefmt=
1197 """
1198
1199 # config1 adds a little to the standard configuration.
1200 config1 = """
1201 [loggers]
1202 keys=root,parser
1203
1204 [handlers]
1205 keys=hand1
1206
1207 [formatters]
1208 keys=form1
1209
1210 [logger_root]
1211 level=WARNING
1212 handlers=
1213
1214 [logger_parser]
1215 level=DEBUG
1216 handlers=hand1
1217 propagate=1
1218 qualname=compiler.parser
1219
1220 [handler_hand1]
1221 class=StreamHandler
1222 level=NOTSET
1223 formatter=form1
1224 args=(sys.stdout,)
1225
1226 [formatter_form1]
1227 format=%(levelname)s ++ %(message)s
1228 datefmt=
1229 """
1230
Vinay Sajip3f84b072011-03-07 17:49:33 +00001231 # config1a moves the handler to the root.
1232 config1a = """
1233 [loggers]
1234 keys=root,parser
1235
1236 [handlers]
1237 keys=hand1
1238
1239 [formatters]
1240 keys=form1
1241
1242 [logger_root]
1243 level=WARNING
1244 handlers=hand1
1245
1246 [logger_parser]
1247 level=DEBUG
1248 handlers=
1249 propagate=1
1250 qualname=compiler.parser
1251
1252 [handler_hand1]
1253 class=StreamHandler
1254 level=NOTSET
1255 formatter=form1
1256 args=(sys.stdout,)
1257
1258 [formatter_form1]
1259 format=%(levelname)s ++ %(message)s
1260 datefmt=
1261 """
1262
Christian Heimes180510d2008-03-03 19:15:45 +00001263 # config2 has a subtle configuration error that should be reported
1264 config2 = config1.replace("sys.stdout", "sys.stbout")
1265
1266 # config3 has a less subtle configuration error
1267 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1268
1269 # config4 specifies a custom formatter class to be loaded
1270 config4 = """
1271 [loggers]
1272 keys=root
1273
1274 [handlers]
1275 keys=hand1
1276
1277 [formatters]
1278 keys=form1
1279
1280 [logger_root]
1281 level=NOTSET
1282 handlers=hand1
1283
1284 [handler_hand1]
1285 class=StreamHandler
1286 level=NOTSET
1287 formatter=form1
1288 args=(sys.stdout,)
1289
1290 [formatter_form1]
1291 class=""" + __name__ + """.ExceptionFormatter
1292 format=%(levelname)s:%(name)s:%(message)s
1293 datefmt=
1294 """
1295
Georg Brandl3dbca812008-07-23 16:10:53 +00001296 # config5 specifies a custom handler class to be loaded
1297 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1298
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001299 # config6 uses ', ' delimiters in the handlers and formatters sections
1300 config6 = """
1301 [loggers]
1302 keys=root,parser
1303
1304 [handlers]
1305 keys=hand1, hand2
1306
1307 [formatters]
1308 keys=form1, form2
1309
1310 [logger_root]
1311 level=WARNING
1312 handlers=
1313
1314 [logger_parser]
1315 level=DEBUG
1316 handlers=hand1
1317 propagate=1
1318 qualname=compiler.parser
1319
1320 [handler_hand1]
1321 class=StreamHandler
1322 level=NOTSET
1323 formatter=form1
1324 args=(sys.stdout,)
1325
1326 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001327 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001328 level=NOTSET
1329 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001330 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001331
1332 [formatter_form1]
1333 format=%(levelname)s ++ %(message)s
1334 datefmt=
1335
1336 [formatter_form2]
1337 format=%(message)s
1338 datefmt=
1339 """
1340
Preston Landers6ea56d22017-08-02 15:44:28 -05001341 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001342 config7 = """
1343 [loggers]
1344 keys=root,parser,compiler
1345
1346 [handlers]
1347 keys=hand1
1348
1349 [formatters]
1350 keys=form1
1351
1352 [logger_root]
1353 level=WARNING
1354 handlers=hand1
1355
1356 [logger_compiler]
1357 level=DEBUG
1358 handlers=
1359 propagate=1
1360 qualname=compiler
1361
1362 [logger_parser]
1363 level=DEBUG
1364 handlers=
1365 propagate=1
1366 qualname=compiler.parser
1367
1368 [handler_hand1]
1369 class=StreamHandler
1370 level=NOTSET
1371 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001372 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001373
1374 [formatter_form1]
1375 format=%(levelname)s ++ %(message)s
1376 datefmt=
1377 """
1378
Xtreak087570a2018-07-02 14:27:46 +05301379 # config 8, check for resource warning
1380 config8 = r"""
1381 [loggers]
1382 keys=root
1383
1384 [handlers]
1385 keys=file
1386
1387 [formatters]
1388 keys=
1389
1390 [logger_root]
1391 level=DEBUG
1392 handlers=file
1393
1394 [handler_file]
1395 class=FileHandler
1396 level=DEBUG
1397 args=("{tempfile}",)
1398 """
1399
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001400 disable_test = """
1401 [loggers]
1402 keys=root
1403
1404 [handlers]
1405 keys=screen
1406
1407 [formatters]
1408 keys=
1409
1410 [logger_root]
1411 level=DEBUG
1412 handlers=screen
1413
1414 [handler_screen]
1415 level=DEBUG
1416 class=StreamHandler
1417 args=(sys.stdout,)
1418 formatter=
1419 """
1420
1421 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001422 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001423 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001424
1425 def test_config0_ok(self):
1426 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001427 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001428 self.apply_config(self.config0)
1429 logger = logging.getLogger()
1430 # Won't output anything
1431 logger.info(self.next_message())
1432 # Outputs a message
1433 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001434 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001435 ('ERROR', '2'),
1436 ], stream=output)
1437 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001438 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001439
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001440 def test_config0_using_cp_ok(self):
1441 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001442 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001443 file = io.StringIO(textwrap.dedent(self.config0))
1444 cp = configparser.ConfigParser()
1445 cp.read_file(file)
1446 logging.config.fileConfig(cp)
1447 logger = logging.getLogger()
1448 # Won't output anything
1449 logger.info(self.next_message())
1450 # Outputs a message
1451 logger.error(self.next_message())
1452 self.assert_log_lines([
1453 ('ERROR', '2'),
1454 ], stream=output)
1455 # Original logger output is empty.
1456 self.assert_log_lines([])
1457
Georg Brandl3dbca812008-07-23 16:10:53 +00001458 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001459 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001460 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001461 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001462 logger = logging.getLogger("compiler.parser")
1463 # Both will output a message
1464 logger.info(self.next_message())
1465 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001466 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001467 ('INFO', '1'),
1468 ('ERROR', '2'),
1469 ], stream=output)
1470 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001471 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001472
1473 def test_config2_failure(self):
1474 # A simple config file which overrides the default settings.
1475 self.assertRaises(Exception, self.apply_config, self.config2)
1476
1477 def test_config3_failure(self):
1478 # A simple config file which overrides the default settings.
1479 self.assertRaises(Exception, self.apply_config, self.config3)
1480
1481 def test_config4_ok(self):
1482 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001483 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001484 self.apply_config(self.config4)
1485 logger = logging.getLogger()
1486 try:
1487 raise RuntimeError()
1488 except RuntimeError:
1489 logging.exception("just testing")
1490 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001491 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001492 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1493 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001494 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001495
Georg Brandl3dbca812008-07-23 16:10:53 +00001496 def test_config5_ok(self):
1497 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001498
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001499 def test_config6_ok(self):
1500 self.test_config1_ok(config=self.config6)
1501
Vinay Sajip3f84b072011-03-07 17:49:33 +00001502 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001503 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001504 self.apply_config(self.config1a)
1505 logger = logging.getLogger("compiler.parser")
1506 # See issue #11424. compiler-hyphenated sorts
1507 # between compiler and compiler.xyz and this
1508 # was preventing compiler.xyz from being included
1509 # in the child loggers of compiler because of an
1510 # overzealous loop termination condition.
1511 hyphenated = logging.getLogger('compiler-hyphenated')
1512 # All will output a message
1513 logger.info(self.next_message())
1514 logger.error(self.next_message())
1515 hyphenated.critical(self.next_message())
1516 self.assert_log_lines([
1517 ('INFO', '1'),
1518 ('ERROR', '2'),
1519 ('CRITICAL', '3'),
1520 ], stream=output)
1521 # Original logger output is empty.
1522 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001523 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001524 self.apply_config(self.config7)
1525 logger = logging.getLogger("compiler.parser")
1526 self.assertFalse(logger.disabled)
1527 # Both will output a message
1528 logger.info(self.next_message())
1529 logger.error(self.next_message())
1530 logger = logging.getLogger("compiler.lexer")
1531 # Both will output a message
1532 logger.info(self.next_message())
1533 logger.error(self.next_message())
1534 # Will not appear
1535 hyphenated.critical(self.next_message())
1536 self.assert_log_lines([
1537 ('INFO', '4'),
1538 ('ERROR', '5'),
1539 ('INFO', '6'),
1540 ('ERROR', '7'),
1541 ], stream=output)
1542 # Original logger output is empty.
1543 self.assert_log_lines([])
1544
Xtreak087570a2018-07-02 14:27:46 +05301545 def test_config8_ok(self):
1546
1547 def cleanup(h1, fn):
1548 h1.close()
1549 os.remove(fn)
1550
1551 with self.check_no_resource_warning():
1552 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1553 os.close(fd)
1554
1555 # Replace single backslash with double backslash in windows
1556 # to avoid unicode error during string formatting
1557 if os.name == "nt":
1558 fn = fn.replace("\\", "\\\\")
1559
1560 config8 = self.config8.format(tempfile=fn)
1561
1562 self.apply_config(config8)
1563 self.apply_config(config8)
1564
1565 handler = logging.root.handlers[0]
1566 self.addCleanup(cleanup, handler, fn)
1567
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001568 def test_logger_disabling(self):
1569 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001570 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001571 self.assertFalse(logger.disabled)
1572 self.apply_config(self.disable_test)
1573 self.assertTrue(logger.disabled)
1574 self.apply_config(self.disable_test, disable_existing_loggers=False)
1575 self.assertFalse(logger.disabled)
1576
Lucas Cimonb15100f2019-10-31 09:06:25 +01001577 def test_config_set_handler_names(self):
1578 test_config = """
1579 [loggers]
1580 keys=root
1581
1582 [handlers]
1583 keys=hand1
1584
1585 [formatters]
1586 keys=form1
1587
1588 [logger_root]
1589 handlers=hand1
1590
1591 [handler_hand1]
1592 class=StreamHandler
1593 formatter=form1
1594
1595 [formatter_form1]
1596 format=%(levelname)s ++ %(message)s
1597 """
1598 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001599 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001600
Łukasz Langa214f18e2018-06-08 04:02:48 -07001601 def test_defaults_do_no_interpolation(self):
1602 """bpo-33802 defaults should not get interpolated"""
1603 ini = textwrap.dedent("""
1604 [formatters]
1605 keys=default
1606
1607 [formatter_default]
1608
1609 [handlers]
1610 keys=console
1611
1612 [handler_console]
1613 class=logging.StreamHandler
1614 args=tuple()
1615
1616 [loggers]
1617 keys=root
1618
1619 [logger_root]
1620 formatter=default
1621 handlers=console
1622 """).strip()
1623 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1624 try:
1625 os.write(fd, ini.encode('ascii'))
1626 os.close(fd)
1627 logging.config.fileConfig(
1628 fn,
1629 defaults=dict(
1630 version=1,
1631 disable_existing_loggers=False,
1632 formatters={
1633 "generic": {
1634 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1635 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1636 "class": "logging.Formatter"
1637 },
1638 },
1639 )
1640 )
1641 finally:
1642 os.unlink(fn)
1643
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001644
Christian Heimes180510d2008-03-03 19:15:45 +00001645class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001646
Christian Heimes180510d2008-03-03 19:15:45 +00001647 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001648
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001649 server_class = TestTCPServer
1650 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001651
Christian Heimes180510d2008-03-03 19:15:45 +00001652 def setUp(self):
1653 """Set up a TCP server to receive log messages, and a SocketHandler
1654 pointing to that server's address and port."""
1655 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001656 # Issue #29177: deal with errors that happen during setup
1657 self.server = self.sock_hdlr = self.server_exception = None
1658 try:
1659 self.server = server = self.server_class(self.address,
1660 self.handle_socket, 0.01)
1661 server.start()
1662 # Uncomment next line to test error recovery in setUp()
1663 # raise OSError('dummy error raised')
1664 except OSError as e:
1665 self.server_exception = e
1666 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001667 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001668 hcls = logging.handlers.SocketHandler
1669 if isinstance(server.server_address, tuple):
1670 self.sock_hdlr = hcls('localhost', server.port)
1671 else:
1672 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001673 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001674 self.root_logger.removeHandler(self.root_logger.handlers[0])
1675 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001676 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001677
Christian Heimes180510d2008-03-03 19:15:45 +00001678 def tearDown(self):
1679 """Shutdown the TCP server."""
1680 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001681 if self.sock_hdlr:
1682 self.root_logger.removeHandler(self.sock_hdlr)
1683 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001684 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001685 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001686 finally:
1687 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001688
Vinay Sajip7367d082011-05-02 13:17:27 +01001689 def handle_socket(self, request):
1690 conn = request.connection
1691 while True:
1692 chunk = conn.recv(4)
1693 if len(chunk) < 4:
1694 break
1695 slen = struct.unpack(">L", chunk)[0]
1696 chunk = conn.recv(slen)
1697 while len(chunk) < slen:
1698 chunk = chunk + conn.recv(slen - len(chunk))
1699 obj = pickle.loads(chunk)
1700 record = logging.makeLogRecord(obj)
1701 self.log_output += record.msg + '\n'
1702 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001703
Christian Heimes180510d2008-03-03 19:15:45 +00001704 def test_output(self):
1705 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001706 if self.server_exception:
1707 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001708 logger = logging.getLogger("tcp")
1709 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001710 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001711 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001712 self.handled.acquire()
1713 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001714
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001715 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001716 if self.server_exception:
1717 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001718 # Avoid timing-related failures due to SocketHandler's own hard-wired
1719 # one-second timeout on socket.create_connection() (issue #16264).
1720 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001721 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001722 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001723 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001724 try:
1725 raise RuntimeError('Deliberate mistake')
1726 except RuntimeError:
1727 self.root_logger.exception('Never sent')
1728 self.root_logger.error('Never sent, either')
1729 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001730 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001731 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1732 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001733
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001734def _get_temp_domain_socket():
1735 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1736 os.close(fd)
1737 # just need a name - file can't be present, or we'll get an
1738 # 'address already in use' error.
1739 os.remove(fn)
1740 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001741
Victor Stinnerec5a8602014-06-02 14:41:51 +02001742@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001743class UnixSocketHandlerTest(SocketHandlerTest):
1744
1745 """Test for SocketHandler with unix sockets."""
1746
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001747 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001748 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001749
1750 def setUp(self):
1751 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001752 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001753 SocketHandlerTest.setUp(self)
1754
1755 def tearDown(self):
1756 SocketHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001757 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001758
Vinay Sajip7367d082011-05-02 13:17:27 +01001759class DatagramHandlerTest(BaseTest):
1760
1761 """Test for DatagramHandler."""
1762
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001763 server_class = TestUDPServer
1764 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001765
Vinay Sajip7367d082011-05-02 13:17:27 +01001766 def setUp(self):
1767 """Set up a UDP server to receive log messages, and a DatagramHandler
1768 pointing to that server's address and port."""
1769 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001770 # Issue #29177: deal with errors that happen during setup
1771 self.server = self.sock_hdlr = self.server_exception = None
1772 try:
1773 self.server = server = self.server_class(self.address,
1774 self.handle_datagram, 0.01)
1775 server.start()
1776 # Uncomment next line to test error recovery in setUp()
1777 # raise OSError('dummy error raised')
1778 except OSError as e:
1779 self.server_exception = e
1780 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001781 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001782 hcls = logging.handlers.DatagramHandler
1783 if isinstance(server.server_address, tuple):
1784 self.sock_hdlr = hcls('localhost', server.port)
1785 else:
1786 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001787 self.log_output = ''
1788 self.root_logger.removeHandler(self.root_logger.handlers[0])
1789 self.root_logger.addHandler(self.sock_hdlr)
1790 self.handled = threading.Event()
1791
1792 def tearDown(self):
1793 """Shutdown the UDP server."""
1794 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001795 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001796 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001797 if self.sock_hdlr:
1798 self.root_logger.removeHandler(self.sock_hdlr)
1799 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001800 finally:
1801 BaseTest.tearDown(self)
1802
1803 def handle_datagram(self, request):
1804 slen = struct.pack('>L', 0) # length of prefix
1805 packet = request.packet[len(slen):]
1806 obj = pickle.loads(packet)
1807 record = logging.makeLogRecord(obj)
1808 self.log_output += record.msg + '\n'
1809 self.handled.set()
1810
1811 def test_output(self):
1812 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001813 if self.server_exception:
1814 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001815 logger = logging.getLogger("udp")
1816 logger.error("spam")
1817 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001818 self.handled.clear()
1819 logger.error("eggs")
1820 self.handled.wait()
1821 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001822
Victor Stinnerec5a8602014-06-02 14:41:51 +02001823@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001824class UnixDatagramHandlerTest(DatagramHandlerTest):
1825
1826 """Test for DatagramHandler using Unix sockets."""
1827
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001828 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001829 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001830
1831 def setUp(self):
1832 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001833 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001834 DatagramHandlerTest.setUp(self)
1835
1836 def tearDown(self):
1837 DatagramHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001838 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001839
Vinay Sajip7367d082011-05-02 13:17:27 +01001840class SysLogHandlerTest(BaseTest):
1841
1842 """Test for SysLogHandler using UDP."""
1843
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001844 server_class = TestUDPServer
1845 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001846
Vinay Sajip7367d082011-05-02 13:17:27 +01001847 def setUp(self):
1848 """Set up a UDP server to receive log messages, and a SysLogHandler
1849 pointing to that server's address and port."""
1850 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001851 # Issue #29177: deal with errors that happen during setup
1852 self.server = self.sl_hdlr = self.server_exception = None
1853 try:
1854 self.server = server = self.server_class(self.address,
1855 self.handle_datagram, 0.01)
1856 server.start()
1857 # Uncomment next line to test error recovery in setUp()
1858 # raise OSError('dummy error raised')
1859 except OSError as e:
1860 self.server_exception = e
1861 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001862 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001863 hcls = logging.handlers.SysLogHandler
1864 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001865 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001866 else:
1867 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001868 self.log_output = ''
1869 self.root_logger.removeHandler(self.root_logger.handlers[0])
1870 self.root_logger.addHandler(self.sl_hdlr)
1871 self.handled = threading.Event()
1872
1873 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001874 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001875 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001876 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001877 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001878 if self.sl_hdlr:
1879 self.root_logger.removeHandler(self.sl_hdlr)
1880 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001881 finally:
1882 BaseTest.tearDown(self)
1883
1884 def handle_datagram(self, request):
1885 self.log_output = request.packet
1886 self.handled.set()
1887
1888 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001889 if self.server_exception:
1890 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001891 # The log message sent to the SysLogHandler is properly received.
1892 logger = logging.getLogger("slh")
1893 logger.error("sp\xe4m")
1894 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001895 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001896 self.handled.clear()
1897 self.sl_hdlr.append_nul = False
1898 logger.error("sp\xe4m")
1899 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001900 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001901 self.handled.clear()
1902 self.sl_hdlr.ident = "h\xe4m-"
1903 logger.error("sp\xe4m")
1904 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001905 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001906
Victor Stinnerec5a8602014-06-02 14:41:51 +02001907@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001908class UnixSysLogHandlerTest(SysLogHandlerTest):
1909
1910 """Test for SysLogHandler with Unix sockets."""
1911
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001912 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001913 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001914
1915 def setUp(self):
1916 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001917 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001918 SysLogHandlerTest.setUp(self)
1919
1920 def tearDown(self):
1921 SysLogHandlerTest.tearDown(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001922 support.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001923
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001924@unittest.skipUnless(support.IPV6_ENABLED,
1925 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001926class IPv6SysLogHandlerTest(SysLogHandlerTest):
1927
1928 """Test for SysLogHandler with IPv6 host."""
1929
1930 server_class = TestUDPServer
1931 address = ('::1', 0)
1932
1933 def setUp(self):
1934 self.server_class.address_family = socket.AF_INET6
1935 super(IPv6SysLogHandlerTest, self).setUp()
1936
1937 def tearDown(self):
1938 self.server_class.address_family = socket.AF_INET
1939 super(IPv6SysLogHandlerTest, self).tearDown()
1940
Vinay Sajip7367d082011-05-02 13:17:27 +01001941class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001942 """Test for HTTPHandler."""
1943
1944 def setUp(self):
1945 """Set up an HTTP server to receive log messages, and a HTTPHandler
1946 pointing to that server's address and port."""
1947 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001948 self.handled = threading.Event()
1949
Vinay Sajip7367d082011-05-02 13:17:27 +01001950 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001951 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001952 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001953 if self.command == 'POST':
1954 try:
1955 rlen = int(request.headers['Content-Length'])
1956 self.post_data = request.rfile.read(rlen)
1957 except:
1958 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001959 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001960 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001961 self.handled.set()
1962
1963 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001964 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001965 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001966 root_logger = self.root_logger
1967 root_logger.removeHandler(self.root_logger.handlers[0])
1968 for secure in (False, True):
1969 addr = ('localhost', 0)
1970 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01001971 try:
1972 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01001973 except ImportError:
1974 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001975 else:
1976 here = os.path.dirname(__file__)
1977 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02001978 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05001979 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06001980
1981 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01001982 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01001983 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06001984 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01001985 self.server = server = TestHTTPServer(addr, self.handle_request,
1986 0.01, sslctx=sslctx)
1987 server.start()
1988 server.ready.wait()
1989 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01001990 secure_client = secure and sslctx
1991 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06001992 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01001993 context=context,
1994 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01001995 self.log_data = None
1996 root_logger.addHandler(self.h_hdlr)
1997
1998 for method in ('GET', 'POST'):
1999 self.h_hdlr.method = method
2000 self.handled.clear()
2001 msg = "sp\xe4m"
2002 logger.error(msg)
2003 self.handled.wait()
2004 self.assertEqual(self.log_data.path, '/frob')
2005 self.assertEqual(self.command, method)
2006 if method == 'GET':
2007 d = parse_qs(self.log_data.query)
2008 else:
2009 d = parse_qs(self.post_data.decode('utf-8'))
2010 self.assertEqual(d['name'], ['http'])
2011 self.assertEqual(d['funcName'], ['test_output'])
2012 self.assertEqual(d['msg'], [msg])
2013
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002014 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002015 self.root_logger.removeHandler(self.h_hdlr)
2016 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002017
Christian Heimes180510d2008-03-03 19:15:45 +00002018class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002019
Christian Heimes180510d2008-03-03 19:15:45 +00002020 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002021
Christian Heimes180510d2008-03-03 19:15:45 +00002022 def setUp(self):
2023 """Create a dict to remember potentially destroyed objects."""
2024 BaseTest.setUp(self)
2025 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002026
Christian Heimes180510d2008-03-03 19:15:45 +00002027 def _watch_for_survival(self, *args):
2028 """Watch the given objects for survival, by creating weakrefs to
2029 them."""
2030 for obj in args:
2031 key = id(obj), repr(obj)
2032 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002033
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002034 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002035 """Assert that all objects watched for survival have survived."""
2036 # Trigger cycle breaking.
2037 gc.collect()
2038 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002039 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002040 if ref() is None:
2041 dead.append(repr_)
2042 if dead:
2043 self.fail("%d objects should have survived "
2044 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002045
Christian Heimes180510d2008-03-03 19:15:45 +00002046 def test_persistent_loggers(self):
2047 # Logger objects are persistent and retain their configuration, even
2048 # if visible references are destroyed.
2049 self.root_logger.setLevel(logging.INFO)
2050 foo = logging.getLogger("foo")
2051 self._watch_for_survival(foo)
2052 foo.setLevel(logging.DEBUG)
2053 self.root_logger.debug(self.next_message())
2054 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002055 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002056 ('foo', 'DEBUG', '2'),
2057 ])
2058 del foo
2059 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002060 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002061 # foo has retained its settings.
2062 bar = logging.getLogger("foo")
2063 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002064 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002065 ('foo', 'DEBUG', '2'),
2066 ('foo', 'DEBUG', '3'),
2067 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002068
Benjamin Petersonf91df042009-02-13 02:50:59 +00002069
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002070class EncodingTest(BaseTest):
2071 def test_encoding_plain_file(self):
2072 # In Python 2.x, a plain file object is treated as having no encoding.
2073 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002074 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2075 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002076 # the non-ascii data we write to the log.
2077 data = "foo\x80"
2078 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002079 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002080 log.addHandler(handler)
2081 try:
2082 # write non-ascii data to the log.
2083 log.warning(data)
2084 finally:
2085 log.removeHandler(handler)
2086 handler.close()
2087 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002088 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002089 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002090 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002091 finally:
2092 f.close()
2093 finally:
2094 if os.path.isfile(fn):
2095 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002096
Benjamin Petersonf91df042009-02-13 02:50:59 +00002097 def test_encoding_cyrillic_unicode(self):
2098 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002099 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002100 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002101 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002102 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002103 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002104 stream = io.BytesIO()
2105 writer = writer_class(stream, 'strict')
2106 handler = logging.StreamHandler(writer)
2107 log.addHandler(handler)
2108 try:
2109 log.warning(message)
2110 finally:
2111 log.removeHandler(handler)
2112 handler.close()
2113 # check we wrote exactly those bytes, ignoring trailing \n etc
2114 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002115 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002116 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2117
2118
Georg Brandlf9734072008-12-07 15:30:06 +00002119class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002120
Georg Brandlf9734072008-12-07 15:30:06 +00002121 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002122 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002123 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002124 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002125 warnings.filterwarnings("always", category=UserWarning)
2126 stream = io.StringIO()
2127 h = logging.StreamHandler(stream)
2128 logger = logging.getLogger("py.warnings")
2129 logger.addHandler(h)
2130 warnings.warn("I'm warning you...")
2131 logger.removeHandler(h)
2132 s = stream.getvalue()
2133 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002134 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002135
Mike53f7a7c2017-12-14 14:04:53 +03002136 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002137 a_file = io.StringIO()
2138 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2139 a_file, "Dummy line")
2140 s = a_file.getvalue()
2141 a_file.close()
2142 self.assertEqual(s,
2143 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2144
2145 def test_warnings_no_handlers(self):
2146 with warnings.catch_warnings():
2147 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002148 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002149
2150 # confirm our assumption: no loggers are set
2151 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002152 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002153
2154 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002155 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002156 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002157
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002158
2159def formatFunc(format, datefmt=None):
2160 return logging.Formatter(format, datefmt)
2161
BNMetrics18fb1fb2018-10-15 19:41:36 +01002162class myCustomFormatter:
2163 def __init__(self, fmt, datefmt=None):
2164 pass
2165
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002166def handlerFunc():
2167 return logging.StreamHandler()
2168
2169class CustomHandler(logging.StreamHandler):
2170 pass
2171
2172class ConfigDictTest(BaseTest):
2173
2174 """Reading logging config from a dictionary."""
2175
Xtreak087570a2018-07-02 14:27:46 +05302176 check_no_resource_warning = support.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002177 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002178
2179 # config0 is a standard configuration.
2180 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002181 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002182 'formatters': {
2183 'form1' : {
2184 'format' : '%(levelname)s ++ %(message)s',
2185 },
2186 },
2187 'handlers' : {
2188 'hand1' : {
2189 'class' : 'logging.StreamHandler',
2190 'formatter' : 'form1',
2191 'level' : 'NOTSET',
2192 'stream' : 'ext://sys.stdout',
2193 },
2194 },
2195 'root' : {
2196 'level' : 'WARNING',
2197 'handlers' : ['hand1'],
2198 },
2199 }
2200
2201 # config1 adds a little to the standard configuration.
2202 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002203 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002204 'formatters': {
2205 'form1' : {
2206 'format' : '%(levelname)s ++ %(message)s',
2207 },
2208 },
2209 'handlers' : {
2210 'hand1' : {
2211 'class' : 'logging.StreamHandler',
2212 'formatter' : 'form1',
2213 'level' : 'NOTSET',
2214 'stream' : 'ext://sys.stdout',
2215 },
2216 },
2217 'loggers' : {
2218 'compiler.parser' : {
2219 'level' : 'DEBUG',
2220 'handlers' : ['hand1'],
2221 },
2222 },
2223 'root' : {
2224 'level' : 'WARNING',
2225 },
2226 }
2227
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002228 # config1a moves the handler to the root. Used with config8a
2229 config1a = {
2230 'version': 1,
2231 'formatters': {
2232 'form1' : {
2233 'format' : '%(levelname)s ++ %(message)s',
2234 },
2235 },
2236 'handlers' : {
2237 'hand1' : {
2238 'class' : 'logging.StreamHandler',
2239 'formatter' : 'form1',
2240 'level' : 'NOTSET',
2241 'stream' : 'ext://sys.stdout',
2242 },
2243 },
2244 'loggers' : {
2245 'compiler.parser' : {
2246 'level' : 'DEBUG',
2247 },
2248 },
2249 'root' : {
2250 'level' : 'WARNING',
2251 'handlers' : ['hand1'],
2252 },
2253 }
2254
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002255 # config2 has a subtle configuration error that should be reported
2256 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002257 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002258 'formatters': {
2259 'form1' : {
2260 'format' : '%(levelname)s ++ %(message)s',
2261 },
2262 },
2263 'handlers' : {
2264 'hand1' : {
2265 'class' : 'logging.StreamHandler',
2266 'formatter' : 'form1',
2267 'level' : 'NOTSET',
2268 'stream' : 'ext://sys.stdbout',
2269 },
2270 },
2271 'loggers' : {
2272 'compiler.parser' : {
2273 'level' : 'DEBUG',
2274 'handlers' : ['hand1'],
2275 },
2276 },
2277 'root' : {
2278 'level' : 'WARNING',
2279 },
2280 }
2281
Mike53f7a7c2017-12-14 14:04:53 +03002282 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002283 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002284 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002285 'formatters': {
2286 'form1' : {
2287 'format' : '%(levelname)s ++ %(message)s',
2288 },
2289 },
2290 'handlers' : {
2291 'hand1' : {
2292 'class' : 'logging.StreamHandler',
2293 'formatter' : 'form1',
2294 'level' : 'NTOSET',
2295 'stream' : 'ext://sys.stdout',
2296 },
2297 },
2298 'loggers' : {
2299 'compiler.parser' : {
2300 'level' : 'DEBUG',
2301 'handlers' : ['hand1'],
2302 },
2303 },
2304 'root' : {
2305 'level' : 'WARNING',
2306 },
2307 }
2308
2309
Mike53f7a7c2017-12-14 14:04:53 +03002310 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002311 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002312 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002313 'formatters': {
2314 'form1' : {
2315 'format' : '%(levelname)s ++ %(message)s',
2316 },
2317 },
2318 'handlers' : {
2319 'hand1' : {
2320 'class' : 'logging.StreamHandler',
2321 'formatter' : 'form1',
2322 'level' : 'NOTSET',
2323 'stream' : 'ext://sys.stdout',
2324 },
2325 },
2326 'loggers' : {
2327 'compiler.parser' : {
2328 'level' : 'DEBUG',
2329 'handlers' : ['hand1'],
2330 },
2331 },
2332 'root' : {
2333 'level' : 'WRANING',
2334 },
2335 }
2336
2337 # config3 has a less subtle configuration error
2338 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002339 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002340 'formatters': {
2341 'form1' : {
2342 'format' : '%(levelname)s ++ %(message)s',
2343 },
2344 },
2345 'handlers' : {
2346 'hand1' : {
2347 'class' : 'logging.StreamHandler',
2348 'formatter' : 'misspelled_name',
2349 'level' : 'NOTSET',
2350 'stream' : 'ext://sys.stdout',
2351 },
2352 },
2353 'loggers' : {
2354 'compiler.parser' : {
2355 'level' : 'DEBUG',
2356 'handlers' : ['hand1'],
2357 },
2358 },
2359 'root' : {
2360 'level' : 'WARNING',
2361 },
2362 }
2363
2364 # config4 specifies a custom formatter class to be loaded
2365 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002366 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002367 'formatters': {
2368 'form1' : {
2369 '()' : __name__ + '.ExceptionFormatter',
2370 'format' : '%(levelname)s:%(name)s:%(message)s',
2371 },
2372 },
2373 'handlers' : {
2374 'hand1' : {
2375 'class' : 'logging.StreamHandler',
2376 'formatter' : 'form1',
2377 'level' : 'NOTSET',
2378 'stream' : 'ext://sys.stdout',
2379 },
2380 },
2381 'root' : {
2382 'level' : 'NOTSET',
2383 'handlers' : ['hand1'],
2384 },
2385 }
2386
2387 # As config4 but using an actual callable rather than a string
2388 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002389 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002390 'formatters': {
2391 'form1' : {
2392 '()' : ExceptionFormatter,
2393 'format' : '%(levelname)s:%(name)s:%(message)s',
2394 },
2395 'form2' : {
2396 '()' : __name__ + '.formatFunc',
2397 'format' : '%(levelname)s:%(name)s:%(message)s',
2398 },
2399 'form3' : {
2400 '()' : formatFunc,
2401 'format' : '%(levelname)s:%(name)s:%(message)s',
2402 },
2403 },
2404 'handlers' : {
2405 'hand1' : {
2406 'class' : 'logging.StreamHandler',
2407 'formatter' : 'form1',
2408 'level' : 'NOTSET',
2409 'stream' : 'ext://sys.stdout',
2410 },
2411 'hand2' : {
2412 '()' : handlerFunc,
2413 },
2414 },
2415 'root' : {
2416 'level' : 'NOTSET',
2417 'handlers' : ['hand1'],
2418 },
2419 }
2420
2421 # config5 specifies a custom handler class to be loaded
2422 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002423 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002424 'formatters': {
2425 'form1' : {
2426 'format' : '%(levelname)s ++ %(message)s',
2427 },
2428 },
2429 'handlers' : {
2430 'hand1' : {
2431 'class' : __name__ + '.CustomHandler',
2432 'formatter' : 'form1',
2433 'level' : 'NOTSET',
2434 'stream' : 'ext://sys.stdout',
2435 },
2436 },
2437 'loggers' : {
2438 'compiler.parser' : {
2439 'level' : 'DEBUG',
2440 'handlers' : ['hand1'],
2441 },
2442 },
2443 'root' : {
2444 'level' : 'WARNING',
2445 },
2446 }
2447
2448 # config6 specifies a custom handler class to be loaded
2449 # but has bad arguments
2450 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002451 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002452 'formatters': {
2453 'form1' : {
2454 'format' : '%(levelname)s ++ %(message)s',
2455 },
2456 },
2457 'handlers' : {
2458 'hand1' : {
2459 'class' : __name__ + '.CustomHandler',
2460 'formatter' : 'form1',
2461 'level' : 'NOTSET',
2462 'stream' : 'ext://sys.stdout',
2463 '9' : 'invalid parameter name',
2464 },
2465 },
2466 'loggers' : {
2467 'compiler.parser' : {
2468 'level' : 'DEBUG',
2469 'handlers' : ['hand1'],
2470 },
2471 },
2472 'root' : {
2473 'level' : 'WARNING',
2474 },
2475 }
2476
Mike53f7a7c2017-12-14 14:04:53 +03002477 # config 7 does not define compiler.parser but defines compiler.lexer
2478 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002479 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002480 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002481 'formatters': {
2482 'form1' : {
2483 'format' : '%(levelname)s ++ %(message)s',
2484 },
2485 },
2486 'handlers' : {
2487 'hand1' : {
2488 'class' : 'logging.StreamHandler',
2489 'formatter' : 'form1',
2490 'level' : 'NOTSET',
2491 'stream' : 'ext://sys.stdout',
2492 },
2493 },
2494 'loggers' : {
2495 'compiler.lexer' : {
2496 'level' : 'DEBUG',
2497 'handlers' : ['hand1'],
2498 },
2499 },
2500 'root' : {
2501 'level' : 'WARNING',
2502 },
2503 }
2504
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002505 # config8 defines both compiler and compiler.lexer
2506 # so compiler.parser should not be disabled (since
2507 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002508 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002509 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002510 'disable_existing_loggers' : False,
2511 'formatters': {
2512 'form1' : {
2513 'format' : '%(levelname)s ++ %(message)s',
2514 },
2515 },
2516 'handlers' : {
2517 'hand1' : {
2518 'class' : 'logging.StreamHandler',
2519 'formatter' : 'form1',
2520 'level' : 'NOTSET',
2521 'stream' : 'ext://sys.stdout',
2522 },
2523 },
2524 'loggers' : {
2525 'compiler' : {
2526 'level' : 'DEBUG',
2527 'handlers' : ['hand1'],
2528 },
2529 'compiler.lexer' : {
2530 },
2531 },
2532 'root' : {
2533 'level' : 'WARNING',
2534 },
2535 }
2536
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002537 # config8a disables existing loggers
2538 config8a = {
2539 'version': 1,
2540 'disable_existing_loggers' : True,
2541 'formatters': {
2542 'form1' : {
2543 'format' : '%(levelname)s ++ %(message)s',
2544 },
2545 },
2546 'handlers' : {
2547 'hand1' : {
2548 'class' : 'logging.StreamHandler',
2549 'formatter' : 'form1',
2550 'level' : 'NOTSET',
2551 'stream' : 'ext://sys.stdout',
2552 },
2553 },
2554 'loggers' : {
2555 'compiler' : {
2556 'level' : 'DEBUG',
2557 'handlers' : ['hand1'],
2558 },
2559 'compiler.lexer' : {
2560 },
2561 },
2562 'root' : {
2563 'level' : 'WARNING',
2564 },
2565 }
2566
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002567 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002568 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002569 'formatters': {
2570 'form1' : {
2571 'format' : '%(levelname)s ++ %(message)s',
2572 },
2573 },
2574 'handlers' : {
2575 'hand1' : {
2576 'class' : 'logging.StreamHandler',
2577 'formatter' : 'form1',
2578 'level' : 'WARNING',
2579 'stream' : 'ext://sys.stdout',
2580 },
2581 },
2582 'loggers' : {
2583 'compiler.parser' : {
2584 'level' : 'WARNING',
2585 'handlers' : ['hand1'],
2586 },
2587 },
2588 'root' : {
2589 'level' : 'NOTSET',
2590 },
2591 }
2592
2593 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002594 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002595 'incremental' : True,
2596 'handlers' : {
2597 'hand1' : {
2598 'level' : 'WARNING',
2599 },
2600 },
2601 'loggers' : {
2602 'compiler.parser' : {
2603 'level' : 'INFO',
2604 },
2605 },
2606 }
2607
2608 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002609 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002610 'incremental' : True,
2611 'handlers' : {
2612 'hand1' : {
2613 'level' : 'INFO',
2614 },
2615 },
2616 'loggers' : {
2617 'compiler.parser' : {
2618 'level' : 'INFO',
2619 },
2620 },
2621 }
2622
Mike53f7a7c2017-12-14 14:04:53 +03002623 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002624 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002625 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002626 'formatters': {
2627 'form1' : {
2628 'format' : '%(levelname)s ++ %(message)s',
2629 },
2630 },
2631 'filters' : {
2632 'filt1' : {
2633 'name' : 'compiler.parser',
2634 },
2635 },
2636 'handlers' : {
2637 'hand1' : {
2638 'class' : 'logging.StreamHandler',
2639 'formatter' : 'form1',
2640 'level' : 'NOTSET',
2641 'stream' : 'ext://sys.stdout',
2642 'filters' : ['filt1'],
2643 },
2644 },
2645 'loggers' : {
2646 'compiler.parser' : {
2647 'level' : 'DEBUG',
2648 'filters' : ['filt1'],
2649 },
2650 },
2651 'root' : {
2652 'level' : 'WARNING',
2653 'handlers' : ['hand1'],
2654 },
2655 }
2656
Mike53f7a7c2017-12-14 14:04:53 +03002657 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002658 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002659 'version': 1,
2660 'true_formatters': {
2661 'form1' : {
2662 'format' : '%(levelname)s ++ %(message)s',
2663 },
2664 },
2665 'handler_configs': {
2666 'hand1' : {
2667 'class' : 'logging.StreamHandler',
2668 'formatter' : 'form1',
2669 'level' : 'NOTSET',
2670 'stream' : 'ext://sys.stdout',
2671 },
2672 },
2673 'formatters' : 'cfg://true_formatters',
2674 'handlers' : {
2675 'hand1' : 'cfg://handler_configs[hand1]',
2676 },
2677 'loggers' : {
2678 'compiler.parser' : {
2679 'level' : 'DEBUG',
2680 'handlers' : ['hand1'],
2681 },
2682 },
2683 'root' : {
2684 'level' : 'WARNING',
2685 },
2686 }
2687
Mike53f7a7c2017-12-14 14:04:53 +03002688 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002689 config12 = {
2690 'true_formatters': {
2691 'form1' : {
2692 'format' : '%(levelname)s ++ %(message)s',
2693 },
2694 },
2695 'handler_configs': {
2696 'hand1' : {
2697 'class' : 'logging.StreamHandler',
2698 'formatter' : 'form1',
2699 'level' : 'NOTSET',
2700 'stream' : 'ext://sys.stdout',
2701 },
2702 },
2703 'formatters' : 'cfg://true_formatters',
2704 'handlers' : {
2705 'hand1' : 'cfg://handler_configs[hand1]',
2706 },
2707 'loggers' : {
2708 'compiler.parser' : {
2709 'level' : 'DEBUG',
2710 'handlers' : ['hand1'],
2711 },
2712 },
2713 'root' : {
2714 'level' : 'WARNING',
2715 },
2716 }
2717
Mike53f7a7c2017-12-14 14:04:53 +03002718 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002719 config13 = {
2720 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002721 'true_formatters': {
2722 'form1' : {
2723 'format' : '%(levelname)s ++ %(message)s',
2724 },
2725 },
2726 'handler_configs': {
2727 'hand1' : {
2728 'class' : 'logging.StreamHandler',
2729 'formatter' : 'form1',
2730 'level' : 'NOTSET',
2731 'stream' : 'ext://sys.stdout',
2732 },
2733 },
2734 'formatters' : 'cfg://true_formatters',
2735 'handlers' : {
2736 'hand1' : 'cfg://handler_configs[hand1]',
2737 },
2738 'loggers' : {
2739 'compiler.parser' : {
2740 'level' : 'DEBUG',
2741 'handlers' : ['hand1'],
2742 },
2743 },
2744 'root' : {
2745 'level' : 'WARNING',
2746 },
2747 }
2748
Vinay Sajip8d270232012-11-15 14:20:18 +00002749 # As config0, but with properties
2750 config14 = {
2751 'version': 1,
2752 'formatters': {
2753 'form1' : {
2754 'format' : '%(levelname)s ++ %(message)s',
2755 },
2756 },
2757 'handlers' : {
2758 'hand1' : {
2759 'class' : 'logging.StreamHandler',
2760 'formatter' : 'form1',
2761 'level' : 'NOTSET',
2762 'stream' : 'ext://sys.stdout',
2763 '.': {
2764 'foo': 'bar',
2765 'terminator': '!\n',
2766 }
2767 },
2768 },
2769 'root' : {
2770 'level' : 'WARNING',
2771 'handlers' : ['hand1'],
2772 },
2773 }
2774
Vinay Sajip3f885b52013-03-22 15:19:54 +00002775 out_of_order = {
2776 "version": 1,
2777 "formatters": {
2778 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002779 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2780 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002781 }
2782 },
2783 "handlers": {
2784 "fileGlobal": {
2785 "class": "logging.StreamHandler",
2786 "level": "DEBUG",
2787 "formatter": "mySimpleFormatter"
2788 },
2789 "bufferGlobal": {
2790 "class": "logging.handlers.MemoryHandler",
2791 "capacity": 5,
2792 "formatter": "mySimpleFormatter",
2793 "target": "fileGlobal",
2794 "level": "DEBUG"
2795 }
2796 },
2797 "loggers": {
2798 "mymodule": {
2799 "level": "DEBUG",
2800 "handlers": ["bufferGlobal"],
2801 "propagate": "true"
2802 }
2803 }
2804 }
2805
BNMetrics18fb1fb2018-10-15 19:41:36 +01002806 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2807 custom_formatter_class_validate = {
2808 'version': 1,
2809 'formatters': {
2810 'form1': {
2811 '()': __name__ + '.ExceptionFormatter',
2812 'format': '%(levelname)s:%(name)s:%(message)s',
2813 'validate': False,
2814 },
2815 },
2816 'handlers' : {
2817 'hand1' : {
2818 'class': 'logging.StreamHandler',
2819 'formatter': 'form1',
2820 'level': 'NOTSET',
2821 'stream': 'ext://sys.stdout',
2822 },
2823 },
2824 "loggers": {
2825 "my_test_logger_custom_formatter": {
2826 "level": "DEBUG",
2827 "handlers": ["hand1"],
2828 "propagate": "true"
2829 }
2830 }
2831 }
2832
2833 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2834 custom_formatter_class_validate2 = {
2835 'version': 1,
2836 'formatters': {
2837 'form1': {
2838 'class': __name__ + '.ExceptionFormatter',
2839 'format': '%(levelname)s:%(name)s:%(message)s',
2840 'validate': False,
2841 },
2842 },
2843 'handlers' : {
2844 'hand1' : {
2845 'class': 'logging.StreamHandler',
2846 'formatter': 'form1',
2847 'level': 'NOTSET',
2848 'stream': 'ext://sys.stdout',
2849 },
2850 },
2851 "loggers": {
2852 "my_test_logger_custom_formatter": {
2853 "level": "DEBUG",
2854 "handlers": ["hand1"],
2855 "propagate": "true"
2856 }
2857 }
2858 }
2859
2860 # Configuration with custom class that is not inherited from logging.Formatter
2861 custom_formatter_class_validate3 = {
2862 'version': 1,
2863 'formatters': {
2864 'form1': {
2865 'class': __name__ + '.myCustomFormatter',
2866 'format': '%(levelname)s:%(name)s:%(message)s',
2867 'validate': False,
2868 },
2869 },
2870 'handlers' : {
2871 'hand1' : {
2872 'class': 'logging.StreamHandler',
2873 'formatter': 'form1',
2874 'level': 'NOTSET',
2875 'stream': 'ext://sys.stdout',
2876 },
2877 },
2878 "loggers": {
2879 "my_test_logger_custom_formatter": {
2880 "level": "DEBUG",
2881 "handlers": ["hand1"],
2882 "propagate": "true"
2883 }
2884 }
2885 }
2886
2887 # Configuration with custom function and 'validate' set to False
2888 custom_formatter_with_function = {
2889 'version': 1,
2890 'formatters': {
2891 'form1': {
2892 '()': formatFunc,
2893 'format': '%(levelname)s:%(name)s:%(message)s',
2894 'validate': False,
2895 },
2896 },
2897 'handlers' : {
2898 'hand1' : {
2899 'class': 'logging.StreamHandler',
2900 'formatter': 'form1',
2901 'level': 'NOTSET',
2902 'stream': 'ext://sys.stdout',
2903 },
2904 },
2905 "loggers": {
2906 "my_test_logger_custom_formatter": {
2907 "level": "DEBUG",
2908 "handlers": ["hand1"],
2909 "propagate": "true"
2910 }
2911 }
2912 }
2913
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002914 def apply_config(self, conf):
2915 logging.config.dictConfig(conf)
2916
2917 def test_config0_ok(self):
2918 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002919 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002920 self.apply_config(self.config0)
2921 logger = logging.getLogger()
2922 # Won't output anything
2923 logger.info(self.next_message())
2924 # Outputs a message
2925 logger.error(self.next_message())
2926 self.assert_log_lines([
2927 ('ERROR', '2'),
2928 ], stream=output)
2929 # Original logger output is empty.
2930 self.assert_log_lines([])
2931
2932 def test_config1_ok(self, config=config1):
2933 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002934 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002935 self.apply_config(config)
2936 logger = logging.getLogger("compiler.parser")
2937 # Both will output a message
2938 logger.info(self.next_message())
2939 logger.error(self.next_message())
2940 self.assert_log_lines([
2941 ('INFO', '1'),
2942 ('ERROR', '2'),
2943 ], stream=output)
2944 # Original logger output is empty.
2945 self.assert_log_lines([])
2946
2947 def test_config2_failure(self):
2948 # A simple config which overrides the default settings.
2949 self.assertRaises(Exception, self.apply_config, self.config2)
2950
2951 def test_config2a_failure(self):
2952 # A simple config which overrides the default settings.
2953 self.assertRaises(Exception, self.apply_config, self.config2a)
2954
2955 def test_config2b_failure(self):
2956 # A simple config which overrides the default settings.
2957 self.assertRaises(Exception, self.apply_config, self.config2b)
2958
2959 def test_config3_failure(self):
2960 # A simple config which overrides the default settings.
2961 self.assertRaises(Exception, self.apply_config, self.config3)
2962
2963 def test_config4_ok(self):
2964 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002965 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002966 self.apply_config(self.config4)
2967 #logger = logging.getLogger()
2968 try:
2969 raise RuntimeError()
2970 except RuntimeError:
2971 logging.exception("just testing")
2972 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002973 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002974 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2975 # Original logger output is empty
2976 self.assert_log_lines([])
2977
2978 def test_config4a_ok(self):
2979 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002980 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002981 self.apply_config(self.config4a)
2982 #logger = logging.getLogger()
2983 try:
2984 raise RuntimeError()
2985 except RuntimeError:
2986 logging.exception("just testing")
2987 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002988 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002989 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2990 # Original logger output is empty
2991 self.assert_log_lines([])
2992
2993 def test_config5_ok(self):
2994 self.test_config1_ok(config=self.config5)
2995
2996 def test_config6_failure(self):
2997 self.assertRaises(Exception, self.apply_config, self.config6)
2998
2999 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003000 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003001 self.apply_config(self.config1)
3002 logger = logging.getLogger("compiler.parser")
3003 # Both will output a message
3004 logger.info(self.next_message())
3005 logger.error(self.next_message())
3006 self.assert_log_lines([
3007 ('INFO', '1'),
3008 ('ERROR', '2'),
3009 ], stream=output)
3010 # Original logger output is empty.
3011 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003012 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003013 self.apply_config(self.config7)
3014 logger = logging.getLogger("compiler.parser")
3015 self.assertTrue(logger.disabled)
3016 logger = logging.getLogger("compiler.lexer")
3017 # Both will output a message
3018 logger.info(self.next_message())
3019 logger.error(self.next_message())
3020 self.assert_log_lines([
3021 ('INFO', '3'),
3022 ('ERROR', '4'),
3023 ], stream=output)
3024 # Original logger output is empty.
3025 self.assert_log_lines([])
3026
Mike53f7a7c2017-12-14 14:04:53 +03003027 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003028 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003029 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003030 self.apply_config(self.config1)
3031 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003032 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003033 logger.info(self.next_message())
3034 logger.error(self.next_message())
3035 self.assert_log_lines([
3036 ('INFO', '1'),
3037 ('ERROR', '2'),
3038 ], stream=output)
3039 # Original logger output is empty.
3040 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003041 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003042 self.apply_config(self.config8)
3043 logger = logging.getLogger("compiler.parser")
3044 self.assertFalse(logger.disabled)
3045 # Both will output a message
3046 logger.info(self.next_message())
3047 logger.error(self.next_message())
3048 logger = logging.getLogger("compiler.lexer")
3049 # Both will output a message
3050 logger.info(self.next_message())
3051 logger.error(self.next_message())
3052 self.assert_log_lines([
3053 ('INFO', '3'),
3054 ('ERROR', '4'),
3055 ('INFO', '5'),
3056 ('ERROR', '6'),
3057 ], stream=output)
3058 # Original logger output is empty.
3059 self.assert_log_lines([])
3060
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003061 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003062 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003063 self.apply_config(self.config1a)
3064 logger = logging.getLogger("compiler.parser")
3065 # See issue #11424. compiler-hyphenated sorts
3066 # between compiler and compiler.xyz and this
3067 # was preventing compiler.xyz from being included
3068 # in the child loggers of compiler because of an
3069 # overzealous loop termination condition.
3070 hyphenated = logging.getLogger('compiler-hyphenated')
3071 # All will output a message
3072 logger.info(self.next_message())
3073 logger.error(self.next_message())
3074 hyphenated.critical(self.next_message())
3075 self.assert_log_lines([
3076 ('INFO', '1'),
3077 ('ERROR', '2'),
3078 ('CRITICAL', '3'),
3079 ], stream=output)
3080 # Original logger output is empty.
3081 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003082 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003083 self.apply_config(self.config8a)
3084 logger = logging.getLogger("compiler.parser")
3085 self.assertFalse(logger.disabled)
3086 # Both will output a message
3087 logger.info(self.next_message())
3088 logger.error(self.next_message())
3089 logger = logging.getLogger("compiler.lexer")
3090 # Both will output a message
3091 logger.info(self.next_message())
3092 logger.error(self.next_message())
3093 # Will not appear
3094 hyphenated.critical(self.next_message())
3095 self.assert_log_lines([
3096 ('INFO', '4'),
3097 ('ERROR', '5'),
3098 ('INFO', '6'),
3099 ('ERROR', '7'),
3100 ], stream=output)
3101 # Original logger output is empty.
3102 self.assert_log_lines([])
3103
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003104 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003105 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003106 self.apply_config(self.config9)
3107 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003108 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003109 logger.info(self.next_message())
3110 self.assert_log_lines([], stream=output)
3111 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003112 # Nothing will be output since handler is still 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.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003116 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003117 logger.info(self.next_message())
3118 self.assert_log_lines([
3119 ('INFO', '3'),
3120 ], stream=output)
3121
3122 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003123 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003124 self.apply_config(self.config10)
3125 logger = logging.getLogger("compiler.parser")
3126 logger.warning(self.next_message())
3127 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003128 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003129 logger.warning(self.next_message())
3130 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003131 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003132 logger.warning(self.next_message())
3133 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003134 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003135 logger.error(self.next_message())
3136 self.assert_log_lines([
3137 ('WARNING', '1'),
3138 ('ERROR', '4'),
3139 ], stream=output)
3140
3141 def test_config11_ok(self):
3142 self.test_config1_ok(self.config11)
3143
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003144 def test_config12_failure(self):
3145 self.assertRaises(Exception, self.apply_config, self.config12)
3146
3147 def test_config13_failure(self):
3148 self.assertRaises(Exception, self.apply_config, self.config13)
3149
Vinay Sajip8d270232012-11-15 14:20:18 +00003150 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003151 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003152 self.apply_config(self.config14)
3153 h = logging._handlers['hand1']
3154 self.assertEqual(h.foo, 'bar')
3155 self.assertEqual(h.terminator, '!\n')
3156 logging.warning('Exclamation')
3157 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3158
Xtreak087570a2018-07-02 14:27:46 +05303159 def test_config15_ok(self):
3160
3161 def cleanup(h1, fn):
3162 h1.close()
3163 os.remove(fn)
3164
3165 with self.check_no_resource_warning():
3166 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3167 os.close(fd)
3168
3169 config = {
3170 "version": 1,
3171 "handlers": {
3172 "file": {
3173 "class": "logging.FileHandler",
3174 "filename": fn
3175 }
3176 },
3177 "root": {
3178 "handlers": ["file"]
3179 }
3180 }
3181
3182 self.apply_config(config)
3183 self.apply_config(config)
3184
3185 handler = logging.root.handlers[0]
3186 self.addCleanup(cleanup, handler, fn)
3187
Vinay Sajip4ded5512012-10-02 15:56:16 +01003188 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003189 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003190 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003191 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003192 t.start()
3193 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003194 # Now get the port allocated
3195 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003196 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003197 try:
3198 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3199 sock.settimeout(2.0)
3200 sock.connect(('localhost', port))
3201
3202 slen = struct.pack('>L', len(text))
3203 s = slen + text
3204 sentsofar = 0
3205 left = len(s)
3206 while left > 0:
3207 sent = sock.send(s[sentsofar:])
3208 sentsofar += sent
3209 left -= sent
3210 sock.close()
3211 finally:
3212 t.ready.wait(2.0)
3213 logging.config.stopListening()
Victor Stinnerbbc8b792019-12-10 20:41:23 +01003214 support.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003215
3216 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003217 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003218 self.setup_via_listener(json.dumps(self.config10))
3219 logger = logging.getLogger("compiler.parser")
3220 logger.warning(self.next_message())
3221 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003222 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003223 logger.warning(self.next_message())
3224 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003225 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003226 logger.warning(self.next_message())
3227 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003228 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003229 logger.error(self.next_message())
3230 self.assert_log_lines([
3231 ('WARNING', '1'),
3232 ('ERROR', '4'),
3233 ], stream=output)
3234
3235 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003236 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003237 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3238 logger = logging.getLogger("compiler.parser")
3239 # Both will output a message
3240 logger.info(self.next_message())
3241 logger.error(self.next_message())
3242 self.assert_log_lines([
3243 ('INFO', '1'),
3244 ('ERROR', '2'),
3245 ], stream=output)
3246 # Original logger output is empty.
3247 self.assert_log_lines([])
3248
Vinay Sajip4ded5512012-10-02 15:56:16 +01003249 def test_listen_verify(self):
3250
3251 def verify_fail(stuff):
3252 return None
3253
3254 def verify_reverse(stuff):
3255 return stuff[::-1]
3256
3257 logger = logging.getLogger("compiler.parser")
3258 to_send = textwrap.dedent(ConfigFileTest.config1)
3259 # First, specify a verification function that will fail.
3260 # We expect to see no output, since our configuration
3261 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003262 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003263 self.setup_via_listener(to_send, verify_fail)
3264 # Both will output a message
3265 logger.info(self.next_message())
3266 logger.error(self.next_message())
3267 self.assert_log_lines([], stream=output)
3268 # Original logger output has the stuff we logged.
3269 self.assert_log_lines([
3270 ('INFO', '1'),
3271 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003272 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003273
3274 # Now, perform no verification. Our configuration
3275 # should take effect.
3276
Vinay Sajip1feedb42014-05-31 08:19:12 +01003277 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003278 self.setup_via_listener(to_send) # no verify callable specified
3279 logger = logging.getLogger("compiler.parser")
3280 # Both will output a message
3281 logger.info(self.next_message())
3282 logger.error(self.next_message())
3283 self.assert_log_lines([
3284 ('INFO', '3'),
3285 ('ERROR', '4'),
3286 ], stream=output)
3287 # Original logger output still has the stuff we logged before.
3288 self.assert_log_lines([
3289 ('INFO', '1'),
3290 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003291 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003292
3293 # Now, perform verification which transforms the bytes.
3294
Vinay Sajip1feedb42014-05-31 08:19:12 +01003295 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003296 self.setup_via_listener(to_send[::-1], verify_reverse)
3297 logger = logging.getLogger("compiler.parser")
3298 # Both will output a message
3299 logger.info(self.next_message())
3300 logger.error(self.next_message())
3301 self.assert_log_lines([
3302 ('INFO', '5'),
3303 ('ERROR', '6'),
3304 ], stream=output)
3305 # Original logger output still has the stuff we logged before.
3306 self.assert_log_lines([
3307 ('INFO', '1'),
3308 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003309 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003310
Vinay Sajip3f885b52013-03-22 15:19:54 +00003311 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003312 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3313
3314 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003315 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003316 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3317
3318 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003319 handler = logging.getLogger('mymodule').handlers[0]
3320 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003321 self.assertIsInstance(handler.formatter._style,
3322 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003323
BNMetrics18fb1fb2018-10-15 19:41:36 +01003324 def test_custom_formatter_class_with_validate(self):
3325 self.apply_config(self.custom_formatter_class_validate)
3326 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3327 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3328
3329 def test_custom_formatter_class_with_validate2(self):
3330 self.apply_config(self.custom_formatter_class_validate2)
3331 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3332 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3333
3334 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3335 config = self.custom_formatter_class_validate.copy()
3336 config['formatters']['form1']['style'] = "$"
3337
3338 # Exception should not be raise as we have configured 'validate' to False
3339 self.apply_config(config)
3340 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3341 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3342
3343 def test_custom_formatter_class_with_validate3(self):
3344 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3345
3346 def test_custom_formatter_function_with_validate(self):
3347 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3348
Vinay Sajip373baef2011-04-26 20:05:24 +01003349 def test_baseconfig(self):
3350 d = {
3351 'atuple': (1, 2, 3),
3352 'alist': ['a', 'b', 'c'],
3353 'adict': {'d': 'e', 'f': 3 },
3354 'nest1': ('g', ('h', 'i'), 'j'),
3355 'nest2': ['k', ['l', 'm'], 'n'],
3356 'nest3': ['o', 'cfg://alist', 'p'],
3357 }
3358 bc = logging.config.BaseConfigurator(d)
3359 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3360 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3361 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3362 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3363 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3364 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3365 v = bc.convert('cfg://nest3')
3366 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3367 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3368 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3369 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003370
Vinay Sajip46abfc12020-01-01 19:32:11 +00003371 def test_namedtuple(self):
3372 # see bpo-39142
3373 from collections import namedtuple
3374
3375 class MyHandler(logging.StreamHandler):
3376 def __init__(self, resource, *args, **kwargs):
3377 super().__init__(*args, **kwargs)
3378 self.resource: namedtuple = resource
3379
3380 def emit(self, record):
3381 record.msg += f' {self.resource.type}'
3382 return super().emit(record)
3383
3384 Resource = namedtuple('Resource', ['type', 'labels'])
3385 resource = Resource(type='my_type', labels=['a'])
3386
3387 config = {
3388 'version': 1,
3389 'handlers': {
3390 'myhandler': {
3391 '()': MyHandler,
3392 'resource': resource
3393 }
3394 },
3395 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3396 }
3397 with support.captured_stderr() as stderr:
3398 self.apply_config(config)
3399 logging.info('some log')
3400 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3401
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003402class ManagerTest(BaseTest):
3403 def test_manager_loggerclass(self):
3404 logged = []
3405
3406 class MyLogger(logging.Logger):
3407 def _log(self, level, msg, args, exc_info=None, extra=None):
3408 logged.append(msg)
3409
3410 man = logging.Manager(None)
3411 self.assertRaises(TypeError, man.setLoggerClass, int)
3412 man.setLoggerClass(MyLogger)
3413 logger = man.getLogger('test')
3414 logger.warning('should appear in logged')
3415 logging.warning('should not appear in logged')
3416
3417 self.assertEqual(logged, ['should appear in logged'])
3418
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003419 def test_set_log_record_factory(self):
3420 man = logging.Manager(None)
3421 expected = object()
3422 man.setLogRecordFactory(expected)
3423 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003424
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003425class ChildLoggerTest(BaseTest):
3426 def test_child_loggers(self):
3427 r = logging.getLogger()
3428 l1 = logging.getLogger('abc')
3429 l2 = logging.getLogger('def.ghi')
3430 c1 = r.getChild('xyz')
3431 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003432 self.assertIs(c1, logging.getLogger('xyz'))
3433 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003434 c1 = l1.getChild('def')
3435 c2 = c1.getChild('ghi')
3436 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003437 self.assertIs(c1, logging.getLogger('abc.def'))
3438 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3439 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003440
3441
Vinay Sajip6fac8172010-10-19 20:44:14 +00003442class DerivedLogRecord(logging.LogRecord):
3443 pass
3444
Vinay Sajip61561522010-12-03 11:50:38 +00003445class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003446
3447 def setUp(self):
3448 class CheckingFilter(logging.Filter):
3449 def __init__(self, cls):
3450 self.cls = cls
3451
3452 def filter(self, record):
3453 t = type(record)
3454 if t is not self.cls:
3455 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3456 self.cls)
3457 raise TypeError(msg)
3458 return True
3459
3460 BaseTest.setUp(self)
3461 self.filter = CheckingFilter(DerivedLogRecord)
3462 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003463 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003464
3465 def tearDown(self):
3466 self.root_logger.removeFilter(self.filter)
3467 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003468 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003469
3470 def test_logrecord_class(self):
3471 self.assertRaises(TypeError, self.root_logger.warning,
3472 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003473 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003474 self.root_logger.error(self.next_message())
3475 self.assert_log_lines([
3476 ('root', 'ERROR', '2'),
3477 ])
3478
3479
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003480class QueueHandlerTest(BaseTest):
3481 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003482 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003483
3484 def setUp(self):
3485 BaseTest.setUp(self)
3486 self.queue = queue.Queue(-1)
3487 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003488 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003489 self.que_logger = logging.getLogger('que')
3490 self.que_logger.propagate = False
3491 self.que_logger.setLevel(logging.WARNING)
3492 self.que_logger.addHandler(self.que_hdlr)
3493
3494 def tearDown(self):
3495 self.que_hdlr.close()
3496 BaseTest.tearDown(self)
3497
3498 def test_queue_handler(self):
3499 self.que_logger.debug(self.next_message())
3500 self.assertRaises(queue.Empty, self.queue.get_nowait)
3501 self.que_logger.info(self.next_message())
3502 self.assertRaises(queue.Empty, self.queue.get_nowait)
3503 msg = self.next_message()
3504 self.que_logger.warning(msg)
3505 data = self.queue.get_nowait()
3506 self.assertTrue(isinstance(data, logging.LogRecord))
3507 self.assertEqual(data.name, self.que_logger.name)
3508 self.assertEqual((data.msg, data.args), (msg, None))
3509
favlladfe3442017-08-01 20:12:26 +02003510 def test_formatting(self):
3511 msg = self.next_message()
3512 levelname = logging.getLevelName(logging.WARNING)
3513 log_format_str = '{name} -> {levelname}: {message}'
3514 formatted_msg = log_format_str.format(name=self.name,
3515 levelname=levelname, message=msg)
3516 formatter = logging.Formatter(self.log_format)
3517 self.que_hdlr.setFormatter(formatter)
3518 self.que_logger.warning(msg)
3519 log_record = self.queue.get_nowait()
3520 self.assertEqual(formatted_msg, log_record.msg)
3521 self.assertEqual(formatted_msg, log_record.message)
3522
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003523 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3524 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003525 def test_queue_listener(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003526 handler = support.TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003527 listener = logging.handlers.QueueListener(self.queue, handler)
3528 listener.start()
3529 try:
3530 self.que_logger.warning(self.next_message())
3531 self.que_logger.error(self.next_message())
3532 self.que_logger.critical(self.next_message())
3533 finally:
3534 listener.stop()
3535 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3536 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3537 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003538 handler.close()
3539
3540 # Now test with respect_handler_level set
3541
3542 handler = support.TestHandler(support.Matcher())
3543 handler.setLevel(logging.CRITICAL)
3544 listener = logging.handlers.QueueListener(self.queue, handler,
3545 respect_handler_level=True)
3546 listener.start()
3547 try:
3548 self.que_logger.warning(self.next_message())
3549 self.que_logger.error(self.next_message())
3550 self.que_logger.critical(self.next_message())
3551 finally:
3552 listener.stop()
3553 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3554 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3555 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003556 handler.close()
3557
3558 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3559 'logging.handlers.QueueListener required for this test')
3560 def test_queue_listener_with_StreamHandler(self):
3561 # Test that traceback only appends once (bpo-34334).
3562 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3563 listener.start()
3564 try:
3565 1 / 0
3566 except ZeroDivisionError as e:
3567 exc = e
3568 self.que_logger.exception(self.next_message(), exc_info=exc)
3569 listener.stop()
3570 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003571
Xtreak2dad9602019-04-07 13:21:27 +05303572 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3573 'logging.handlers.QueueListener required for this test')
3574 def test_queue_listener_with_multiple_handlers(self):
3575 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3576 self.que_hdlr.setFormatter(self.root_formatter)
3577 self.que_logger.addHandler(self.root_hdlr)
3578
3579 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3580 listener.start()
3581 self.que_logger.error("error")
3582 listener.stop()
3583 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3584
Vinay Sajipd61910c2016-09-08 01:13:39 +01003585if hasattr(logging.handlers, 'QueueListener'):
3586 import multiprocessing
3587 from unittest.mock import patch
3588
3589 class QueueListenerTest(BaseTest):
3590 """
3591 Tests based on patch submitted for issue #27930. Ensure that
3592 QueueListener handles all log messages.
3593 """
3594
3595 repeat = 20
3596
3597 @staticmethod
3598 def setup_and_log(log_queue, ident):
3599 """
3600 Creates a logger with a QueueHandler that logs to a queue read by a
3601 QueueListener. Starts the listener, logs five messages, and stops
3602 the listener.
3603 """
3604 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3605 logger.setLevel(logging.DEBUG)
3606 handler = logging.handlers.QueueHandler(log_queue)
3607 logger.addHandler(handler)
3608 listener = logging.handlers.QueueListener(log_queue)
3609 listener.start()
3610
3611 logger.info('one')
3612 logger.info('two')
3613 logger.info('three')
3614 logger.info('four')
3615 logger.info('five')
3616
3617 listener.stop()
3618 logger.removeHandler(handler)
3619 handler.close()
3620
3621 @patch.object(logging.handlers.QueueListener, 'handle')
3622 def test_handle_called_with_queue_queue(self, mock_handle):
3623 for i in range(self.repeat):
3624 log_queue = queue.Queue()
3625 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3626 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3627 'correct number of handled log messages')
3628
3629 @patch.object(logging.handlers.QueueListener, 'handle')
3630 def test_handle_called_with_mp_queue(self, mock_handle):
xdegayebf2b65e2017-12-01 08:08:49 +01003631 # Issue 28668: The multiprocessing (mp) module is not functional
3632 # when the mp.synchronize module cannot be imported.
3633 support.import_module('multiprocessing.synchronize')
Vinay Sajipd61910c2016-09-08 01:13:39 +01003634 for i in range(self.repeat):
3635 log_queue = multiprocessing.Queue()
3636 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003637 log_queue.close()
3638 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003639 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3640 'correct number of handled log messages')
3641
3642 @staticmethod
3643 def get_all_from_queue(log_queue):
3644 try:
3645 while True:
3646 yield log_queue.get_nowait()
3647 except queue.Empty:
3648 return []
3649
3650 def test_no_messages_in_queue_after_stop(self):
3651 """
3652 Five messages are logged then the QueueListener is stopped. This
3653 test then gets everything off the queue. Failure of this test
3654 indicates that messages were not registered on the queue until
3655 _after_ the QueueListener stopped.
3656 """
xdegayebf2b65e2017-12-01 08:08:49 +01003657 # Issue 28668: The multiprocessing (mp) module is not functional
3658 # when the mp.synchronize module cannot be imported.
3659 support.import_module('multiprocessing.synchronize')
Vinay Sajipd61910c2016-09-08 01:13:39 +01003660 for i in range(self.repeat):
3661 queue = multiprocessing.Queue()
3662 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3663 # time.sleep(1)
3664 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003665 queue.close()
3666 queue.join_thread()
3667
Vinay Sajipd61910c2016-09-08 01:13:39 +01003668 expected = [[], [logging.handlers.QueueListener._sentinel]]
3669 self.assertIn(items, expected,
3670 'Found unexpected messages in queue: %s' % (
3671 [m.msg if isinstance(m, logging.LogRecord)
3672 else m for m in items]))
3673
Bar Harel6b282e12019-06-01 12:19:09 +03003674 def test_calls_task_done_after_stop(self):
3675 # Issue 36813: Make sure queue.join does not deadlock.
3676 log_queue = queue.Queue()
3677 listener = logging.handlers.QueueListener(log_queue)
3678 listener.start()
3679 listener.stop()
3680 with self.assertRaises(ValueError):
3681 # Make sure all tasks are done and .join won't block.
3682 log_queue.task_done()
3683
Vinay Sajipe723e962011-04-15 22:27:17 +01003684
Vinay Sajip37eb3382011-04-26 20:26:41 +01003685ZERO = datetime.timedelta(0)
3686
3687class UTC(datetime.tzinfo):
3688 def utcoffset(self, dt):
3689 return ZERO
3690
3691 dst = utcoffset
3692
3693 def tzname(self, dt):
3694 return 'UTC'
3695
3696utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003697
Vinay Sajipa39c5712010-10-25 13:57:39 +00003698class FormatterTest(unittest.TestCase):
3699 def setUp(self):
3700 self.common = {
3701 'name': 'formatter.test',
3702 'level': logging.DEBUG,
3703 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3704 'lineno': 42,
3705 'exc_info': None,
3706 'func': None,
3707 'msg': 'Message with %d %s',
3708 'args': (2, 'placeholders'),
3709 }
3710 self.variants = {
3711 }
3712
3713 def get_record(self, name=None):
3714 result = dict(self.common)
3715 if name is not None:
3716 result.update(self.variants[name])
3717 return logging.makeLogRecord(result)
3718
BNMetrics18fb1fb2018-10-15 19:41:36 +01003719 def assert_error_message(self, exception, message, *args, **kwargs):
3720 try:
3721 self.assertRaises(exception, *args, **kwargs)
3722 except exception as e:
3723 self.assertEqual(message, e.message)
3724
Vinay Sajipa39c5712010-10-25 13:57:39 +00003725 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003726 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003727 r = self.get_record()
3728 f = logging.Formatter('${%(message)s}')
3729 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3730 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003731 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003732 self.assertFalse(f.usesTime())
3733 f = logging.Formatter('%(asctime)s')
3734 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003735 f = logging.Formatter('%(asctime)-15s')
3736 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003737 f = logging.Formatter('%(asctime)#15s')
3738 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003739
3740 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003741 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003742 r = self.get_record()
3743 f = logging.Formatter('$%{message}%$', style='{')
3744 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3745 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003746 self.assertRaises(ValueError, f.format, r)
3747 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003748 self.assertFalse(f.usesTime())
3749 f = logging.Formatter('{asctime}', style='{')
3750 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003751 f = logging.Formatter('{asctime!s:15}', style='{')
3752 self.assertTrue(f.usesTime())
3753 f = logging.Formatter('{asctime:15}', style='{')
3754 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003755
3756 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003757 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003758 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003759 f = logging.Formatter('${message}', style='$')
3760 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003761 f = logging.Formatter('$message', style='$')
3762 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3763 f = logging.Formatter('$$%${message}%$$', style='$')
3764 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3765 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003766 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003767 self.assertFalse(f.usesTime())
3768 f = logging.Formatter('${asctime}', style='$')
3769 self.assertTrue(f.usesTime())
3770 f = logging.Formatter('$asctime', style='$')
3771 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003772 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003773 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003774 f = logging.Formatter('${asctime}--', style='$')
3775 self.assertTrue(f.usesTime())
3776
3777 def test_format_validate(self):
3778 # Check correct formatting
3779 # Percentage style
3780 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3781 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3782 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3783 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3784 f = logging.Formatter("%(process)#+027.23X")
3785 self.assertEqual(f._fmt, "%(process)#+027.23X")
3786 f = logging.Formatter("%(foo)#.*g")
3787 self.assertEqual(f._fmt, "%(foo)#.*g")
3788
3789 # StrFormat Style
3790 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3791 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3792 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3793 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3794 f = logging.Formatter("{customfield!s:#<30}", style="{")
3795 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3796 f = logging.Formatter("{message!r}", style="{")
3797 self.assertEqual(f._fmt, "{message!r}")
3798 f = logging.Formatter("{message!s}", style="{")
3799 self.assertEqual(f._fmt, "{message!s}")
3800 f = logging.Formatter("{message!a}", style="{")
3801 self.assertEqual(f._fmt, "{message!a}")
3802 f = logging.Formatter("{process!r:4.2}", style="{")
3803 self.assertEqual(f._fmt, "{process!r:4.2}")
3804 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3805 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3806 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3807 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3808 f = logging.Formatter("{foo:12.{p}}", style="{")
3809 self.assertEqual(f._fmt, "{foo:12.{p}}")
3810 f = logging.Formatter("{foo:{w}.6}", style="{")
3811 self.assertEqual(f._fmt, "{foo:{w}.6}")
3812 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3813 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3814 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3815 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3816 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3817 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3818
3819 # Dollar style
3820 f = logging.Formatter("${asctime} - $message", style="$")
3821 self.assertEqual(f._fmt, "${asctime} - $message")
3822 f = logging.Formatter("$bar $$", style="$")
3823 self.assertEqual(f._fmt, "$bar $$")
3824 f = logging.Formatter("$bar $$$$", style="$")
3825 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3826
3827 # Testing when ValueError being raised from incorrect format
3828 # Percentage Style
3829 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3830 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3831 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3832 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3833 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3834 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3835 self.assertRaises(ValueError, logging.Formatter, '${message}')
3836 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3837 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3838
3839 # StrFormat Style
3840 # Testing failure for '-' in field name
3841 self.assert_error_message(
3842 ValueError,
3843 "invalid field name/expression: 'name-thing'",
3844 logging.Formatter, "{name-thing}", style="{"
3845 )
3846 # Testing failure for style mismatch
3847 self.assert_error_message(
3848 ValueError,
3849 "invalid format: no fields",
3850 logging.Formatter, '%(asctime)s', style='{'
3851 )
3852 # Testing failure for invalid conversion
3853 self.assert_error_message(
3854 ValueError,
3855 "invalid conversion: 'Z'"
3856 )
3857 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3858 self.assert_error_message(
3859 ValueError,
3860 "invalid format: expected ':' after conversion specifier",
3861 logging.Formatter, '{asctime!aa:15}', style='{'
3862 )
3863 # Testing failure for invalid spec
3864 self.assert_error_message(
3865 ValueError,
3866 "bad specifier: '.2ff'",
3867 logging.Formatter, '{process:.2ff}', style='{'
3868 )
3869 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3870 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3871 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3872 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3873 # Testing failure for mismatch braces
3874 self.assert_error_message(
3875 ValueError,
3876 "invalid format: unmatched '{' in format spec",
3877 logging.Formatter, '{process', style='{'
3878 )
3879 self.assert_error_message(
3880 ValueError,
3881 "invalid format: unmatched '{' in format spec",
3882 logging.Formatter, 'process}', style='{'
3883 )
3884 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3885 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3886 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3887 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3888 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3889 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3890 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3891 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3892 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3893
3894 # Dollar style
3895 # Testing failure for mismatch bare $
3896 self.assert_error_message(
3897 ValueError,
3898 "invalid format: bare \'$\' not allowed",
3899 logging.Formatter, '$bar $$$', style='$'
3900 )
3901 self.assert_error_message(
3902 ValueError,
3903 "invalid format: bare \'$\' not allowed",
3904 logging.Formatter, 'bar $', style='$'
3905 )
3906 self.assert_error_message(
3907 ValueError,
3908 "invalid format: bare \'$\' not allowed",
3909 logging.Formatter, 'foo $.', style='$'
3910 )
3911 # Testing failure for mismatch style
3912 self.assert_error_message(
3913 ValueError,
3914 "invalid format: no fields",
3915 logging.Formatter, '{asctime}', style='$'
3916 )
3917 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3918
3919 # Testing failure for incorrect fields
3920 self.assert_error_message(
3921 ValueError,
3922 "invalid format: no fields",
3923 logging.Formatter, 'foo', style='$'
3924 )
3925 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003926
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003927 def test_invalid_style(self):
3928 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3929
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003930 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003931 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003932 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3933 # We use None to indicate we want the local timezone
3934 # We're essentially converting a UTC time to local time
3935 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003936 r.msecs = 123
3937 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003938 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003939 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3940 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003941 f.format(r)
3942 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
3943
Mariusz Felisiak06a35542020-04-17 18:02:47 +02003944 def test_default_msec_format_none(self):
3945 class NoMsecFormatter(logging.Formatter):
3946 default_msec_format = None
3947 default_time_format = '%d/%m/%Y %H:%M:%S'
3948
3949 r = self.get_record()
3950 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
3951 r.created = time.mktime(dt.astimezone(None).timetuple())
3952 f = NoMsecFormatter()
3953 f.converter = time.gmtime
3954 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
3955
3956
Vinay Sajip985ef872011-04-26 19:34:04 +01003957class TestBufferingFormatter(logging.BufferingFormatter):
3958 def formatHeader(self, records):
3959 return '[(%d)' % len(records)
3960
3961 def formatFooter(self, records):
3962 return '(%d)]' % len(records)
3963
3964class BufferingFormatterTest(unittest.TestCase):
3965 def setUp(self):
3966 self.records = [
3967 logging.makeLogRecord({'msg': 'one'}),
3968 logging.makeLogRecord({'msg': 'two'}),
3969 ]
3970
3971 def test_default(self):
3972 f = logging.BufferingFormatter()
3973 self.assertEqual('', f.format([]))
3974 self.assertEqual('onetwo', f.format(self.records))
3975
3976 def test_custom(self):
3977 f = TestBufferingFormatter()
3978 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
3979 lf = logging.Formatter('<%(message)s>')
3980 f = TestBufferingFormatter(lf)
3981 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003982
3983class ExceptionTest(BaseTest):
3984 def test_formatting(self):
3985 r = self.root_logger
3986 h = RecordingHandler()
3987 r.addHandler(h)
3988 try:
3989 raise RuntimeError('deliberate mistake')
3990 except:
3991 logging.exception('failed', stack_info=True)
3992 r.removeHandler(h)
3993 h.close()
3994 r = h.records[0]
3995 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
3996 'call last):\n'))
3997 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
3998 'deliberate mistake'))
3999 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4000 'call last):\n'))
4001 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4002 'stack_info=True)'))
4003
4004
Vinay Sajip5a27d402010-12-10 11:42:57 +00004005class LastResortTest(BaseTest):
4006 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004007 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004008 root = self.root_logger
4009 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004010 old_lastresort = logging.lastResort
4011 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004012
Vinay Sajip5a27d402010-12-10 11:42:57 +00004013 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004014 with support.captured_stderr() as stderr:
4015 root.debug('This should not appear')
4016 self.assertEqual(stderr.getvalue(), '')
4017 root.warning('Final chance!')
4018 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4019
4020 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004021 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004022 with support.captured_stderr() as stderr:
4023 root.warning('Final chance!')
4024 msg = 'No handlers could be found for logger "root"\n'
4025 self.assertEqual(stderr.getvalue(), msg)
4026
Vinay Sajip5a27d402010-12-10 11:42:57 +00004027 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004028 with support.captured_stderr() as stderr:
4029 root.warning('Final chance!')
4030 self.assertEqual(stderr.getvalue(), '')
4031
4032 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004033 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004034 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004035 with support.captured_stderr() as stderr:
4036 root.warning('Final chance!')
4037 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004038 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004039 root.addHandler(self.root_hdlr)
4040 logging.lastResort = old_lastresort
4041 logging.raiseExceptions = old_raise_exceptions
4042
4043
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004044class FakeHandler:
4045
4046 def __init__(self, identifier, called):
4047 for method in ('acquire', 'flush', 'close', 'release'):
4048 setattr(self, method, self.record_call(identifier, method, called))
4049
4050 def record_call(self, identifier, method_name, called):
4051 def inner():
4052 called.append('{} - {}'.format(identifier, method_name))
4053 return inner
4054
4055
4056class RecordingHandler(logging.NullHandler):
4057
4058 def __init__(self, *args, **kwargs):
4059 super(RecordingHandler, self).__init__(*args, **kwargs)
4060 self.records = []
4061
4062 def handle(self, record):
4063 """Keep track of all the emitted records."""
4064 self.records.append(record)
4065
4066
4067class ShutdownTest(BaseTest):
4068
Vinay Sajip5e66b162011-04-20 15:41:14 +01004069 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004070
4071 def setUp(self):
4072 super(ShutdownTest, self).setUp()
4073 self.called = []
4074
4075 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004076 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004077
4078 def raise_error(self, error):
4079 def inner():
4080 raise error()
4081 return inner
4082
4083 def test_no_failure(self):
4084 # create some fake handlers
4085 handler0 = FakeHandler(0, self.called)
4086 handler1 = FakeHandler(1, self.called)
4087 handler2 = FakeHandler(2, self.called)
4088
4089 # create live weakref to those handlers
4090 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4091
4092 logging.shutdown(handlerList=list(handlers))
4093
4094 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4095 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4096 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4097 self.assertEqual(expected, self.called)
4098
4099 def _test_with_failure_in_method(self, method, error):
4100 handler = FakeHandler(0, self.called)
4101 setattr(handler, method, self.raise_error(error))
4102 handlers = [logging.weakref.ref(handler)]
4103
4104 logging.shutdown(handlerList=list(handlers))
4105
4106 self.assertEqual('0 - release', self.called[-1])
4107
4108 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004109 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004110
4111 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004112 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004113
4114 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004115 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004116
4117 def test_with_valueerror_in_acquire(self):
4118 self._test_with_failure_in_method('acquire', ValueError)
4119
4120 def test_with_valueerror_in_flush(self):
4121 self._test_with_failure_in_method('flush', ValueError)
4122
4123 def test_with_valueerror_in_close(self):
4124 self._test_with_failure_in_method('close', ValueError)
4125
4126 def test_with_other_error_in_acquire_without_raise(self):
4127 logging.raiseExceptions = False
4128 self._test_with_failure_in_method('acquire', IndexError)
4129
4130 def test_with_other_error_in_flush_without_raise(self):
4131 logging.raiseExceptions = False
4132 self._test_with_failure_in_method('flush', IndexError)
4133
4134 def test_with_other_error_in_close_without_raise(self):
4135 logging.raiseExceptions = False
4136 self._test_with_failure_in_method('close', IndexError)
4137
4138 def test_with_other_error_in_acquire_with_raise(self):
4139 logging.raiseExceptions = True
4140 self.assertRaises(IndexError, self._test_with_failure_in_method,
4141 'acquire', IndexError)
4142
4143 def test_with_other_error_in_flush_with_raise(self):
4144 logging.raiseExceptions = True
4145 self.assertRaises(IndexError, self._test_with_failure_in_method,
4146 'flush', IndexError)
4147
4148 def test_with_other_error_in_close_with_raise(self):
4149 logging.raiseExceptions = True
4150 self.assertRaises(IndexError, self._test_with_failure_in_method,
4151 'close', IndexError)
4152
4153
4154class ModuleLevelMiscTest(BaseTest):
4155
Vinay Sajip5e66b162011-04-20 15:41:14 +01004156 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004157
4158 def test_disable(self):
4159 old_disable = logging.root.manager.disable
4160 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004161 self.assertEqual(old_disable, 0)
4162 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004163
4164 logging.disable(83)
4165 self.assertEqual(logging.root.manager.disable, 83)
4166
Vinay Sajipd489ac92016-12-31 11:40:11 +00004167 # test the default value introduced in 3.7
4168 # (Issue #28524)
4169 logging.disable()
4170 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4171
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004172 def _test_log(self, method, level=None):
4173 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004174 support.patch(self, logging, 'basicConfig',
4175 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004176
4177 recording = RecordingHandler()
4178 logging.root.addHandler(recording)
4179
4180 log_method = getattr(logging, method)
4181 if level is not None:
4182 log_method(level, "test me: %r", recording)
4183 else:
4184 log_method("test me: %r", recording)
4185
4186 self.assertEqual(len(recording.records), 1)
4187 record = recording.records[0]
4188 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4189
4190 expected_level = level if level is not None else getattr(logging, method.upper())
4191 self.assertEqual(record.levelno, expected_level)
4192
4193 # basicConfig was not called!
4194 self.assertEqual(called, [])
4195
4196 def test_log(self):
4197 self._test_log('log', logging.ERROR)
4198
4199 def test_debug(self):
4200 self._test_log('debug')
4201
4202 def test_info(self):
4203 self._test_log('info')
4204
4205 def test_warning(self):
4206 self._test_log('warning')
4207
4208 def test_error(self):
4209 self._test_log('error')
4210
4211 def test_critical(self):
4212 self._test_log('critical')
4213
4214 def test_set_logger_class(self):
4215 self.assertRaises(TypeError, logging.setLoggerClass, object)
4216
4217 class MyLogger(logging.Logger):
4218 pass
4219
4220 logging.setLoggerClass(MyLogger)
4221 self.assertEqual(logging.getLoggerClass(), MyLogger)
4222
4223 logging.setLoggerClass(logging.Logger)
4224 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4225
Vinay Sajip01500012019-06-19 11:46:53 +01004226 def test_subclass_logger_cache(self):
4227 # bpo-37258
4228 message = []
4229
4230 class MyLogger(logging.getLoggerClass()):
4231 def __init__(self, name='MyLogger', level=logging.NOTSET):
4232 super().__init__(name, level)
4233 message.append('initialized')
4234
4235 logging.setLoggerClass(MyLogger)
4236 logger = logging.getLogger('just_some_logger')
4237 self.assertEqual(message, ['initialized'])
4238 stream = io.StringIO()
4239 h = logging.StreamHandler(stream)
4240 logger.addHandler(h)
4241 try:
4242 logger.setLevel(logging.DEBUG)
4243 logger.debug("hello")
4244 self.assertEqual(stream.getvalue().strip(), "hello")
4245
4246 stream.truncate(0)
4247 stream.seek(0)
4248
4249 logger.setLevel(logging.INFO)
4250 logger.debug("hello")
4251 self.assertEqual(stream.getvalue(), "")
4252 finally:
4253 logger.removeHandler(h)
4254 h.close()
4255 logging.setLoggerClass(logging.Logger)
4256
Antoine Pitrou712cb732013-12-21 15:51:54 +01004257 def test_logging_at_shutdown(self):
4258 # Issue #20037
4259 code = """if 1:
4260 import logging
4261
4262 class A:
4263 def __del__(self):
4264 try:
4265 raise ValueError("some error")
4266 except Exception:
4267 logging.exception("exception in __del__")
4268
4269 a = A()"""
4270 rc, out, err = assert_python_ok("-c", code)
4271 err = err.decode()
4272 self.assertIn("exception in __del__", err)
4273 self.assertIn("ValueError: some error", err)
4274
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004275 def test_recursion_error(self):
4276 # Issue 36272
4277 code = """if 1:
4278 import logging
4279
4280 def rec():
4281 logging.error("foo")
4282 rec()
4283
4284 rec()"""
4285 rc, out, err = assert_python_failure("-c", code)
4286 err = err.decode()
4287 self.assertNotIn("Cannot recover from stack overflow.", err)
4288 self.assertEqual(rc, 1)
4289
Antoine Pitrou712cb732013-12-21 15:51:54 +01004290
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004291class LogRecordTest(BaseTest):
4292 def test_str_rep(self):
4293 r = logging.makeLogRecord({})
4294 s = str(r)
4295 self.assertTrue(s.startswith('<LogRecord: '))
4296 self.assertTrue(s.endswith('>'))
4297
4298 def test_dict_arg(self):
4299 h = RecordingHandler()
4300 r = logging.getLogger()
4301 r.addHandler(h)
4302 d = {'less' : 'more' }
4303 logging.warning('less is %(less)s', d)
4304 self.assertIs(h.records[0].args, d)
4305 self.assertEqual(h.records[0].message, 'less is more')
4306 r.removeHandler(h)
4307 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004308
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004309 def test_multiprocessing(self):
4310 r = logging.makeLogRecord({})
4311 self.assertEqual(r.processName, 'MainProcess')
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004312 try:
4313 import multiprocessing as mp
4314 r = logging.makeLogRecord({})
4315 self.assertEqual(r.processName, mp.current_process().name)
4316 except ImportError:
4317 pass
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004318
4319 def test_optional(self):
4320 r = logging.makeLogRecord({})
4321 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004322 NOT_NONE(r.thread)
4323 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004324 NOT_NONE(r.process)
4325 NOT_NONE(r.processName)
4326 log_threads = logging.logThreads
4327 log_processes = logging.logProcesses
4328 log_multiprocessing = logging.logMultiprocessing
4329 try:
4330 logging.logThreads = False
4331 logging.logProcesses = False
4332 logging.logMultiprocessing = False
4333 r = logging.makeLogRecord({})
4334 NONE = self.assertIsNone
4335 NONE(r.thread)
4336 NONE(r.threadName)
4337 NONE(r.process)
4338 NONE(r.processName)
4339 finally:
4340 logging.logThreads = log_threads
4341 logging.logProcesses = log_processes
4342 logging.logMultiprocessing = log_multiprocessing
4343
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004344class BasicConfigTest(unittest.TestCase):
4345
Vinay Sajip95bf5042011-04-20 11:50:56 +01004346 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004347
4348 def setUp(self):
4349 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004350 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004351 self.saved_handlers = logging._handlers.copy()
4352 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004353 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004354 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004355 logging.root.handlers = []
4356
4357 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004358 for h in logging.root.handlers[:]:
4359 logging.root.removeHandler(h)
4360 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004361 super(BasicConfigTest, self).tearDown()
4362
Vinay Sajip3def7e02011-04-20 10:58:06 +01004363 def cleanup(self):
4364 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004365 logging._handlers.clear()
4366 logging._handlers.update(self.saved_handlers)
4367 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004368 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004369
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004370 def test_no_kwargs(self):
4371 logging.basicConfig()
4372
4373 # handler defaults to a StreamHandler to sys.stderr
4374 self.assertEqual(len(logging.root.handlers), 1)
4375 handler = logging.root.handlers[0]
4376 self.assertIsInstance(handler, logging.StreamHandler)
4377 self.assertEqual(handler.stream, sys.stderr)
4378
4379 formatter = handler.formatter
4380 # format defaults to logging.BASIC_FORMAT
4381 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4382 # datefmt defaults to None
4383 self.assertIsNone(formatter.datefmt)
4384 # style defaults to %
4385 self.assertIsInstance(formatter._style, logging.PercentStyle)
4386
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004387 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004388 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004389
Vinay Sajip1fd12022014-01-13 21:59:56 +00004390 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004391 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004392 logging.basicConfig(stream=sys.stdout, style="{")
4393 logging.error("Log an error")
4394 sys.stdout.seek(0)
4395 self.assertEqual(output.getvalue().strip(),
4396 "ERROR:root:Log an error")
4397
4398 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004399 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004400 logging.basicConfig(stream=sys.stdout, style="$")
4401 logging.error("Log an error")
4402 sys.stdout.seek(0)
4403 self.assertEqual(output.getvalue().strip(),
4404 "ERROR:root:Log an error")
4405
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004406 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004407
4408 def cleanup(h1, h2, fn):
4409 h1.close()
4410 h2.close()
4411 os.remove(fn)
4412
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004413 logging.basicConfig(filename='test.log')
4414
4415 self.assertEqual(len(logging.root.handlers), 1)
4416 handler = logging.root.handlers[0]
4417 self.assertIsInstance(handler, logging.FileHandler)
4418
4419 expected = logging.FileHandler('test.log', 'a')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004420 self.assertEqual(handler.stream.mode, expected.stream.mode)
4421 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004422 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004423
4424 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004425
4426 def cleanup(h1, h2, fn):
4427 h1.close()
4428 h2.close()
4429 os.remove(fn)
4430
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004431 logging.basicConfig(filename='test.log', filemode='wb')
4432
4433 handler = logging.root.handlers[0]
4434 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004435 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004436 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004437
4438 def test_stream(self):
4439 stream = io.StringIO()
4440 self.addCleanup(stream.close)
4441 logging.basicConfig(stream=stream)
4442
4443 self.assertEqual(len(logging.root.handlers), 1)
4444 handler = logging.root.handlers[0]
4445 self.assertIsInstance(handler, logging.StreamHandler)
4446 self.assertEqual(handler.stream, stream)
4447
4448 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004449 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004450
4451 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004452 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004453
4454 def test_datefmt(self):
4455 logging.basicConfig(datefmt='bar')
4456
4457 formatter = logging.root.handlers[0].formatter
4458 self.assertEqual(formatter.datefmt, 'bar')
4459
4460 def test_style(self):
4461 logging.basicConfig(style='$')
4462
4463 formatter = logging.root.handlers[0].formatter
4464 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4465
4466 def test_level(self):
4467 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004468 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004469
4470 logging.basicConfig(level=57)
4471 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004472 # Test that second call has no effect
4473 logging.basicConfig(level=58)
4474 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004475
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004476 def test_incompatible(self):
4477 assertRaises = self.assertRaises
4478 handlers = [logging.StreamHandler()]
4479 stream = sys.stderr
4480 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004481 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004482 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004483 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004484 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004485 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004486 # Issue 23207: test for invalid kwargs
4487 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4488 # Should pop both filename and filemode even if filename is None
4489 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004490
4491 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004492 handlers = [
4493 logging.StreamHandler(),
4494 logging.StreamHandler(sys.stdout),
4495 logging.StreamHandler(),
4496 ]
4497 f = logging.Formatter()
4498 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004499 logging.basicConfig(handlers=handlers)
4500 self.assertIs(handlers[0], logging.root.handlers[0])
4501 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004502 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004503 self.assertIsNotNone(handlers[0].formatter)
4504 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004505 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004506 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4507
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004508 def test_force(self):
4509 old_string_io = io.StringIO()
4510 new_string_io = io.StringIO()
4511 old_handlers = [logging.StreamHandler(old_string_io)]
4512 new_handlers = [logging.StreamHandler(new_string_io)]
4513 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4514 logging.warning('warn')
4515 logging.info('info')
4516 logging.debug('debug')
4517 self.assertEqual(len(logging.root.handlers), 1)
4518 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4519 force=True)
4520 logging.warning('warn')
4521 logging.info('info')
4522 logging.debug('debug')
4523 self.assertEqual(len(logging.root.handlers), 1)
4524 self.assertEqual(old_string_io.getvalue().strip(),
4525 'WARNING:root:warn')
4526 self.assertEqual(new_string_io.getvalue().strip(),
4527 'WARNING:root:warn\nINFO:root:info')
4528
Vinay Sajipca7b5042019-06-17 17:40:52 +01004529 def test_encoding(self):
4530 try:
4531 encoding = 'utf-8'
4532 logging.basicConfig(filename='test.log', encoding=encoding,
4533 errors='strict',
4534 format='%(message)s', level=logging.DEBUG)
4535
4536 self.assertEqual(len(logging.root.handlers), 1)
4537 handler = logging.root.handlers[0]
4538 self.assertIsInstance(handler, logging.FileHandler)
4539 self.assertEqual(handler.encoding, encoding)
4540 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4541 finally:
4542 handler.close()
4543 with open('test.log', encoding='utf-8') as f:
4544 data = f.read().strip()
4545 os.remove('test.log')
4546 self.assertEqual(data,
4547 'The Øresund Bridge joins Copenhagen to Malmö')
4548
4549 def test_encoding_errors(self):
4550 try:
4551 encoding = 'ascii'
4552 logging.basicConfig(filename='test.log', encoding=encoding,
4553 errors='ignore',
4554 format='%(message)s', level=logging.DEBUG)
4555
4556 self.assertEqual(len(logging.root.handlers), 1)
4557 handler = logging.root.handlers[0]
4558 self.assertIsInstance(handler, logging.FileHandler)
4559 self.assertEqual(handler.encoding, encoding)
4560 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4561 finally:
4562 handler.close()
4563 with open('test.log', encoding='utf-8') as f:
4564 data = f.read().strip()
4565 os.remove('test.log')
4566 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4567
4568 def test_encoding_errors_default(self):
4569 try:
4570 encoding = 'ascii'
4571 logging.basicConfig(filename='test.log', encoding=encoding,
4572 format='%(message)s', level=logging.DEBUG)
4573
4574 self.assertEqual(len(logging.root.handlers), 1)
4575 handler = logging.root.handlers[0]
4576 self.assertIsInstance(handler, logging.FileHandler)
4577 self.assertEqual(handler.encoding, encoding)
4578 self.assertEqual(handler.errors, 'backslashreplace')
4579 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4580 finally:
4581 handler.close()
4582 with open('test.log', encoding='utf-8') as f:
4583 data = f.read().strip()
4584 os.remove('test.log')
4585 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4586 r'Bridge joins Copenhagen to Malm\xf6')
4587
4588 def test_encoding_errors_none(self):
4589 # Specifying None should behave as 'strict'
4590 try:
4591 encoding = 'ascii'
4592 logging.basicConfig(filename='test.log', encoding=encoding,
4593 errors=None,
4594 format='%(message)s', level=logging.DEBUG)
4595
4596 self.assertEqual(len(logging.root.handlers), 1)
4597 handler = logging.root.handlers[0]
4598 self.assertIsInstance(handler, logging.FileHandler)
4599 self.assertEqual(handler.encoding, encoding)
4600 self.assertIsNone(handler.errors)
4601
4602 message = []
4603
4604 def dummy_handle_error(record):
4605 _, v, _ = sys.exc_info()
4606 message.append(str(v))
4607
4608 handler.handleError = dummy_handle_error
4609 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4610 self.assertTrue(message)
4611 self.assertIn("'ascii' codec can't encode "
4612 "character '\\xd8' in position 4:", message[0])
4613 finally:
4614 handler.close()
4615 with open('test.log', encoding='utf-8') as f:
4616 data = f.read().strip()
4617 os.remove('test.log')
4618 # didn't write anything due to the encoding error
4619 self.assertEqual(data, r'')
4620
4621
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004622 def _test_log(self, method, level=None):
4623 # logging.root has no handlers so basicConfig should be called
4624 called = []
4625
4626 old_basic_config = logging.basicConfig
4627 def my_basic_config(*a, **kw):
4628 old_basic_config()
4629 old_level = logging.root.level
4630 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004631 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004632 called.append((a, kw))
4633
Vinay Sajip1feedb42014-05-31 08:19:12 +01004634 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004635
4636 log_method = getattr(logging, method)
4637 if level is not None:
4638 log_method(level, "test me")
4639 else:
4640 log_method("test me")
4641
4642 # basicConfig was called with no arguments
4643 self.assertEqual(called, [((), {})])
4644
4645 def test_log(self):
4646 self._test_log('log', logging.WARNING)
4647
4648 def test_debug(self):
4649 self._test_log('debug')
4650
4651 def test_info(self):
4652 self._test_log('info')
4653
4654 def test_warning(self):
4655 self._test_log('warning')
4656
4657 def test_error(self):
4658 self._test_log('error')
4659
4660 def test_critical(self):
4661 self._test_log('critical')
4662
4663
4664class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004665 def setUp(self):
4666 super(LoggerAdapterTest, self).setUp()
4667 old_handler_list = logging._handlerList[:]
4668
4669 self.recording = RecordingHandler()
4670 self.logger = logging.root
4671 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004672 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004673 self.addCleanup(self.recording.close)
4674
4675 def cleanup():
4676 logging._handlerList[:] = old_handler_list
4677
4678 self.addCleanup(cleanup)
4679 self.addCleanup(logging.shutdown)
4680 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4681
4682 def test_exception(self):
4683 msg = 'testing exception: %r'
4684 exc = None
4685 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004686 1 / 0
4687 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004688 exc = e
4689 self.adapter.exception(msg, self.recording)
4690
4691 self.assertEqual(len(self.recording.records), 1)
4692 record = self.recording.records[0]
4693 self.assertEqual(record.levelno, logging.ERROR)
4694 self.assertEqual(record.msg, msg)
4695 self.assertEqual(record.args, (self.recording,))
4696 self.assertEqual(record.exc_info,
4697 (exc.__class__, exc, exc.__traceback__))
4698
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004699 def test_exception_excinfo(self):
4700 try:
4701 1 / 0
4702 except ZeroDivisionError as e:
4703 exc = e
4704
4705 self.adapter.exception('exc_info test', exc_info=exc)
4706
4707 self.assertEqual(len(self.recording.records), 1)
4708 record = self.recording.records[0]
4709 self.assertEqual(record.exc_info,
4710 (exc.__class__, exc, exc.__traceback__))
4711
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004712 def test_critical(self):
4713 msg = 'critical test! %r'
4714 self.adapter.critical(msg, self.recording)
4715
4716 self.assertEqual(len(self.recording.records), 1)
4717 record = self.recording.records[0]
4718 self.assertEqual(record.levelno, logging.CRITICAL)
4719 self.assertEqual(record.msg, msg)
4720 self.assertEqual(record.args, (self.recording,))
4721
4722 def test_is_enabled_for(self):
4723 old_disable = self.adapter.logger.manager.disable
4724 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004725 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4726 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004727 self.assertFalse(self.adapter.isEnabledFor(32))
4728
4729 def test_has_handlers(self):
4730 self.assertTrue(self.adapter.hasHandlers())
4731
4732 for handler in self.logger.handlers:
4733 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004734
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004735 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004736 self.assertFalse(self.adapter.hasHandlers())
4737
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004738 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004739 class Adapter(logging.LoggerAdapter):
4740 prefix = 'Adapter'
4741
4742 def process(self, msg, kwargs):
4743 return f"{self.prefix} {msg}", kwargs
4744
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004745 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004746 adapter = Adapter(logger=self.logger, extra=None)
4747 adapter_adapter = Adapter(logger=adapter, extra=None)
4748 adapter_adapter.prefix = 'AdapterAdapter'
4749 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004750 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004751 self.assertEqual(len(self.recording.records), 1)
4752 record = self.recording.records[0]
4753 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004754 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004755 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004756 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004757 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004758 self.assertIs(self.logger.manager, orig_manager)
4759 temp_manager = object()
4760 try:
4761 adapter_adapter.manager = temp_manager
4762 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004763 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004764 self.assertIs(self.logger.manager, temp_manager)
4765 finally:
4766 adapter_adapter.manager = orig_manager
4767 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004768 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004769 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004770
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004771
4772class LoggerTest(BaseTest):
4773
4774 def setUp(self):
4775 super(LoggerTest, self).setUp()
4776 self.recording = RecordingHandler()
4777 self.logger = logging.Logger(name='blah')
4778 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004779 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004780 self.addCleanup(self.recording.close)
4781 self.addCleanup(logging.shutdown)
4782
4783 def test_set_invalid_level(self):
4784 self.assertRaises(TypeError, self.logger.setLevel, object())
4785
4786 def test_exception(self):
4787 msg = 'testing exception: %r'
4788 exc = None
4789 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004790 1 / 0
4791 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004792 exc = e
4793 self.logger.exception(msg, self.recording)
4794
4795 self.assertEqual(len(self.recording.records), 1)
4796 record = self.recording.records[0]
4797 self.assertEqual(record.levelno, logging.ERROR)
4798 self.assertEqual(record.msg, msg)
4799 self.assertEqual(record.args, (self.recording,))
4800 self.assertEqual(record.exc_info,
4801 (exc.__class__, exc, exc.__traceback__))
4802
4803 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004804 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004805 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004806
4807 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004808 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004809 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004810
4811 def test_find_caller_with_stack_info(self):
4812 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004813 support.patch(self, logging.traceback, 'print_stack',
4814 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004815
4816 self.logger.findCaller(stack_info=True)
4817
4818 self.assertEqual(len(called), 1)
4819 self.assertEqual('Stack (most recent call last):\n', called[0])
4820
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004821 def test_find_caller_with_stacklevel(self):
4822 the_level = 1
4823
4824 def innermost():
4825 self.logger.warning('test', stacklevel=the_level)
4826
4827 def inner():
4828 innermost()
4829
4830 def outer():
4831 inner()
4832
4833 records = self.recording.records
4834 outer()
4835 self.assertEqual(records[-1].funcName, 'innermost')
4836 lineno = records[-1].lineno
4837 the_level += 1
4838 outer()
4839 self.assertEqual(records[-1].funcName, 'inner')
4840 self.assertGreater(records[-1].lineno, lineno)
4841 lineno = records[-1].lineno
4842 the_level += 1
4843 outer()
4844 self.assertEqual(records[-1].funcName, 'outer')
4845 self.assertGreater(records[-1].lineno, lineno)
4846 lineno = records[-1].lineno
4847 the_level += 1
4848 outer()
4849 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
4850 self.assertGreater(records[-1].lineno, lineno)
4851
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004852 def test_make_record_with_extra_overwrite(self):
4853 name = 'my record'
4854 level = 13
4855 fn = lno = msg = args = exc_info = func = sinfo = None
4856 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
4857 exc_info, func, sinfo)
4858
4859 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
4860 extra = {key: 'some value'}
4861 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
4862 fn, lno, msg, args, exc_info,
4863 extra=extra, sinfo=sinfo)
4864
4865 def test_make_record_with_extra_no_overwrite(self):
4866 name = 'my record'
4867 level = 13
4868 fn = lno = msg = args = exc_info = func = sinfo = None
4869 extra = {'valid_key': 'some value'}
4870 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
4871 exc_info, extra=extra, sinfo=sinfo)
4872 self.assertIn('valid_key', result.__dict__)
4873
4874 def test_has_handlers(self):
4875 self.assertTrue(self.logger.hasHandlers())
4876
4877 for handler in self.logger.handlers:
4878 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004879 self.assertFalse(self.logger.hasHandlers())
4880
4881 def test_has_handlers_no_propagate(self):
4882 child_logger = logging.getLogger('blah.child')
4883 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004884 self.assertFalse(child_logger.hasHandlers())
4885
4886 def test_is_enabled_for(self):
4887 old_disable = self.logger.manager.disable
4888 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004889 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004890 self.assertFalse(self.logger.isEnabledFor(22))
4891
Timo Furrer6e3ca642018-06-01 09:29:46 +02004892 def test_is_enabled_for_disabled_logger(self):
4893 old_disabled = self.logger.disabled
4894 old_disable = self.logger.manager.disable
4895
4896 self.logger.disabled = True
4897 self.logger.manager.disable = 21
4898
4899 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
4900 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
4901
4902 self.assertFalse(self.logger.isEnabledFor(22))
4903
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004904 def test_root_logger_aliases(self):
4905 root = logging.getLogger()
4906 self.assertIs(root, logging.root)
4907 self.assertIs(root, logging.getLogger(None))
4908 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01004909 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01004910 self.assertIs(root, logging.getLogger('foo').root)
4911 self.assertIs(root, logging.getLogger('foo.bar').root)
4912 self.assertIs(root, logging.getLogger('foo').parent)
4913
4914 self.assertIsNot(root, logging.getLogger('\0'))
4915 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
4916
4917 def test_invalid_names(self):
4918 self.assertRaises(TypeError, logging.getLogger, any)
4919 self.assertRaises(TypeError, logging.getLogger, b'foo')
4920
Vinay Sajip6260d9f2017-06-06 16:34:29 +01004921 def test_pickling(self):
4922 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
4923 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
4924 logger = logging.getLogger(name)
4925 s = pickle.dumps(logger, proto)
4926 unpickled = pickle.loads(s)
4927 self.assertIs(unpickled, logger)
4928
Avram Lubkin78c18a92017-07-30 05:36:33 -04004929 def test_caching(self):
4930 root = self.root_logger
4931 logger1 = logging.getLogger("abc")
4932 logger2 = logging.getLogger("abc.def")
4933
4934 # Set root logger level and ensure cache is empty
4935 root.setLevel(logging.ERROR)
4936 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
4937 self.assertEqual(logger2._cache, {})
4938
4939 # Ensure cache is populated and calls are consistent
4940 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4941 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
4942 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
4943 self.assertEqual(root._cache, {})
4944 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
4945
4946 # Ensure root cache gets populated
4947 self.assertEqual(root._cache, {})
4948 self.assertTrue(root.isEnabledFor(logging.ERROR))
4949 self.assertEqual(root._cache, {logging.ERROR: True})
4950
4951 # Set parent logger level and ensure caches are emptied
4952 logger1.setLevel(logging.CRITICAL)
4953 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4954 self.assertEqual(logger2._cache, {})
4955
4956 # Ensure logger2 uses parent logger's effective level
4957 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4958
4959 # Set level to NOTSET and ensure caches are empty
4960 logger2.setLevel(logging.NOTSET)
4961 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4962 self.assertEqual(logger2._cache, {})
4963 self.assertEqual(logger1._cache, {})
4964 self.assertEqual(root._cache, {})
4965
4966 # Verify logger2 follows parent and not root
4967 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
4968 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
4969 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
4970 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
4971 self.assertTrue(root.isEnabledFor(logging.ERROR))
4972
4973 # Disable logging in manager and ensure caches are clear
4974 logging.disable()
4975 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
4976 self.assertEqual(logger2._cache, {})
4977 self.assertEqual(logger1._cache, {})
4978 self.assertEqual(root._cache, {})
4979
4980 # Ensure no loggers are enabled
4981 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
4982 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
4983 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
4984
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004985
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004986class BaseFileTest(BaseTest):
4987 "Base class for handler tests that write log files"
4988
4989 def setUp(self):
4990 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00004991 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
4992 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00004993 self.rmfiles = []
4994
4995 def tearDown(self):
4996 for fn in self.rmfiles:
4997 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00004998 if os.path.exists(self.fn):
4999 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005000 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005001
5002 def assertLogFile(self, filename):
5003 "Assert a log file is there and register it for deletion"
5004 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005005 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005006 self.rmfiles.append(filename)
5007
5008
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005009class FileHandlerTest(BaseFileTest):
5010 def test_delay(self):
5011 os.unlink(self.fn)
5012 fh = logging.FileHandler(self.fn, delay=True)
5013 self.assertIsNone(fh.stream)
5014 self.assertFalse(os.path.exists(self.fn))
5015 fh.handle(logging.makeLogRecord({}))
5016 self.assertIsNotNone(fh.stream)
5017 self.assertTrue(os.path.exists(self.fn))
5018 fh.close()
5019
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005020class RotatingFileHandlerTest(BaseFileTest):
5021 def next_rec(self):
5022 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5023 self.next_message(), None, None, None)
5024
5025 def test_should_not_rollover(self):
5026 # If maxbytes is zero rollover never occurs
5027 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
5028 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005029 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005030
5031 def test_should_rollover(self):
5032 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
5033 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005034 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005035
5036 def test_file_created(self):
5037 # checks that the file is created and assumes it was created
5038 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005039 rh = logging.handlers.RotatingFileHandler(self.fn)
5040 rh.emit(self.next_rec())
5041 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005042 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005043
5044 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005045 def namer(name):
5046 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005047 rh = logging.handlers.RotatingFileHandler(
5048 self.fn, backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005049 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005050 rh.emit(self.next_rec())
5051 self.assertLogFile(self.fn)
5052 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005053 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005054 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005055 self.assertLogFile(namer(self.fn + ".2"))
5056 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5057 rh.close()
5058
l0rb519cb872019-11-06 22:21:40 +01005059 def test_namer_rotator_inheritance(self):
5060 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5061 def namer(self, name):
5062 return name + ".test"
5063
5064 def rotator(self, source, dest):
5065 if os.path.exists(source):
5066 os.rename(source, dest + ".rotated")
5067
5068 rh = HandlerWithNamerAndRotator(
5069 self.fn, backupCount=2, maxBytes=1)
5070 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5071 rh.emit(self.next_rec())
5072 self.assertLogFile(self.fn)
5073 rh.emit(self.next_rec())
5074 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5075 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5076 rh.close()
5077
Vinay Sajip1feedb42014-05-31 08:19:12 +01005078 @support.requires_zlib
Vinay Sajip23b94d02012-01-04 12:02:26 +00005079 def test_rotator(self):
5080 def namer(name):
5081 return name + ".gz"
5082
5083 def rotator(source, dest):
5084 with open(source, "rb") as sf:
5085 data = sf.read()
5086 compressed = zlib.compress(data, 9)
5087 with open(dest, "wb") as df:
5088 df.write(compressed)
5089 os.remove(source)
5090
5091 rh = logging.handlers.RotatingFileHandler(
5092 self.fn, backupCount=2, maxBytes=1)
5093 rh.rotator = rotator
5094 rh.namer = namer
5095 m1 = self.next_rec()
5096 rh.emit(m1)
5097 self.assertLogFile(self.fn)
5098 m2 = self.next_rec()
5099 rh.emit(m2)
5100 fn = namer(self.fn + ".1")
5101 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005102 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005103 with open(fn, "rb") as f:
5104 compressed = f.read()
5105 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005106 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005107 rh.emit(self.next_rec())
5108 fn = namer(self.fn + ".2")
5109 self.assertLogFile(fn)
5110 with open(fn, "rb") as f:
5111 compressed = f.read()
5112 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005113 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005114 rh.emit(self.next_rec())
5115 fn = namer(self.fn + ".2")
5116 with open(fn, "rb") as f:
5117 compressed = f.read()
5118 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005119 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005120 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005121 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005122
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005123class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005124 # other test methods added below
5125 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01005126 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
5127 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005128 fmt = logging.Formatter('%(asctime)s %(message)s')
5129 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005130 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005131 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005132 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005133 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005134 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005135 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005136 fh.close()
5137 # At this point, we should have a recent rotated file which we
5138 # can test for the existence of. However, in practice, on some
5139 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005140 # in time to go to look for the log file. So, we go back a fair
5141 # bit, and stop as soon as we see a rotated file. In theory this
5142 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005143 found = False
5144 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005145 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005146 for secs in range(GO_BACK):
5147 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005148 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5149 found = os.path.exists(fn)
5150 if found:
5151 self.rmfiles.append(fn)
5152 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005153 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5154 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005155 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005156 dn, fn = os.path.split(self.fn)
5157 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005158 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5159 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005160 for f in files:
5161 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005162 path = os.path.join(dn, f)
5163 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005164 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005165 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005166
Vinay Sajip0372e102011-05-05 12:59:14 +01005167 def test_invalid(self):
5168 assertRaises = self.assertRaises
5169 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005170 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005171 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005172 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005173 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005174 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005175
Vinay Sajipa7130792013-04-12 17:04:23 +01005176 def test_compute_rollover_daily_attime(self):
5177 currentTime = 0
5178 atTime = datetime.time(12, 0, 0)
5179 rh = logging.handlers.TimedRotatingFileHandler(
5180 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
5181 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005182 try:
5183 actual = rh.computeRollover(currentTime)
5184 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005185
Vinay Sajipd86ac962013-04-14 12:20:46 +01005186 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5187 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5188 finally:
5189 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005190
Vinay Sajip10e8c492013-05-18 10:19:54 -07005191 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005192 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005193 currentTime = int(time.time())
5194 today = currentTime - currentTime % 86400
5195
Vinay Sajipa7130792013-04-12 17:04:23 +01005196 atTime = datetime.time(12, 0, 0)
5197
Vinay Sajip10e8c492013-05-18 10:19:54 -07005198 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005199 for day in range(7):
5200 rh = logging.handlers.TimedRotatingFileHandler(
5201 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
5202 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005203 try:
5204 if wday > day:
5205 # The rollover day has already passed this week, so we
5206 # go over into next week
5207 expected = (7 - wday + day)
5208 else:
5209 expected = (day - wday)
5210 # At this point expected is in days from now, convert to seconds
5211 expected *= 24 * 60 * 60
5212 # Add in the rollover time
5213 expected += 12 * 60 * 60
5214 # Add in adjustment for today
5215 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005216 actual = rh.computeRollover(today)
5217 if actual != expected:
5218 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005219 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005220 self.assertEqual(actual, expected)
5221 if day == wday:
5222 # goes into following week
5223 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005224 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005225 if actual != expected:
5226 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005227 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005228 self.assertEqual(actual, expected)
5229 finally:
5230 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005231
5232
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005233def secs(**kw):
5234 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5235
5236for when, exp in (('S', 1),
5237 ('M', 60),
5238 ('H', 60 * 60),
5239 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005240 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005241 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005242 ('W0', secs(days=4, hours=24)),
5243 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005244 def test_compute_rollover(self, when=when, exp=exp):
5245 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00005246 self.fn, when=when, interval=1, backupCount=0, utc=True)
5247 currentTime = 0.0
5248 actual = rh.computeRollover(currentTime)
5249 if exp != actual:
5250 # Failures occur on some systems for MIDNIGHT and W0.
5251 # Print detailed calculation for MIDNIGHT so we can try to see
5252 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005253 if when == 'MIDNIGHT':
5254 try:
5255 if rh.utc:
5256 t = time.gmtime(currentTime)
5257 else:
5258 t = time.localtime(currentTime)
5259 currentHour = t[3]
5260 currentMinute = t[4]
5261 currentSecond = t[5]
5262 # r is the number of seconds left between now and midnight
5263 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5264 currentMinute) * 60 +
5265 currentSecond)
5266 result = currentTime + r
5267 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5268 print('currentHour: %s' % currentHour, file=sys.stderr)
5269 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5270 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5271 print('r: %s' % r, file=sys.stderr)
5272 print('result: %s' % result, file=sys.stderr)
5273 except Exception:
5274 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5275 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005276 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005277 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5278
Vinay Sajip60ccd822011-05-09 17:32:09 +01005279
Vinay Sajip223349c2015-10-01 20:37:54 +01005280@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005281class NTEventLogHandlerTest(BaseTest):
5282 def test_basic(self):
5283 logtype = 'Application'
5284 elh = win32evtlog.OpenEventLog(None, logtype)
5285 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005286
5287 try:
5288 h = logging.handlers.NTEventLogHandler('test_logging')
5289 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005290 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005291 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005292 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005293
Vinay Sajip60ccd822011-05-09 17:32:09 +01005294 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5295 h.handle(r)
5296 h.close()
5297 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005298 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005299 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5300 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5301 found = False
5302 GO_BACK = 100
5303 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5304 for e in events:
5305 if e.SourceName != 'test_logging':
5306 continue
5307 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5308 if msg != 'Test Log Message\r\n':
5309 continue
5310 found = True
5311 break
5312 msg = 'Record not found in event log, went back %d records' % GO_BACK
5313 self.assertTrue(found, msg=msg)
5314
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005315
5316class MiscTestCase(unittest.TestCase):
5317 def test__all__(self):
5318 blacklist = {'logThreads', 'logMultiprocessing',
5319 'logProcesses', 'currentframe',
5320 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5321 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger',
Martin Panter94332cb2016-10-20 05:10:44 +00005322 'root', 'threading'}
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005323 support.check__all__(self, logging, blacklist=blacklist)
5324
5325
Christian Heimes180510d2008-03-03 19:15:45 +00005326# Set the locale to the platform-dependent default. I have no idea
5327# why the test does this, but in any case we save the current locale
5328# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005329@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005330def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005331 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005332 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5333 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5334 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5335 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5336 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5337 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5338 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5339 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005340 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005341 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005342 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005343 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005344 ]
5345 if hasattr(logging.handlers, 'QueueListener'):
5346 tests.append(QueueListenerTest)
5347 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005348
Christian Heimes180510d2008-03-03 19:15:45 +00005349if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005350 test_main()