blob: bb31e29ca7691180abfc75cd8da7ec5c8bbb193c [file] [log] [blame]
Vinay Sajipca7b5042019-06-17 17:40:52 +01001# Copyright 2001-2019 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00002#
3# Permission to use, copy, modify, and distribute this software and its
4# documentation for any purpose and without fee is hereby granted,
5# provided that the above copyright notice appear in all copies and that
6# both that copyright notice and this permission notice appear in
7# supporting documentation, and that the name of Vinay Sajip
8# not be used in advertising or publicity pertaining to distribution
9# of the software without specific, written prior permission.
10# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
11# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
12# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
13# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
14# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
17"""Test harness for the logging module. Run all tests.
18
Vinay Sajipca7b5042019-06-17 17:40:52 +010019Copyright (C) 2001-2019 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000020"""
21
Christian Heimes180510d2008-03-03 19:15:45 +000022import logging
23import logging.handlers
24import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000025
Benjamin Petersonf91df042009-02-13 02:50:59 +000026import codecs
Vinay Sajipcf9e2f22012-10-09 09:06:03 +010027import configparser
Pablo Galindo137b0632018-10-16 15:17:57 +010028import copy
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Vinay Sajipdb8f4632016-09-08 01:37:03 +010030import pathlib
Christian Heimes180510d2008-03-03 19:15:45 +000031import pickle
32import io
33import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000034import json
Christian Heimes180510d2008-03-03 19:15:45 +000035import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000036import queue
Vinay Sajip66b8b082012-04-24 23:25:30 +010037import random
Christian Heimes180510d2008-03-03 19:15:45 +000038import re
Christian Heimes180510d2008-03-03 19:15:45 +000039import socket
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Rémi Lapeyre65f64b12019-03-15 07:53:34 +010043from test.support.script_helper import assert_python_ok, assert_python_failure
Vinay Sajip1feedb42014-05-31 08:19:12 +010044from test import support
Hai Shi3ddc6342020-06-30 21:46:06 +080045from test.support import os_helper
Serhiy Storchaka16994912020-04-25 10:06:29 +030046from test.support import socket_helper
Hai Shie80697d2020-05-28 06:10:27 +080047from test.support import threading_helper
Hai Shi3ddc6342020-06-30 21:46:06 +080048from test.support import warnings_helper
Serhiy Storchaka515fce42020-04-25 11:35:18 +030049from test.support.logging_helper import TestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000050import textwrap
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020051import threading
Vinay Sajip37eb3382011-04-26 20:26:41 +010052import time
Christian Heimes180510d2008-03-03 19:15:45 +000053import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000054import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000055import weakref
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020056
57import asyncore
58from http.server import HTTPServer, BaseHTTPRequestHandler
59import smtpd
60from urllib.parse import urlparse, parse_qs
61from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
62 ThreadingTCPServer, StreamRequestHandler)
63
Vinay Sajip60ccd822011-05-09 17:32:09 +010064try:
Vinay Sajip223349c2015-10-01 20:37:54 +010065 import win32evtlog, win32evtlogutil, pywintypes
Vinay Sajip60ccd822011-05-09 17:32:09 +010066except ImportError:
Vinay Sajip223349c2015-10-01 20:37:54 +010067 win32evtlog = win32evtlogutil = pywintypes = None
68
Eric V. Smith851cad72012-03-11 22:46:04 -070069try:
70 import zlib
71except ImportError:
72 pass
Christian Heimes18c66892008-02-17 13:31:39 +000073
Christian Heimes180510d2008-03-03 19:15:45 +000074class BaseTest(unittest.TestCase):
75
76 """Base class for logging tests."""
77
78 log_format = "%(name)s -> %(levelname)s: %(message)s"
Ezio Melotti3ffd29b2012-10-02 19:45:00 +030079 expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +000080 message_num = 0
81
82 def setUp(self):
83 """Setup the default logging stream to an internal StringIO instance,
84 so that we can examine log output as we want."""
Hai Shie80697d2020-05-28 06:10:27 +080085 self._threading_key = threading_helper.threading_setup()
Victor Stinner69669602017-08-18 23:47:54 +020086
Christian Heimes180510d2008-03-03 19:15:45 +000087 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000088 logging._acquireLock()
89 try:
Christian Heimes180510d2008-03-03 19:15:45 +000090 self.saved_handlers = logging._handlers.copy()
91 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000092 self.saved_loggers = saved_loggers = logger_dict.copy()
Vinay Sajip3b84eae2013-05-25 03:20:34 -070093 self.saved_name_to_level = logging._nameToLevel.copy()
94 self.saved_level_to_name = logging._levelToName.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000095 self.logger_states = logger_states = {}
96 for name in saved_loggers:
97 logger_states[name] = getattr(saved_loggers[name],
98 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000099 finally:
100 logging._releaseLock()
101
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100102 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000103 self.logger1 = logging.getLogger("\xab\xd7\xbb")
104 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000105
Christian Heimes180510d2008-03-03 19:15:45 +0000106 self.root_logger = logging.getLogger("")
107 self.original_logging_level = self.root_logger.getEffectiveLevel()
108
109 self.stream = io.StringIO()
110 self.root_logger.setLevel(logging.DEBUG)
111 self.root_hdlr = logging.StreamHandler(self.stream)
112 self.root_formatter = logging.Formatter(self.log_format)
113 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000114 if self.logger1.hasHandlers():
115 hlist = self.logger1.handlers + self.root_logger.handlers
116 raise AssertionError('Unexpected handlers: %s' % hlist)
117 if self.logger2.hasHandlers():
118 hlist = self.logger2.handlers + self.root_logger.handlers
119 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000120 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000121 self.assertTrue(self.logger1.hasHandlers())
122 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000123
124 def tearDown(self):
125 """Remove our logging stream, and restore the original logging
126 level."""
127 self.stream.close()
128 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000129 while self.root_logger.handlers:
130 h = self.root_logger.handlers[0]
131 self.root_logger.removeHandler(h)
132 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000133 self.root_logger.setLevel(self.original_logging_level)
134 logging._acquireLock()
135 try:
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700136 logging._levelToName.clear()
137 logging._levelToName.update(self.saved_level_to_name)
138 logging._nameToLevel.clear()
139 logging._nameToLevel.update(self.saved_name_to_level)
Christian Heimes180510d2008-03-03 19:15:45 +0000140 logging._handlers.clear()
141 logging._handlers.update(self.saved_handlers)
142 logging._handlerList[:] = self.saved_handler_list
Avram Lubkin78c18a92017-07-30 05:36:33 -0400143 manager = logging.getLogger().manager
144 manager.disable = 0
145 loggerDict = manager.loggerDict
Christian Heimes180510d2008-03-03 19:15:45 +0000146 loggerDict.clear()
147 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000148 logger_states = self.logger_states
149 for name in self.logger_states:
150 if logger_states[name] is not None:
151 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000152 finally:
153 logging._releaseLock()
154
Victor Stinner69669602017-08-18 23:47:54 +0200155 self.doCleanups()
Hai Shie80697d2020-05-28 06:10:27 +0800156 threading_helper.threading_cleanup(*self._threading_key)
Victor Stinner69669602017-08-18 23:47:54 +0200157
Vinay Sajip4ded5512012-10-02 15:56:16 +0100158 def assert_log_lines(self, expected_values, stream=None, pat=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000159 """Match the collected log lines against the regular expression
160 self.expected_log_pat, and compare the extracted group values to
161 the expected_values list of tuples."""
162 stream = stream or self.stream
Vinay Sajip4ded5512012-10-02 15:56:16 +0100163 pat = re.compile(pat or self.expected_log_pat)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300164 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100165 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000166 for actual, expected in zip(actual_lines, expected_values):
167 match = pat.search(actual)
168 if not match:
169 self.fail("Log line does not match expected pattern:\n" +
170 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000171 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000172 s = stream.read()
173 if s:
174 self.fail("Remaining output at end of log stream:\n" + s)
175
176 def next_message(self):
177 """Generate a message consisting solely of an auto-incrementing
178 integer."""
179 self.message_num += 1
180 return "%d" % self.message_num
181
182
183class BuiltinLevelsTest(BaseTest):
184 """Test builtin levels and their inheritance."""
185
186 def test_flat(self):
Mike53f7a7c2017-12-14 14:04:53 +0300187 # Logging levels in a flat logger namespace.
Christian Heimes180510d2008-03-03 19:15:45 +0000188 m = self.next_message
189
190 ERR = logging.getLogger("ERR")
191 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000192 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000193 INF.setLevel(logging.INFO)
194 DEB = logging.getLogger("DEB")
195 DEB.setLevel(logging.DEBUG)
196
197 # These should log.
198 ERR.log(logging.CRITICAL, m())
199 ERR.error(m())
200
201 INF.log(logging.CRITICAL, m())
202 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100203 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000204 INF.info(m())
205
206 DEB.log(logging.CRITICAL, m())
207 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100208 DEB.warning(m())
209 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000210 DEB.debug(m())
211
212 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100213 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000214 ERR.info(m())
215 ERR.debug(m())
216
217 INF.debug(m())
218
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000219 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000220 ('ERR', 'CRITICAL', '1'),
221 ('ERR', 'ERROR', '2'),
222 ('INF', 'CRITICAL', '3'),
223 ('INF', 'ERROR', '4'),
224 ('INF', 'WARNING', '5'),
225 ('INF', 'INFO', '6'),
226 ('DEB', 'CRITICAL', '7'),
227 ('DEB', 'ERROR', '8'),
228 ('DEB', 'WARNING', '9'),
229 ('DEB', 'INFO', '10'),
230 ('DEB', 'DEBUG', '11'),
231 ])
232
233 def test_nested_explicit(self):
234 # Logging levels in a nested namespace, all explicitly set.
235 m = self.next_message
236
237 INF = logging.getLogger("INF")
238 INF.setLevel(logging.INFO)
239 INF_ERR = logging.getLogger("INF.ERR")
240 INF_ERR.setLevel(logging.ERROR)
241
242 # These should log.
243 INF_ERR.log(logging.CRITICAL, m())
244 INF_ERR.error(m())
245
246 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100247 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000248 INF_ERR.info(m())
249 INF_ERR.debug(m())
250
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000251 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000252 ('INF.ERR', 'CRITICAL', '1'),
253 ('INF.ERR', 'ERROR', '2'),
254 ])
255
256 def test_nested_inherited(self):
Mike53f7a7c2017-12-14 14:04:53 +0300257 # Logging levels in a nested namespace, inherited from parent loggers.
Christian Heimes180510d2008-03-03 19:15:45 +0000258 m = self.next_message
259
260 INF = logging.getLogger("INF")
261 INF.setLevel(logging.INFO)
262 INF_ERR = logging.getLogger("INF.ERR")
263 INF_ERR.setLevel(logging.ERROR)
264 INF_UNDEF = logging.getLogger("INF.UNDEF")
265 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
266 UNDEF = logging.getLogger("UNDEF")
267
268 # These should log.
269 INF_UNDEF.log(logging.CRITICAL, m())
270 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100271 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000272 INF_UNDEF.info(m())
273 INF_ERR_UNDEF.log(logging.CRITICAL, m())
274 INF_ERR_UNDEF.error(m())
275
276 # These should not log.
277 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100278 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000279 INF_ERR_UNDEF.info(m())
280 INF_ERR_UNDEF.debug(m())
281
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000282 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000283 ('INF.UNDEF', 'CRITICAL', '1'),
284 ('INF.UNDEF', 'ERROR', '2'),
285 ('INF.UNDEF', 'WARNING', '3'),
286 ('INF.UNDEF', 'INFO', '4'),
287 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
288 ('INF.ERR.UNDEF', 'ERROR', '6'),
289 ])
290
291 def test_nested_with_virtual_parent(self):
292 # Logging levels when some parent does not exist yet.
293 m = self.next_message
294
295 INF = logging.getLogger("INF")
296 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
297 CHILD = logging.getLogger("INF.BADPARENT")
298 INF.setLevel(logging.INFO)
299
300 # These should log.
301 GRANDCHILD.log(logging.FATAL, m())
302 GRANDCHILD.info(m())
303 CHILD.log(logging.FATAL, m())
304 CHILD.info(m())
305
306 # These should not log.
307 GRANDCHILD.debug(m())
308 CHILD.debug(m())
309
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000310 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000311 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
312 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
313 ('INF.BADPARENT', 'CRITICAL', '3'),
314 ('INF.BADPARENT', 'INFO', '4'),
315 ])
316
Vinay Sajipd1d4fbf2014-09-11 23:06:09 +0100317 def test_regression_22386(self):
318 """See issue #22386 for more information."""
319 self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
320 self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
Christian Heimes180510d2008-03-03 19:15:45 +0000321
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100322 def test_issue27935(self):
323 fatal = logging.getLevelName('FATAL')
324 self.assertEqual(fatal, logging.FATAL)
325
Vinay Sajip924aaae2017-01-11 17:35:36 +0000326 def test_regression_29220(self):
327 """See issue #29220 for more information."""
328 logging.addLevelName(logging.INFO, '')
329 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
330 self.assertEqual(logging.getLevelName(logging.INFO), '')
331 self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
332 self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
333
Christian Heimes180510d2008-03-03 19:15:45 +0000334class BasicFilterTest(BaseTest):
335
336 """Test the bundled Filter class."""
337
338 def test_filter(self):
339 # Only messages satisfying the specified criteria pass through the
340 # filter.
341 filter_ = logging.Filter("spam.eggs")
342 handler = self.root_logger.handlers[0]
343 try:
344 handler.addFilter(filter_)
345 spam = logging.getLogger("spam")
346 spam_eggs = logging.getLogger("spam.eggs")
347 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
348 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
349
350 spam.info(self.next_message())
351 spam_eggs.info(self.next_message()) # Good.
352 spam_eggs_fish.info(self.next_message()) # Good.
353 spam_bakedbeans.info(self.next_message())
354
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000355 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000356 ('spam.eggs', 'INFO', '2'),
357 ('spam.eggs.fish', 'INFO', '3'),
358 ])
359 finally:
360 handler.removeFilter(filter_)
361
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000362 def test_callable_filter(self):
363 # Only messages satisfying the specified criteria pass through the
364 # filter.
365
366 def filterfunc(record):
367 parts = record.name.split('.')
368 prefix = '.'.join(parts[:2])
369 return prefix == 'spam.eggs'
370
371 handler = self.root_logger.handlers[0]
372 try:
373 handler.addFilter(filterfunc)
374 spam = logging.getLogger("spam")
375 spam_eggs = logging.getLogger("spam.eggs")
376 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
377 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
378
379 spam.info(self.next_message())
380 spam_eggs.info(self.next_message()) # Good.
381 spam_eggs_fish.info(self.next_message()) # Good.
382 spam_bakedbeans.info(self.next_message())
383
384 self.assert_log_lines([
385 ('spam.eggs', 'INFO', '2'),
386 ('spam.eggs.fish', 'INFO', '3'),
387 ])
388 finally:
389 handler.removeFilter(filterfunc)
390
Vinay Sajip985ef872011-04-26 19:34:04 +0100391 def test_empty_filter(self):
392 f = logging.Filter()
393 r = logging.makeLogRecord({'name': 'spam.eggs'})
394 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000395
396#
397# First, we define our levels. There can be as many as you want - the only
398# limitations are that they should be integers, the lowest should be > 0 and
399# larger values mean less information being logged. If you need specific
400# level values which do not fit into these limitations, you can use a
401# mapping dictionary to convert between your application levels and the
402# logging system.
403#
404SILENT = 120
405TACITURN = 119
406TERSE = 118
407EFFUSIVE = 117
408SOCIABLE = 116
409VERBOSE = 115
410TALKATIVE = 114
411GARRULOUS = 113
412CHATTERBOX = 112
413BORING = 111
414
415LEVEL_RANGE = range(BORING, SILENT + 1)
416
417#
418# Next, we define names for our levels. You don't need to do this - in which
419# case the system will use "Level n" to denote the text for the level.
420#
421my_logging_levels = {
422 SILENT : 'Silent',
423 TACITURN : 'Taciturn',
424 TERSE : 'Terse',
425 EFFUSIVE : 'Effusive',
426 SOCIABLE : 'Sociable',
427 VERBOSE : 'Verbose',
428 TALKATIVE : 'Talkative',
429 GARRULOUS : 'Garrulous',
430 CHATTERBOX : 'Chatterbox',
431 BORING : 'Boring',
432}
433
434class GarrulousFilter(logging.Filter):
435
436 """A filter which blocks garrulous messages."""
437
438 def filter(self, record):
439 return record.levelno != GARRULOUS
440
441class VerySpecificFilter(logging.Filter):
442
443 """A filter which blocks sociable and taciturn messages."""
444
445 def filter(self, record):
446 return record.levelno not in [SOCIABLE, TACITURN]
447
448
449class CustomLevelsAndFiltersTest(BaseTest):
450
451 """Test various filtering possibilities with custom logging levels."""
452
453 # Skip the logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +0300454 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +0000455
456 def setUp(self):
457 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000458 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000459 logging.addLevelName(k, v)
460
461 def log_at_all_levels(self, logger):
462 for lvl in LEVEL_RANGE:
463 logger.log(lvl, self.next_message())
464
465 def test_logger_filter(self):
466 # Filter at logger level.
467 self.root_logger.setLevel(VERBOSE)
468 # Levels >= 'Verbose' are good.
469 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000470 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000471 ('Verbose', '5'),
472 ('Sociable', '6'),
473 ('Effusive', '7'),
474 ('Terse', '8'),
475 ('Taciturn', '9'),
476 ('Silent', '10'),
477 ])
478
479 def test_handler_filter(self):
480 # Filter at handler level.
481 self.root_logger.handlers[0].setLevel(SOCIABLE)
482 try:
483 # Levels >= 'Sociable' are good.
484 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000485 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000486 ('Sociable', '6'),
487 ('Effusive', '7'),
488 ('Terse', '8'),
489 ('Taciturn', '9'),
490 ('Silent', '10'),
491 ])
492 finally:
493 self.root_logger.handlers[0].setLevel(logging.NOTSET)
494
495 def test_specific_filters(self):
496 # Set a specific filter object on the handler, and then add another
497 # filter object on the logger itself.
498 handler = self.root_logger.handlers[0]
499 specific_filter = None
500 garr = GarrulousFilter()
501 handler.addFilter(garr)
502 try:
503 self.log_at_all_levels(self.root_logger)
504 first_lines = [
505 # Notice how 'Garrulous' is missing
506 ('Boring', '1'),
507 ('Chatterbox', '2'),
508 ('Talkative', '4'),
509 ('Verbose', '5'),
510 ('Sociable', '6'),
511 ('Effusive', '7'),
512 ('Terse', '8'),
513 ('Taciturn', '9'),
514 ('Silent', '10'),
515 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000516 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000517
518 specific_filter = VerySpecificFilter()
519 self.root_logger.addFilter(specific_filter)
520 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000521 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000522 # Not only 'Garrulous' is still missing, but also 'Sociable'
523 # and 'Taciturn'
524 ('Boring', '11'),
525 ('Chatterbox', '12'),
526 ('Talkative', '14'),
527 ('Verbose', '15'),
528 ('Effusive', '17'),
529 ('Terse', '18'),
530 ('Silent', '20'),
531 ])
532 finally:
533 if specific_filter:
534 self.root_logger.removeFilter(specific_filter)
535 handler.removeFilter(garr)
536
537
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100538class HandlerTest(BaseTest):
539 def test_name(self):
540 h = logging.Handler()
541 h.name = 'generic'
542 self.assertEqual(h.name, 'generic')
543 h.name = 'anothergeneric'
544 self.assertEqual(h.name, 'anothergeneric')
545 self.assertRaises(NotImplementedError, h.emit, None)
546
Vinay Sajip5a35b062011-04-27 11:31:14 +0100547 def test_builtin_handlers(self):
548 # We can't actually *use* too many handlers in the tests,
549 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200550 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100551 for existing in (True, False):
552 fd, fn = tempfile.mkstemp()
553 os.close(fd)
554 if not existing:
555 os.unlink(fn)
Inada Naokifb786922021-04-06 11:18:41 +0900556 h = logging.handlers.WatchedFileHandler(fn, encoding='utf-8', delay=True)
Vinay Sajip5a35b062011-04-27 11:31:14 +0100557 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100558 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100559 self.assertEqual(dev, -1)
560 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100561 r = logging.makeLogRecord({'msg': 'Test'})
562 h.handle(r)
563 # Now remove the file.
564 os.unlink(fn)
565 self.assertFalse(os.path.exists(fn))
566 # The next call should recreate the file.
567 h.handle(r)
568 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100569 else:
570 self.assertEqual(h.dev, -1)
571 self.assertEqual(h.ino, -1)
572 h.close()
573 if existing:
574 os.unlink(fn)
575 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100576 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100577 else:
578 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100579 try:
580 h = logging.handlers.SysLogHandler(sockname)
581 self.assertEqual(h.facility, h.LOG_USER)
582 self.assertTrue(h.unixsocket)
583 h.close()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200584 except OSError: # syslogd might not be available
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100585 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100586 for method in ('GET', 'POST', 'PUT'):
587 if method == 'PUT':
588 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
589 'localhost', '/log', method)
590 else:
591 h = logging.handlers.HTTPHandler('localhost', '/log', method)
592 h.close()
593 h = logging.handlers.BufferingHandler(0)
594 r = logging.makeLogRecord({})
595 self.assertTrue(h.shouldFlush(r))
596 h.close()
597 h = logging.handlers.BufferingHandler(1)
598 self.assertFalse(h.shouldFlush(r))
599 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100600
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100601 def test_path_objects(self):
602 """
603 Test that Path objects are accepted as filename arguments to handlers.
604
605 See Issue #27493.
606 """
607 fd, fn = tempfile.mkstemp()
608 os.close(fd)
609 os.unlink(fn)
610 pfn = pathlib.Path(fn)
611 cases = (
612 (logging.FileHandler, (pfn, 'w')),
613 (logging.handlers.RotatingFileHandler, (pfn, 'a')),
614 (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
615 )
616 if sys.platform in ('linux', 'darwin'):
617 cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
618 for cls, args in cases:
Inada Naokifb786922021-04-06 11:18:41 +0900619 h = cls(*args, encoding="utf-8")
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100620 self.assertTrue(os.path.exists(fn))
621 h.close()
622 os.unlink(fn)
623
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100624 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100625 def test_race(self):
626 # Issue #14632 refers.
627 def remove_loop(fname, tries):
628 for _ in range(tries):
629 try:
630 os.unlink(fname)
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000631 self.deletion_time = time.time()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100632 except OSError:
633 pass
634 time.sleep(0.004 * random.randint(0, 4))
635
Vinay Sajipc94871a2012-04-25 10:51:35 +0100636 del_count = 500
637 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100638
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000639 self.handle_time = None
640 self.deletion_time = None
641
Vinay Sajipa5798de2012-04-24 23:33:33 +0100642 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100643 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
644 os.close(fd)
645 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
646 remover.daemon = True
647 remover.start()
Inada Naokifb786922021-04-06 11:18:41 +0900648 h = logging.handlers.WatchedFileHandler(fn, encoding='utf-8', delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100649 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
650 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100651 try:
652 for _ in range(log_count):
653 time.sleep(0.005)
654 r = logging.makeLogRecord({'msg': 'testing' })
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000655 try:
656 self.handle_time = time.time()
657 h.handle(r)
658 except Exception:
659 print('Deleted at %s, '
660 'opened at %s' % (self.deletion_time,
661 self.handle_time))
662 raise
Vinay Sajipc94871a2012-04-25 10:51:35 +0100663 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100664 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100665 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100666 if os.path.exists(fn):
667 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100668
Gregory P. Smith19003842018-09-13 22:08:31 -0700669 # The implementation relies on os.register_at_fork existing, but we test
670 # based on os.fork existing because that is what users and this test use.
671 # This helps ensure that when fork exists (the important concept) that the
672 # register_at_fork mechanism is also present and used.
673 @unittest.skipIf(not hasattr(os, 'fork'), 'Test requires os.fork().')
674 def test_post_fork_child_no_deadlock(self):
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400675 """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
676 class _OurHandler(logging.Handler):
677 def __init__(self):
678 super().__init__()
679 self.sub_handler = logging.StreamHandler(
Inada Naokifb786922021-04-06 11:18:41 +0900680 stream=open('/dev/null', 'wt', encoding='utf-8'))
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400681
682 def emit(self, record):
683 self.sub_handler.acquire()
684 try:
685 self.sub_handler.emit(record)
686 finally:
687 self.sub_handler.release()
688
689 self.assertEqual(len(logging._handlers), 0)
690 refed_h = _OurHandler()
Xtreak2c105382019-05-13 20:18:52 +0530691 self.addCleanup(refed_h.sub_handler.stream.close)
Gregory P. Smith19003842018-09-13 22:08:31 -0700692 refed_h.name = 'because we need at least one for this test'
693 self.assertGreater(len(logging._handlers), 0)
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400694 self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
695 test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
696 test_logger.addHandler(refed_h)
697 test_logger.setLevel(logging.DEBUG)
Gregory P. Smith19003842018-09-13 22:08:31 -0700698
699 locks_held__ready_to_fork = threading.Event()
700 fork_happened__release_locks_and_end_thread = threading.Event()
701
702 def lock_holder_thread_fn():
703 logging._acquireLock()
704 try:
705 refed_h.acquire()
706 try:
707 # Tell the main thread to do the fork.
708 locks_held__ready_to_fork.set()
709
710 # If the deadlock bug exists, the fork will happen
711 # without dealing with the locks we hold, deadlocking
712 # the child.
713
714 # Wait for a successful fork or an unreasonable amount of
715 # time before releasing our locks. To avoid a timing based
716 # test we'd need communication from os.fork() as to when it
717 # has actually happened. Given this is a regression test
718 # for a fixed issue, potentially less reliably detecting
719 # regression via timing is acceptable for simplicity.
720 # The test will always take at least this long. :(
721 fork_happened__release_locks_and_end_thread.wait(0.5)
722 finally:
723 refed_h.release()
724 finally:
725 logging._releaseLock()
726
727 lock_holder_thread = threading.Thread(
728 target=lock_holder_thread_fn,
729 name='test_post_fork_child_no_deadlock lock holder')
730 lock_holder_thread.start()
731
732 locks_held__ready_to_fork.wait()
733 pid = os.fork()
Victor Stinner278c1e12020-03-31 20:08:12 +0200734 if pid == 0:
735 # Child process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400736 try:
737 test_logger.info(r'Child process did not deadlock. \o/')
738 finally:
739 os._exit(0)
Victor Stinner278c1e12020-03-31 20:08:12 +0200740 else:
741 # Parent process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400742 test_logger.info(r'Parent process returned from fork. \o/')
Gregory P. Smith19003842018-09-13 22:08:31 -0700743 fork_happened__release_locks_and_end_thread.set()
744 lock_holder_thread.join()
Victor Stinner278c1e12020-03-31 20:08:12 +0200745
746 support.wait_process(pid, exitcode=0)
Gregory P. Smith19003842018-09-13 22:08:31 -0700747
Vinay Sajipa5798de2012-04-24 23:33:33 +0100748
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100749class BadStream(object):
750 def write(self, data):
751 raise RuntimeError('deliberate mistake')
752
753class TestStreamHandler(logging.StreamHandler):
754 def handleError(self, record):
755 self.error_record = record
756
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200757class StreamWithIntName(object):
758 level = logging.NOTSET
759 name = 2
760
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100761class StreamHandlerTest(BaseTest):
762 def test_error_handling(self):
763 h = TestStreamHandler(BadStream())
764 r = logging.makeLogRecord({})
765 old_raise = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +0100766
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100767 try:
768 h.handle(r)
769 self.assertIs(h.error_record, r)
Vinay Sajip1feedb42014-05-31 08:19:12 +0100770
Vinay Sajip985ef872011-04-26 19:34:04 +0100771 h = logging.StreamHandler(BadStream())
Vinay Sajip1feedb42014-05-31 08:19:12 +0100772 with support.captured_stderr() as stderr:
773 h.handle(r)
774 msg = '\nRuntimeError: deliberate mistake\n'
775 self.assertIn(msg, stderr.getvalue())
776
Vinay Sajip985ef872011-04-26 19:34:04 +0100777 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +0100778 with support.captured_stderr() as stderr:
779 h.handle(r)
780 self.assertEqual('', stderr.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100781 finally:
782 logging.raiseExceptions = old_raise
783
Vinay Sajip2543f502017-07-30 10:41:45 +0100784 def test_stream_setting(self):
785 """
786 Test setting the handler's stream
787 """
788 h = logging.StreamHandler()
789 stream = io.StringIO()
790 old = h.setStream(stream)
791 self.assertIs(old, sys.stderr)
792 actual = h.setStream(old)
793 self.assertIs(actual, stream)
794 # test that setting to existing value returns None
795 actual = h.setStream(old)
796 self.assertIsNone(actual)
797
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200798 def test_can_represent_stream_with_int_name(self):
799 h = logging.StreamHandler(StreamWithIntName())
800 self.assertEqual(repr(h), '<StreamHandler 2 (NOTSET)>')
801
Vinay Sajip7367d082011-05-02 13:17:27 +0100802# -- The following section could be moved into a server_helper.py module
803# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100804
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200805class TestSMTPServer(smtpd.SMTPServer):
806 """
807 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100808
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200809 :param addr: A (host, port) tuple which the server listens on.
810 You can specify a port value of zero: the server's
811 *port* attribute will hold the actual port number
812 used, which can be used in client connections.
813 :param handler: A callable which will be called to process
814 incoming messages. The handler will be passed
815 the client address tuple, who the message is from,
816 a list of recipients and the message data.
817 :param poll_interval: The interval, in seconds, used in the underlying
818 :func:`select` or :func:`poll` call by
819 :func:`asyncore.loop`.
820 :param sockmap: A dictionary which will be used to hold
821 :class:`asyncore.dispatcher` instances used by
822 :func:`asyncore.loop`. This avoids changing the
823 :mod:`asyncore` module's global state.
824 """
825
826 def __init__(self, addr, handler, poll_interval, sockmap):
827 smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
828 decode_data=True)
829 self.port = self.socket.getsockname()[1]
830 self._handler = handler
831 self._thread = None
Victor Stinner75ec1032021-04-16 14:32:01 +0200832 self._quit = False
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200833 self.poll_interval = poll_interval
834
835 def process_message(self, peer, mailfrom, rcpttos, data):
836 """
837 Delegates to the handler passed in to the server's constructor.
838
839 Typically, this will be a test case method.
840 :param peer: The client (host, port) tuple.
841 :param mailfrom: The address of the sender.
842 :param rcpttos: The addresses of the recipients.
843 :param data: The message.
844 """
845 self._handler(peer, mailfrom, rcpttos, data)
846
847 def start(self):
848 """
849 Start the server running on a separate daemon thread.
850 """
851 self._thread = t = threading.Thread(target=self.serve_forever,
852 args=(self.poll_interval,))
Christian Heimes95bbb332021-04-12 13:12:36 +0200853 t.daemon = True
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200854 t.start()
855
856 def serve_forever(self, poll_interval):
857 """
858 Run the :mod:`asyncore` loop until normal termination
859 conditions arise.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100860 :param poll_interval: The interval, in seconds, used in the underlying
861 :func:`select` or :func:`poll` call by
862 :func:`asyncore.loop`.
863 """
Victor Stinner75ec1032021-04-16 14:32:01 +0200864 while not self._quit:
865 asyncore.loop(poll_interval, map=self._map, count=1)
Vinay Sajipce7c9782011-05-17 07:15:53 +0100866
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100867 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200868 """
869 Stop the thread by closing the server instance.
870 Wait for the server thread to terminate.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200871 """
Victor Stinner75ec1032021-04-16 14:32:01 +0200872 self._quit = True
Hai Shie80697d2020-05-28 06:10:27 +0800873 threading_helper.join_thread(self._thread)
Victor Stinnerb9b69002017-09-14 14:40:56 -0700874 self._thread = None
Victor Stinner75ec1032021-04-16 14:32:01 +0200875 self.close()
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200876 asyncore.close_all(map=self._map, ignore_all=True)
Vinay Sajipa463d252011-04-30 21:52:48 +0100877
Vinay Sajip7367d082011-05-02 13:17:27 +0100878
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200879class ControlMixin(object):
880 """
881 This mixin is used to start a server on a separate thread, and
882 shut it down programmatically. Request handling is simplified - instead
883 of needing to derive a suitable RequestHandler subclass, you just
884 provide a callable which will be passed each received request to be
885 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100886
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200887 :param handler: A handler callable which will be called with a
888 single parameter - the request - in order to
889 process the request. This handler is called on the
890 server thread, effectively meaning that requests are
891 processed serially. While not quite Web scale ;-),
892 this should be fine for testing applications.
893 :param poll_interval: The polling interval in seconds.
894 """
895 def __init__(self, handler, poll_interval):
896 self._thread = None
897 self.poll_interval = poll_interval
898 self._handler = handler
899 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100900
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200901 def start(self):
902 """
903 Create a daemon thread to run the server, and start it.
904 """
905 self._thread = t = threading.Thread(target=self.serve_forever,
906 args=(self.poll_interval,))
Christian Heimes95bbb332021-04-12 13:12:36 +0200907 t.daemon = True
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200908 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100909
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200910 def serve_forever(self, poll_interval):
911 """
912 Run the server. Set the ready flag before entering the
913 service loop.
914 """
915 self.ready.set()
916 super(ControlMixin, self).serve_forever(poll_interval)
917
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100918 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200919 """
920 Tell the server thread to stop, and wait for it to do so.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200921 """
922 self.shutdown()
923 if self._thread is not None:
Hai Shie80697d2020-05-28 06:10:27 +0800924 threading_helper.join_thread(self._thread)
Vinay Sajip7367d082011-05-02 13:17:27 +0100925 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200926 self.server_close()
927 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100928
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200929class TestHTTPServer(ControlMixin, HTTPServer):
930 """
931 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100932
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200933 :param addr: A tuple with the IP address and port to listen on.
934 :param handler: A handler callable which will be called with a
935 single parameter - the request - in order to
936 process the request.
937 :param poll_interval: The polling interval in seconds.
938 :param log: Pass ``True`` to enable log messages.
939 """
940 def __init__(self, addr, handler, poll_interval=0.5,
941 log=False, sslctx=None):
942 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
943 def __getattr__(self, name, default=None):
944 if name.startswith('do_'):
945 return self.process_request
946 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100947
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200948 def process_request(self):
949 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100950
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200951 def log_message(self, format, *args):
952 if log:
953 super(DelegatingHTTPRequestHandler,
954 self).log_message(format, *args)
955 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
956 ControlMixin.__init__(self, handler, poll_interval)
957 self.sslctx = sslctx
Vinay Sajip7367d082011-05-02 13:17:27 +0100958
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200959 def get_request(self):
960 try:
961 sock, addr = self.socket.accept()
962 if self.sslctx:
963 sock = self.sslctx.wrap_socket(sock, server_side=True)
964 except OSError as e:
965 # socket errors are silenced by the caller, print them here
966 sys.stderr.write("Got an error:\n%s\n" % e)
967 raise
968 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100969
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200970class TestTCPServer(ControlMixin, ThreadingTCPServer):
971 """
972 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100973
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200974 :param addr: A tuple with the IP address and port to listen on.
975 :param handler: A handler callable which will be called with a single
976 parameter - the request - in order to process the request.
977 :param poll_interval: The polling interval in seconds.
978 :bind_and_activate: If True (the default), binds the server and starts it
979 listening. If False, you need to call
980 :meth:`server_bind` and :meth:`server_activate` at
981 some later time before calling :meth:`start`, so that
982 the server will set up the socket and listen on it.
983 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100984
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200985 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100986
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200987 def __init__(self, addr, handler, poll_interval=0.5,
988 bind_and_activate=True):
989 class DelegatingTCPRequestHandler(StreamRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +0100990
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200991 def handle(self):
992 self.server._handler(self)
993 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
994 bind_and_activate)
995 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100996
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200997 def server_bind(self):
998 super(TestTCPServer, self).server_bind()
999 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +01001000
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001001class TestUDPServer(ControlMixin, ThreadingUDPServer):
1002 """
1003 A UDP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +01001004
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001005 :param addr: A tuple with the IP address and port to listen on.
1006 :param handler: A handler callable which will be called with a
1007 single parameter - the request - in order to
1008 process the request.
1009 :param poll_interval: The polling interval for shutdown requests,
1010 in seconds.
1011 :bind_and_activate: If True (the default), binds the server and
1012 starts it listening. If False, you need to
1013 call :meth:`server_bind` and
1014 :meth:`server_activate` at some later time
1015 before calling :meth:`start`, so that the server will
1016 set up the socket and listen on it.
1017 """
1018 def __init__(self, addr, handler, poll_interval=0.5,
1019 bind_and_activate=True):
1020 class DelegatingUDPRequestHandler(DatagramRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +01001021
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001022 def handle(self):
1023 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001024
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001025 def finish(self):
1026 data = self.wfile.getvalue()
1027 if data:
1028 try:
1029 super(DelegatingUDPRequestHandler, self).finish()
1030 except OSError:
1031 if not self.server._closed:
1032 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +01001033
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001034 ThreadingUDPServer.__init__(self, addr,
1035 DelegatingUDPRequestHandler,
1036 bind_and_activate)
1037 ControlMixin.__init__(self, handler, poll_interval)
1038 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +01001039
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001040 def server_bind(self):
1041 super(TestUDPServer, self).server_bind()
1042 self.port = self.socket.getsockname()[1]
Vinay Sajipce7c9782011-05-17 07:15:53 +01001043
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001044 def server_close(self):
1045 super(TestUDPServer, self).server_close()
1046 self._closed = True
Vinay Sajipce7c9782011-05-17 07:15:53 +01001047
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001048if hasattr(socket, "AF_UNIX"):
1049 class TestUnixStreamServer(TestTCPServer):
1050 address_family = socket.AF_UNIX
Vinay Sajipce7c9782011-05-17 07:15:53 +01001051
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001052 class TestUnixDatagramServer(TestUDPServer):
1053 address_family = socket.AF_UNIX
Vinay Sajip5421f352013-09-27 18:18:28 +01001054
Vinay Sajip7367d082011-05-02 13:17:27 +01001055# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +01001056
1057class SMTPHandlerTest(BaseTest):
Victor Stinnerc98b0192019-12-10 21:12:26 +01001058 # bpo-14314, bpo-19665, bpo-34092: don't wait forever
1059 TIMEOUT = support.LONG_TIMEOUT
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02001060
Vinay Sajipa463d252011-04-30 21:52:48 +01001061 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +01001062 sockmap = {}
Serhiy Storchaka16994912020-04-25 10:06:29 +03001063 server = TestSMTPServer((socket_helper.HOST, 0), self.process_message, 0.001,
Vinay Sajip7367d082011-05-02 13:17:27 +01001064 sockmap)
1065 server.start()
Serhiy Storchaka16994912020-04-25 10:06:29 +03001066 addr = (socket_helper.HOST, server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +00001067 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
1068 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001069 self.assertEqual(h.toaddrs, ['you'])
1070 self.messages = []
Vinay Sajip277640a2015-10-17 16:13:10 +01001071 r = logging.makeLogRecord({'msg': 'Hello \u2713'})
Vinay Sajip7367d082011-05-02 13:17:27 +01001072 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +01001073 h.handle(r)
Victor Stinner31b50b82018-07-11 12:35:38 +02001074 self.handled.wait(self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001075 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +00001076 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +01001077 self.assertEqual(len(self.messages), 1)
1078 peer, mailfrom, rcpttos, data = self.messages[0]
1079 self.assertEqual(mailfrom, 'me')
1080 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001081 self.assertIn('\nSubject: Log\n', data)
Vinay Sajip277640a2015-10-17 16:13:10 +01001082 self.assertTrue(data.endswith('\n\nHello \u2713'))
Vinay Sajipa463d252011-04-30 21:52:48 +01001083 h.close()
1084
1085 def process_message(self, *args):
1086 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +01001087 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +01001088
Christian Heimes180510d2008-03-03 19:15:45 +00001089class MemoryHandlerTest(BaseTest):
1090
1091 """Tests for the MemoryHandler."""
1092
1093 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001094 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001095
1096 def setUp(self):
1097 BaseTest.setUp(self)
1098 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001099 self.root_hdlr)
Christian Heimes180510d2008-03-03 19:15:45 +00001100 self.mem_logger = logging.getLogger('mem')
1101 self.mem_logger.propagate = 0
1102 self.mem_logger.addHandler(self.mem_hdlr)
1103
1104 def tearDown(self):
1105 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001106 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001107
1108 def test_flush(self):
1109 # The memory handler flushes to its target handler based on specific
1110 # criteria (message count and message level).
1111 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001112 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001113 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001114 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001115 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001116 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001117 lines = [
1118 ('DEBUG', '1'),
1119 ('INFO', '2'),
1120 ('WARNING', '3'),
1121 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001122 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001123 for n in (4, 14):
1124 for i in range(9):
1125 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001126 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001127 # This will flush because it's the 10th message since the last
1128 # flush.
1129 self.mem_logger.debug(self.next_message())
1130 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001131 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001132
1133 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001134 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001135
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001136 def test_flush_on_close(self):
1137 """
1138 Test that the flush-on-close configuration works as expected.
1139 """
1140 self.mem_logger.debug(self.next_message())
1141 self.assert_log_lines([])
1142 self.mem_logger.info(self.next_message())
1143 self.assert_log_lines([])
1144 self.mem_logger.removeHandler(self.mem_hdlr)
1145 # Default behaviour is to flush on close. Check that it happens.
1146 self.mem_hdlr.close()
1147 lines = [
1148 ('DEBUG', '1'),
1149 ('INFO', '2'),
1150 ]
1151 self.assert_log_lines(lines)
1152 # Now configure for flushing not to be done on close.
1153 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1154 self.root_hdlr,
1155 False)
1156 self.mem_logger.addHandler(self.mem_hdlr)
1157 self.mem_logger.debug(self.next_message())
1158 self.assert_log_lines(lines) # no change
1159 self.mem_logger.info(self.next_message())
1160 self.assert_log_lines(lines) # no change
1161 self.mem_logger.removeHandler(self.mem_hdlr)
1162 self.mem_hdlr.close()
1163 # assert that no new lines have been added
1164 self.assert_log_lines(lines) # no change
1165
Irit Katriel2353d772020-08-16 16:10:13 +01001166 def test_race_between_set_target_and_flush(self):
1167 class MockRaceConditionHandler:
1168 def __init__(self, mem_hdlr):
1169 self.mem_hdlr = mem_hdlr
Victor Stinner13ff3962020-10-12 00:37:20 +02001170 self.threads = []
Irit Katriel2353d772020-08-16 16:10:13 +01001171
1172 def removeTarget(self):
1173 self.mem_hdlr.setTarget(None)
1174
1175 def handle(self, msg):
Victor Stinner13ff3962020-10-12 00:37:20 +02001176 thread = threading.Thread(target=self.removeTarget)
1177 self.threads.append(thread)
1178 thread.start()
Irit Katriel2353d772020-08-16 16:10:13 +01001179
1180 target = MockRaceConditionHandler(self.mem_hdlr)
Victor Stinner13ff3962020-10-12 00:37:20 +02001181 try:
1182 self.mem_hdlr.setTarget(target)
Irit Katriel2353d772020-08-16 16:10:13 +01001183
Victor Stinner13ff3962020-10-12 00:37:20 +02001184 for _ in range(10):
1185 time.sleep(0.005)
1186 self.mem_logger.info("not flushed")
1187 self.mem_logger.warning("flushed")
1188 finally:
1189 for thread in target.threads:
1190 threading_helper.join_thread(thread)
Irit Katriel2353d772020-08-16 16:10:13 +01001191
Christian Heimes180510d2008-03-03 19:15:45 +00001192
1193class ExceptionFormatter(logging.Formatter):
1194 """A special exception formatter."""
1195 def formatException(self, ei):
1196 return "Got a [%s]" % ei[0].__name__
1197
1198
1199class ConfigFileTest(BaseTest):
1200
1201 """Reading logging config from a .ini-style config file."""
1202
Hai Shi3ddc6342020-06-30 21:46:06 +08001203 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001204 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001205
1206 # config0 is a standard configuration.
1207 config0 = """
1208 [loggers]
1209 keys=root
1210
1211 [handlers]
1212 keys=hand1
1213
1214 [formatters]
1215 keys=form1
1216
1217 [logger_root]
1218 level=WARNING
1219 handlers=hand1
1220
1221 [handler_hand1]
1222 class=StreamHandler
1223 level=NOTSET
1224 formatter=form1
1225 args=(sys.stdout,)
1226
1227 [formatter_form1]
1228 format=%(levelname)s ++ %(message)s
1229 datefmt=
1230 """
1231
1232 # config1 adds a little to the standard configuration.
1233 config1 = """
1234 [loggers]
1235 keys=root,parser
1236
1237 [handlers]
1238 keys=hand1
1239
1240 [formatters]
1241 keys=form1
1242
1243 [logger_root]
1244 level=WARNING
1245 handlers=
1246
1247 [logger_parser]
1248 level=DEBUG
1249 handlers=hand1
1250 propagate=1
1251 qualname=compiler.parser
1252
1253 [handler_hand1]
1254 class=StreamHandler
1255 level=NOTSET
1256 formatter=form1
1257 args=(sys.stdout,)
1258
1259 [formatter_form1]
1260 format=%(levelname)s ++ %(message)s
1261 datefmt=
1262 """
1263
Vinay Sajip3f84b072011-03-07 17:49:33 +00001264 # config1a moves the handler to the root.
1265 config1a = """
1266 [loggers]
1267 keys=root,parser
1268
1269 [handlers]
1270 keys=hand1
1271
1272 [formatters]
1273 keys=form1
1274
1275 [logger_root]
1276 level=WARNING
1277 handlers=hand1
1278
1279 [logger_parser]
1280 level=DEBUG
1281 handlers=
1282 propagate=1
1283 qualname=compiler.parser
1284
1285 [handler_hand1]
1286 class=StreamHandler
1287 level=NOTSET
1288 formatter=form1
1289 args=(sys.stdout,)
1290
1291 [formatter_form1]
1292 format=%(levelname)s ++ %(message)s
1293 datefmt=
1294 """
1295
Christian Heimes180510d2008-03-03 19:15:45 +00001296 # config2 has a subtle configuration error that should be reported
1297 config2 = config1.replace("sys.stdout", "sys.stbout")
1298
1299 # config3 has a less subtle configuration error
1300 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1301
1302 # config4 specifies a custom formatter class to be loaded
1303 config4 = """
1304 [loggers]
1305 keys=root
1306
1307 [handlers]
1308 keys=hand1
1309
1310 [formatters]
1311 keys=form1
1312
1313 [logger_root]
1314 level=NOTSET
1315 handlers=hand1
1316
1317 [handler_hand1]
1318 class=StreamHandler
1319 level=NOTSET
1320 formatter=form1
1321 args=(sys.stdout,)
1322
1323 [formatter_form1]
1324 class=""" + __name__ + """.ExceptionFormatter
1325 format=%(levelname)s:%(name)s:%(message)s
1326 datefmt=
1327 """
1328
Georg Brandl3dbca812008-07-23 16:10:53 +00001329 # config5 specifies a custom handler class to be loaded
1330 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1331
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001332 # config6 uses ', ' delimiters in the handlers and formatters sections
1333 config6 = """
1334 [loggers]
1335 keys=root,parser
1336
1337 [handlers]
1338 keys=hand1, hand2
1339
1340 [formatters]
1341 keys=form1, form2
1342
1343 [logger_root]
1344 level=WARNING
1345 handlers=
1346
1347 [logger_parser]
1348 level=DEBUG
1349 handlers=hand1
1350 propagate=1
1351 qualname=compiler.parser
1352
1353 [handler_hand1]
1354 class=StreamHandler
1355 level=NOTSET
1356 formatter=form1
1357 args=(sys.stdout,)
1358
1359 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001360 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001361 level=NOTSET
1362 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001363 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001364
1365 [formatter_form1]
1366 format=%(levelname)s ++ %(message)s
1367 datefmt=
1368
1369 [formatter_form2]
1370 format=%(message)s
1371 datefmt=
1372 """
1373
Preston Landers6ea56d22017-08-02 15:44:28 -05001374 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001375 config7 = """
1376 [loggers]
1377 keys=root,parser,compiler
1378
1379 [handlers]
1380 keys=hand1
1381
1382 [formatters]
1383 keys=form1
1384
1385 [logger_root]
1386 level=WARNING
1387 handlers=hand1
1388
1389 [logger_compiler]
1390 level=DEBUG
1391 handlers=
1392 propagate=1
1393 qualname=compiler
1394
1395 [logger_parser]
1396 level=DEBUG
1397 handlers=
1398 propagate=1
1399 qualname=compiler.parser
1400
1401 [handler_hand1]
1402 class=StreamHandler
1403 level=NOTSET
1404 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001405 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001406
1407 [formatter_form1]
1408 format=%(levelname)s ++ %(message)s
1409 datefmt=
1410 """
1411
Xtreak087570a2018-07-02 14:27:46 +05301412 # config 8, check for resource warning
1413 config8 = r"""
1414 [loggers]
1415 keys=root
1416
1417 [handlers]
1418 keys=file
1419
1420 [formatters]
1421 keys=
1422
1423 [logger_root]
1424 level=DEBUG
1425 handlers=file
1426
1427 [handler_file]
1428 class=FileHandler
1429 level=DEBUG
1430 args=("{tempfile}",)
1431 """
1432
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001433 disable_test = """
1434 [loggers]
1435 keys=root
1436
1437 [handlers]
1438 keys=screen
1439
1440 [formatters]
1441 keys=
1442
1443 [logger_root]
1444 level=DEBUG
1445 handlers=screen
1446
1447 [handler_screen]
1448 level=DEBUG
1449 class=StreamHandler
1450 args=(sys.stdout,)
1451 formatter=
1452 """
1453
1454 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001455 file = io.StringIO(textwrap.dedent(conf))
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001456 logging.config.fileConfig(file, **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001457
1458 def test_config0_ok(self):
1459 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001460 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001461 self.apply_config(self.config0)
1462 logger = logging.getLogger()
1463 # Won't output anything
1464 logger.info(self.next_message())
1465 # Outputs a message
1466 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001467 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001468 ('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
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001473 def test_config0_using_cp_ok(self):
1474 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001475 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001476 file = io.StringIO(textwrap.dedent(self.config0))
1477 cp = configparser.ConfigParser()
1478 cp.read_file(file)
1479 logging.config.fileConfig(cp)
1480 logger = logging.getLogger()
1481 # Won't output anything
1482 logger.info(self.next_message())
1483 # Outputs a message
1484 logger.error(self.next_message())
1485 self.assert_log_lines([
1486 ('ERROR', '2'),
1487 ], stream=output)
1488 # Original logger output is empty.
1489 self.assert_log_lines([])
1490
Georg Brandl3dbca812008-07-23 16:10:53 +00001491 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001492 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001493 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001494 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001495 logger = logging.getLogger("compiler.parser")
1496 # Both will output a message
1497 logger.info(self.next_message())
1498 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001499 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001500 ('INFO', '1'),
1501 ('ERROR', '2'),
1502 ], stream=output)
1503 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001504 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001505
1506 def test_config2_failure(self):
1507 # A simple config file which overrides the default settings.
1508 self.assertRaises(Exception, self.apply_config, self.config2)
1509
1510 def test_config3_failure(self):
1511 # A simple config file which overrides the default settings.
1512 self.assertRaises(Exception, self.apply_config, self.config3)
1513
1514 def test_config4_ok(self):
1515 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001516 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001517 self.apply_config(self.config4)
1518 logger = logging.getLogger()
1519 try:
1520 raise RuntimeError()
1521 except RuntimeError:
1522 logging.exception("just testing")
1523 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001524 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001525 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1526 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001527 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001528
Georg Brandl3dbca812008-07-23 16:10:53 +00001529 def test_config5_ok(self):
1530 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001531
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001532 def test_config6_ok(self):
1533 self.test_config1_ok(config=self.config6)
1534
Vinay Sajip3f84b072011-03-07 17:49:33 +00001535 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001536 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001537 self.apply_config(self.config1a)
1538 logger = logging.getLogger("compiler.parser")
1539 # See issue #11424. compiler-hyphenated sorts
1540 # between compiler and compiler.xyz and this
1541 # was preventing compiler.xyz from being included
1542 # in the child loggers of compiler because of an
1543 # overzealous loop termination condition.
1544 hyphenated = logging.getLogger('compiler-hyphenated')
1545 # All will output a message
1546 logger.info(self.next_message())
1547 logger.error(self.next_message())
1548 hyphenated.critical(self.next_message())
1549 self.assert_log_lines([
1550 ('INFO', '1'),
1551 ('ERROR', '2'),
1552 ('CRITICAL', '3'),
1553 ], stream=output)
1554 # Original logger output is empty.
1555 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001556 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001557 self.apply_config(self.config7)
1558 logger = logging.getLogger("compiler.parser")
1559 self.assertFalse(logger.disabled)
1560 # Both will output a message
1561 logger.info(self.next_message())
1562 logger.error(self.next_message())
1563 logger = logging.getLogger("compiler.lexer")
1564 # Both will output a message
1565 logger.info(self.next_message())
1566 logger.error(self.next_message())
1567 # Will not appear
1568 hyphenated.critical(self.next_message())
1569 self.assert_log_lines([
1570 ('INFO', '4'),
1571 ('ERROR', '5'),
1572 ('INFO', '6'),
1573 ('ERROR', '7'),
1574 ], stream=output)
1575 # Original logger output is empty.
1576 self.assert_log_lines([])
1577
Xtreak087570a2018-07-02 14:27:46 +05301578 def test_config8_ok(self):
1579
1580 def cleanup(h1, fn):
1581 h1.close()
1582 os.remove(fn)
1583
1584 with self.check_no_resource_warning():
1585 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1586 os.close(fd)
1587
1588 # Replace single backslash with double backslash in windows
1589 # to avoid unicode error during string formatting
1590 if os.name == "nt":
1591 fn = fn.replace("\\", "\\\\")
1592
1593 config8 = self.config8.format(tempfile=fn)
1594
1595 self.apply_config(config8)
1596 self.apply_config(config8)
1597
1598 handler = logging.root.handlers[0]
1599 self.addCleanup(cleanup, handler, fn)
1600
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001601 def test_logger_disabling(self):
1602 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001603 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001604 self.assertFalse(logger.disabled)
1605 self.apply_config(self.disable_test)
1606 self.assertTrue(logger.disabled)
1607 self.apply_config(self.disable_test, disable_existing_loggers=False)
1608 self.assertFalse(logger.disabled)
1609
Lucas Cimonb15100f2019-10-31 09:06:25 +01001610 def test_config_set_handler_names(self):
1611 test_config = """
1612 [loggers]
1613 keys=root
1614
1615 [handlers]
1616 keys=hand1
1617
1618 [formatters]
1619 keys=form1
1620
1621 [logger_root]
1622 handlers=hand1
1623
1624 [handler_hand1]
1625 class=StreamHandler
1626 formatter=form1
1627
1628 [formatter_form1]
1629 format=%(levelname)s ++ %(message)s
1630 """
1631 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001632 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001633
Łukasz Langa214f18e2018-06-08 04:02:48 -07001634 def test_defaults_do_no_interpolation(self):
1635 """bpo-33802 defaults should not get interpolated"""
1636 ini = textwrap.dedent("""
1637 [formatters]
1638 keys=default
1639
1640 [formatter_default]
1641
1642 [handlers]
1643 keys=console
1644
1645 [handler_console]
1646 class=logging.StreamHandler
1647 args=tuple()
1648
1649 [loggers]
1650 keys=root
1651
1652 [logger_root]
1653 formatter=default
1654 handlers=console
1655 """).strip()
1656 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1657 try:
1658 os.write(fd, ini.encode('ascii'))
1659 os.close(fd)
1660 logging.config.fileConfig(
1661 fn,
1662 defaults=dict(
1663 version=1,
1664 disable_existing_loggers=False,
1665 formatters={
1666 "generic": {
1667 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1668 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1669 "class": "logging.Formatter"
1670 },
1671 },
1672 )
1673 )
1674 finally:
1675 os.unlink(fn)
1676
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001677
Christian Heimes180510d2008-03-03 19:15:45 +00001678class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001679
Christian Heimes180510d2008-03-03 19:15:45 +00001680 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001681
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001682 server_class = TestTCPServer
1683 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001684
Christian Heimes180510d2008-03-03 19:15:45 +00001685 def setUp(self):
1686 """Set up a TCP server to receive log messages, and a SocketHandler
1687 pointing to that server's address and port."""
1688 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001689 # Issue #29177: deal with errors that happen during setup
1690 self.server = self.sock_hdlr = self.server_exception = None
1691 try:
1692 self.server = server = self.server_class(self.address,
1693 self.handle_socket, 0.01)
1694 server.start()
1695 # Uncomment next line to test error recovery in setUp()
1696 # raise OSError('dummy error raised')
1697 except OSError as e:
1698 self.server_exception = e
1699 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001700 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001701 hcls = logging.handlers.SocketHandler
1702 if isinstance(server.server_address, tuple):
1703 self.sock_hdlr = hcls('localhost', server.port)
1704 else:
1705 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001706 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001707 self.root_logger.removeHandler(self.root_logger.handlers[0])
1708 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001709 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001710
Christian Heimes180510d2008-03-03 19:15:45 +00001711 def tearDown(self):
1712 """Shutdown the TCP server."""
1713 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001714 if self.sock_hdlr:
1715 self.root_logger.removeHandler(self.sock_hdlr)
1716 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001717 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001718 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001719 finally:
1720 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001721
Vinay Sajip7367d082011-05-02 13:17:27 +01001722 def handle_socket(self, request):
1723 conn = request.connection
1724 while True:
1725 chunk = conn.recv(4)
1726 if len(chunk) < 4:
1727 break
1728 slen = struct.unpack(">L", chunk)[0]
1729 chunk = conn.recv(slen)
1730 while len(chunk) < slen:
1731 chunk = chunk + conn.recv(slen - len(chunk))
1732 obj = pickle.loads(chunk)
1733 record = logging.makeLogRecord(obj)
1734 self.log_output += record.msg + '\n'
1735 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001736
Christian Heimes180510d2008-03-03 19:15:45 +00001737 def test_output(self):
1738 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001739 if self.server_exception:
1740 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001741 logger = logging.getLogger("tcp")
1742 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001743 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001744 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001745 self.handled.acquire()
1746 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001747
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001748 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001749 if self.server_exception:
1750 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001751 # Avoid timing-related failures due to SocketHandler's own hard-wired
1752 # one-second timeout on socket.create_connection() (issue #16264).
1753 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001754 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001755 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001756 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001757 try:
1758 raise RuntimeError('Deliberate mistake')
1759 except RuntimeError:
1760 self.root_logger.exception('Never sent')
1761 self.root_logger.error('Never sent, either')
1762 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001763 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001764 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1765 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001766
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001767def _get_temp_domain_socket():
1768 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1769 os.close(fd)
1770 # just need a name - file can't be present, or we'll get an
1771 # 'address already in use' error.
1772 os.remove(fn)
1773 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001774
Victor Stinnerec5a8602014-06-02 14:41:51 +02001775@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001776class UnixSocketHandlerTest(SocketHandlerTest):
1777
1778 """Test for SocketHandler with unix sockets."""
1779
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001780 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001781 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001782
1783 def setUp(self):
1784 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001785 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001786 SocketHandlerTest.setUp(self)
1787
1788 def tearDown(self):
1789 SocketHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001790 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001791
Vinay Sajip7367d082011-05-02 13:17:27 +01001792class DatagramHandlerTest(BaseTest):
1793
1794 """Test for DatagramHandler."""
1795
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001796 server_class = TestUDPServer
1797 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001798
Vinay Sajip7367d082011-05-02 13:17:27 +01001799 def setUp(self):
1800 """Set up a UDP server to receive log messages, and a DatagramHandler
1801 pointing to that server's address and port."""
1802 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001803 # Issue #29177: deal with errors that happen during setup
1804 self.server = self.sock_hdlr = self.server_exception = None
1805 try:
1806 self.server = server = self.server_class(self.address,
1807 self.handle_datagram, 0.01)
1808 server.start()
1809 # Uncomment next line to test error recovery in setUp()
1810 # raise OSError('dummy error raised')
1811 except OSError as e:
1812 self.server_exception = e
1813 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001814 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001815 hcls = logging.handlers.DatagramHandler
1816 if isinstance(server.server_address, tuple):
1817 self.sock_hdlr = hcls('localhost', server.port)
1818 else:
1819 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001820 self.log_output = ''
1821 self.root_logger.removeHandler(self.root_logger.handlers[0])
1822 self.root_logger.addHandler(self.sock_hdlr)
1823 self.handled = threading.Event()
1824
1825 def tearDown(self):
1826 """Shutdown the UDP server."""
1827 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001828 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001829 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001830 if self.sock_hdlr:
1831 self.root_logger.removeHandler(self.sock_hdlr)
1832 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001833 finally:
1834 BaseTest.tearDown(self)
1835
1836 def handle_datagram(self, request):
1837 slen = struct.pack('>L', 0) # length of prefix
1838 packet = request.packet[len(slen):]
1839 obj = pickle.loads(packet)
1840 record = logging.makeLogRecord(obj)
1841 self.log_output += record.msg + '\n'
1842 self.handled.set()
1843
1844 def test_output(self):
1845 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001846 if self.server_exception:
1847 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001848 logger = logging.getLogger("udp")
1849 logger.error("spam")
1850 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001851 self.handled.clear()
1852 logger.error("eggs")
1853 self.handled.wait()
1854 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001855
Victor Stinnerec5a8602014-06-02 14:41:51 +02001856@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001857class UnixDatagramHandlerTest(DatagramHandlerTest):
1858
1859 """Test for DatagramHandler using Unix sockets."""
1860
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001861 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001862 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001863
1864 def setUp(self):
1865 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001866 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001867 DatagramHandlerTest.setUp(self)
1868
1869 def tearDown(self):
1870 DatagramHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001871 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001872
Vinay Sajip7367d082011-05-02 13:17:27 +01001873class SysLogHandlerTest(BaseTest):
1874
1875 """Test for SysLogHandler using UDP."""
1876
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001877 server_class = TestUDPServer
1878 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001879
Vinay Sajip7367d082011-05-02 13:17:27 +01001880 def setUp(self):
1881 """Set up a UDP server to receive log messages, and a SysLogHandler
1882 pointing to that server's address and port."""
1883 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001884 # Issue #29177: deal with errors that happen during setup
1885 self.server = self.sl_hdlr = self.server_exception = None
1886 try:
1887 self.server = server = self.server_class(self.address,
1888 self.handle_datagram, 0.01)
1889 server.start()
1890 # Uncomment next line to test error recovery in setUp()
1891 # raise OSError('dummy error raised')
1892 except OSError as e:
1893 self.server_exception = e
1894 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001895 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001896 hcls = logging.handlers.SysLogHandler
1897 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001898 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001899 else:
1900 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001901 self.log_output = ''
1902 self.root_logger.removeHandler(self.root_logger.handlers[0])
1903 self.root_logger.addHandler(self.sl_hdlr)
1904 self.handled = threading.Event()
1905
1906 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001907 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001908 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001909 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001910 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001911 if self.sl_hdlr:
1912 self.root_logger.removeHandler(self.sl_hdlr)
1913 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001914 finally:
1915 BaseTest.tearDown(self)
1916
1917 def handle_datagram(self, request):
1918 self.log_output = request.packet
1919 self.handled.set()
1920
1921 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001922 if self.server_exception:
1923 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001924 # The log message sent to the SysLogHandler is properly received.
1925 logger = logging.getLogger("slh")
1926 logger.error("sp\xe4m")
1927 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001928 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001929 self.handled.clear()
1930 self.sl_hdlr.append_nul = False
1931 logger.error("sp\xe4m")
1932 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001933 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001934 self.handled.clear()
1935 self.sl_hdlr.ident = "h\xe4m-"
1936 logger.error("sp\xe4m")
1937 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001938 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001939
Victor Stinnerec5a8602014-06-02 14:41:51 +02001940@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001941class UnixSysLogHandlerTest(SysLogHandlerTest):
1942
1943 """Test for SysLogHandler with Unix sockets."""
1944
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001945 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001946 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001947
1948 def setUp(self):
1949 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001950 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001951 SysLogHandlerTest.setUp(self)
1952
1953 def tearDown(self):
1954 SysLogHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001955 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001956
Serhiy Storchaka16994912020-04-25 10:06:29 +03001957@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001958 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001959class IPv6SysLogHandlerTest(SysLogHandlerTest):
1960
1961 """Test for SysLogHandler with IPv6 host."""
1962
1963 server_class = TestUDPServer
1964 address = ('::1', 0)
1965
1966 def setUp(self):
1967 self.server_class.address_family = socket.AF_INET6
1968 super(IPv6SysLogHandlerTest, self).setUp()
1969
1970 def tearDown(self):
1971 self.server_class.address_family = socket.AF_INET
1972 super(IPv6SysLogHandlerTest, self).tearDown()
1973
Vinay Sajip7367d082011-05-02 13:17:27 +01001974class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001975 """Test for HTTPHandler."""
1976
1977 def setUp(self):
1978 """Set up an HTTP server to receive log messages, and a HTTPHandler
1979 pointing to that server's address and port."""
1980 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001981 self.handled = threading.Event()
1982
Vinay Sajip7367d082011-05-02 13:17:27 +01001983 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001984 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001985 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001986 if self.command == 'POST':
1987 try:
1988 rlen = int(request.headers['Content-Length'])
1989 self.post_data = request.rfile.read(rlen)
1990 except:
1991 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001992 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001993 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001994 self.handled.set()
1995
1996 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001997 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001998 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01001999 root_logger = self.root_logger
2000 root_logger.removeHandler(self.root_logger.handlers[0])
2001 for secure in (False, True):
2002 addr = ('localhost', 0)
2003 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01002004 try:
2005 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01002006 except ImportError:
2007 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002008 else:
2009 here = os.path.dirname(__file__)
2010 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02002011 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002012 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06002013
2014 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01002015 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01002016 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06002017 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01002018 self.server = server = TestHTTPServer(addr, self.handle_request,
2019 0.01, sslctx=sslctx)
2020 server.start()
2021 server.ready.wait()
2022 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01002023 secure_client = secure and sslctx
2024 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06002025 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01002026 context=context,
2027 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01002028 self.log_data = None
2029 root_logger.addHandler(self.h_hdlr)
2030
2031 for method in ('GET', 'POST'):
2032 self.h_hdlr.method = method
2033 self.handled.clear()
2034 msg = "sp\xe4m"
2035 logger.error(msg)
2036 self.handled.wait()
2037 self.assertEqual(self.log_data.path, '/frob')
2038 self.assertEqual(self.command, method)
2039 if method == 'GET':
2040 d = parse_qs(self.log_data.query)
2041 else:
2042 d = parse_qs(self.post_data.decode('utf-8'))
2043 self.assertEqual(d['name'], ['http'])
2044 self.assertEqual(d['funcName'], ['test_output'])
2045 self.assertEqual(d['msg'], [msg])
2046
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002047 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002048 self.root_logger.removeHandler(self.h_hdlr)
2049 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002050
Christian Heimes180510d2008-03-03 19:15:45 +00002051class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002052
Christian Heimes180510d2008-03-03 19:15:45 +00002053 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002054
Christian Heimes180510d2008-03-03 19:15:45 +00002055 def setUp(self):
2056 """Create a dict to remember potentially destroyed objects."""
2057 BaseTest.setUp(self)
2058 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002059
Christian Heimes180510d2008-03-03 19:15:45 +00002060 def _watch_for_survival(self, *args):
2061 """Watch the given objects for survival, by creating weakrefs to
2062 them."""
2063 for obj in args:
2064 key = id(obj), repr(obj)
2065 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002066
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002067 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002068 """Assert that all objects watched for survival have survived."""
2069 # Trigger cycle breaking.
2070 gc.collect()
2071 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002072 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002073 if ref() is None:
2074 dead.append(repr_)
2075 if dead:
2076 self.fail("%d objects should have survived "
2077 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002078
Christian Heimes180510d2008-03-03 19:15:45 +00002079 def test_persistent_loggers(self):
2080 # Logger objects are persistent and retain their configuration, even
2081 # if visible references are destroyed.
2082 self.root_logger.setLevel(logging.INFO)
2083 foo = logging.getLogger("foo")
2084 self._watch_for_survival(foo)
2085 foo.setLevel(logging.DEBUG)
2086 self.root_logger.debug(self.next_message())
2087 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002088 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002089 ('foo', 'DEBUG', '2'),
2090 ])
2091 del foo
2092 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002093 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002094 # foo has retained its settings.
2095 bar = logging.getLogger("foo")
2096 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002097 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002098 ('foo', 'DEBUG', '2'),
2099 ('foo', 'DEBUG', '3'),
2100 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002101
Benjamin Petersonf91df042009-02-13 02:50:59 +00002102
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002103class EncodingTest(BaseTest):
2104 def test_encoding_plain_file(self):
2105 # In Python 2.x, a plain file object is treated as having no encoding.
2106 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002107 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2108 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002109 # the non-ascii data we write to the log.
2110 data = "foo\x80"
2111 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002112 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002113 log.addHandler(handler)
2114 try:
2115 # write non-ascii data to the log.
2116 log.warning(data)
2117 finally:
2118 log.removeHandler(handler)
2119 handler.close()
2120 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002121 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002122 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002123 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002124 finally:
2125 f.close()
2126 finally:
2127 if os.path.isfile(fn):
2128 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002129
Benjamin Petersonf91df042009-02-13 02:50:59 +00002130 def test_encoding_cyrillic_unicode(self):
2131 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002132 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002133 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002134 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002135 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002136 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002137 stream = io.BytesIO()
2138 writer = writer_class(stream, 'strict')
2139 handler = logging.StreamHandler(writer)
2140 log.addHandler(handler)
2141 try:
2142 log.warning(message)
2143 finally:
2144 log.removeHandler(handler)
2145 handler.close()
2146 # check we wrote exactly those bytes, ignoring trailing \n etc
2147 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002148 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002149 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2150
2151
Georg Brandlf9734072008-12-07 15:30:06 +00002152class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002153
Georg Brandlf9734072008-12-07 15:30:06 +00002154 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002155 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002156 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002157 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002158 warnings.filterwarnings("always", category=UserWarning)
2159 stream = io.StringIO()
2160 h = logging.StreamHandler(stream)
2161 logger = logging.getLogger("py.warnings")
2162 logger.addHandler(h)
2163 warnings.warn("I'm warning you...")
2164 logger.removeHandler(h)
2165 s = stream.getvalue()
2166 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002167 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002168
Mike53f7a7c2017-12-14 14:04:53 +03002169 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002170 a_file = io.StringIO()
2171 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2172 a_file, "Dummy line")
2173 s = a_file.getvalue()
2174 a_file.close()
2175 self.assertEqual(s,
2176 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2177
2178 def test_warnings_no_handlers(self):
2179 with warnings.catch_warnings():
2180 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002181 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002182
2183 # confirm our assumption: no loggers are set
2184 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002185 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002186
2187 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002188 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002189 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002190
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002191
2192def formatFunc(format, datefmt=None):
2193 return logging.Formatter(format, datefmt)
2194
BNMetrics18fb1fb2018-10-15 19:41:36 +01002195class myCustomFormatter:
2196 def __init__(self, fmt, datefmt=None):
2197 pass
2198
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002199def handlerFunc():
2200 return logging.StreamHandler()
2201
2202class CustomHandler(logging.StreamHandler):
2203 pass
2204
2205class ConfigDictTest(BaseTest):
2206
2207 """Reading logging config from a dictionary."""
2208
Hai Shi3ddc6342020-06-30 21:46:06 +08002209 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002210 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002211
2212 # config0 is a standard configuration.
2213 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002214 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002215 'formatters': {
2216 'form1' : {
2217 'format' : '%(levelname)s ++ %(message)s',
2218 },
2219 },
2220 'handlers' : {
2221 'hand1' : {
2222 'class' : 'logging.StreamHandler',
2223 'formatter' : 'form1',
2224 'level' : 'NOTSET',
2225 'stream' : 'ext://sys.stdout',
2226 },
2227 },
2228 'root' : {
2229 'level' : 'WARNING',
2230 'handlers' : ['hand1'],
2231 },
2232 }
2233
2234 # config1 adds a little to the standard configuration.
2235 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002236 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002237 'formatters': {
2238 'form1' : {
2239 'format' : '%(levelname)s ++ %(message)s',
2240 },
2241 },
2242 'handlers' : {
2243 'hand1' : {
2244 'class' : 'logging.StreamHandler',
2245 'formatter' : 'form1',
2246 'level' : 'NOTSET',
2247 'stream' : 'ext://sys.stdout',
2248 },
2249 },
2250 'loggers' : {
2251 'compiler.parser' : {
2252 'level' : 'DEBUG',
2253 'handlers' : ['hand1'],
2254 },
2255 },
2256 'root' : {
2257 'level' : 'WARNING',
2258 },
2259 }
2260
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002261 # config1a moves the handler to the root. Used with config8a
2262 config1a = {
2263 'version': 1,
2264 'formatters': {
2265 'form1' : {
2266 'format' : '%(levelname)s ++ %(message)s',
2267 },
2268 },
2269 'handlers' : {
2270 'hand1' : {
2271 'class' : 'logging.StreamHandler',
2272 'formatter' : 'form1',
2273 'level' : 'NOTSET',
2274 'stream' : 'ext://sys.stdout',
2275 },
2276 },
2277 'loggers' : {
2278 'compiler.parser' : {
2279 'level' : 'DEBUG',
2280 },
2281 },
2282 'root' : {
2283 'level' : 'WARNING',
2284 'handlers' : ['hand1'],
2285 },
2286 }
2287
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002288 # config2 has a subtle configuration error that should be reported
2289 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002290 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002291 'formatters': {
2292 'form1' : {
2293 'format' : '%(levelname)s ++ %(message)s',
2294 },
2295 },
2296 'handlers' : {
2297 'hand1' : {
2298 'class' : 'logging.StreamHandler',
2299 'formatter' : 'form1',
2300 'level' : 'NOTSET',
2301 'stream' : 'ext://sys.stdbout',
2302 },
2303 },
2304 'loggers' : {
2305 'compiler.parser' : {
2306 'level' : 'DEBUG',
2307 'handlers' : ['hand1'],
2308 },
2309 },
2310 'root' : {
2311 'level' : 'WARNING',
2312 },
2313 }
2314
Mike53f7a7c2017-12-14 14:04:53 +03002315 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002316 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002317 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002318 'formatters': {
2319 'form1' : {
2320 'format' : '%(levelname)s ++ %(message)s',
2321 },
2322 },
2323 'handlers' : {
2324 'hand1' : {
2325 'class' : 'logging.StreamHandler',
2326 'formatter' : 'form1',
2327 'level' : 'NTOSET',
2328 'stream' : 'ext://sys.stdout',
2329 },
2330 },
2331 'loggers' : {
2332 'compiler.parser' : {
2333 'level' : 'DEBUG',
2334 'handlers' : ['hand1'],
2335 },
2336 },
2337 'root' : {
2338 'level' : 'WARNING',
2339 },
2340 }
2341
2342
Mike53f7a7c2017-12-14 14:04:53 +03002343 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002344 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002345 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002346 'formatters': {
2347 'form1' : {
2348 'format' : '%(levelname)s ++ %(message)s',
2349 },
2350 },
2351 'handlers' : {
2352 'hand1' : {
2353 'class' : 'logging.StreamHandler',
2354 'formatter' : 'form1',
2355 'level' : 'NOTSET',
2356 'stream' : 'ext://sys.stdout',
2357 },
2358 },
2359 'loggers' : {
2360 'compiler.parser' : {
2361 'level' : 'DEBUG',
2362 'handlers' : ['hand1'],
2363 },
2364 },
2365 'root' : {
2366 'level' : 'WRANING',
2367 },
2368 }
2369
2370 # config3 has a less subtle configuration error
2371 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002372 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002373 'formatters': {
2374 'form1' : {
2375 'format' : '%(levelname)s ++ %(message)s',
2376 },
2377 },
2378 'handlers' : {
2379 'hand1' : {
2380 'class' : 'logging.StreamHandler',
2381 'formatter' : 'misspelled_name',
2382 'level' : 'NOTSET',
2383 'stream' : 'ext://sys.stdout',
2384 },
2385 },
2386 'loggers' : {
2387 'compiler.parser' : {
2388 'level' : 'DEBUG',
2389 'handlers' : ['hand1'],
2390 },
2391 },
2392 'root' : {
2393 'level' : 'WARNING',
2394 },
2395 }
2396
2397 # config4 specifies a custom formatter class to be loaded
2398 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002399 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002400 'formatters': {
2401 'form1' : {
2402 '()' : __name__ + '.ExceptionFormatter',
2403 'format' : '%(levelname)s:%(name)s:%(message)s',
2404 },
2405 },
2406 'handlers' : {
2407 'hand1' : {
2408 'class' : 'logging.StreamHandler',
2409 'formatter' : 'form1',
2410 'level' : 'NOTSET',
2411 'stream' : 'ext://sys.stdout',
2412 },
2413 },
2414 'root' : {
2415 'level' : 'NOTSET',
2416 'handlers' : ['hand1'],
2417 },
2418 }
2419
2420 # As config4 but using an actual callable rather than a string
2421 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002422 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002423 'formatters': {
2424 'form1' : {
2425 '()' : ExceptionFormatter,
2426 'format' : '%(levelname)s:%(name)s:%(message)s',
2427 },
2428 'form2' : {
2429 '()' : __name__ + '.formatFunc',
2430 'format' : '%(levelname)s:%(name)s:%(message)s',
2431 },
2432 'form3' : {
2433 '()' : formatFunc,
2434 'format' : '%(levelname)s:%(name)s:%(message)s',
2435 },
2436 },
2437 'handlers' : {
2438 'hand1' : {
2439 'class' : 'logging.StreamHandler',
2440 'formatter' : 'form1',
2441 'level' : 'NOTSET',
2442 'stream' : 'ext://sys.stdout',
2443 },
2444 'hand2' : {
2445 '()' : handlerFunc,
2446 },
2447 },
2448 'root' : {
2449 'level' : 'NOTSET',
2450 'handlers' : ['hand1'],
2451 },
2452 }
2453
2454 # config5 specifies a custom handler class to be loaded
2455 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002456 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002457 'formatters': {
2458 'form1' : {
2459 'format' : '%(levelname)s ++ %(message)s',
2460 },
2461 },
2462 'handlers' : {
2463 'hand1' : {
2464 'class' : __name__ + '.CustomHandler',
2465 'formatter' : 'form1',
2466 'level' : 'NOTSET',
2467 'stream' : 'ext://sys.stdout',
2468 },
2469 },
2470 'loggers' : {
2471 'compiler.parser' : {
2472 'level' : 'DEBUG',
2473 'handlers' : ['hand1'],
2474 },
2475 },
2476 'root' : {
2477 'level' : 'WARNING',
2478 },
2479 }
2480
2481 # config6 specifies a custom handler class to be loaded
2482 # but has bad arguments
2483 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002484 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002485 'formatters': {
2486 'form1' : {
2487 'format' : '%(levelname)s ++ %(message)s',
2488 },
2489 },
2490 'handlers' : {
2491 'hand1' : {
2492 'class' : __name__ + '.CustomHandler',
2493 'formatter' : 'form1',
2494 'level' : 'NOTSET',
2495 'stream' : 'ext://sys.stdout',
2496 '9' : 'invalid parameter name',
2497 },
2498 },
2499 'loggers' : {
2500 'compiler.parser' : {
2501 'level' : 'DEBUG',
2502 'handlers' : ['hand1'],
2503 },
2504 },
2505 'root' : {
2506 'level' : 'WARNING',
2507 },
2508 }
2509
Mike53f7a7c2017-12-14 14:04:53 +03002510 # config 7 does not define compiler.parser but defines compiler.lexer
2511 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002512 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002513 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002514 'formatters': {
2515 'form1' : {
2516 'format' : '%(levelname)s ++ %(message)s',
2517 },
2518 },
2519 'handlers' : {
2520 'hand1' : {
2521 'class' : 'logging.StreamHandler',
2522 'formatter' : 'form1',
2523 'level' : 'NOTSET',
2524 'stream' : 'ext://sys.stdout',
2525 },
2526 },
2527 'loggers' : {
2528 'compiler.lexer' : {
2529 'level' : 'DEBUG',
2530 'handlers' : ['hand1'],
2531 },
2532 },
2533 'root' : {
2534 'level' : 'WARNING',
2535 },
2536 }
2537
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002538 # config8 defines both compiler and compiler.lexer
2539 # so compiler.parser should not be disabled (since
2540 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002541 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002542 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002543 'disable_existing_loggers' : False,
2544 'formatters': {
2545 'form1' : {
2546 'format' : '%(levelname)s ++ %(message)s',
2547 },
2548 },
2549 'handlers' : {
2550 'hand1' : {
2551 'class' : 'logging.StreamHandler',
2552 'formatter' : 'form1',
2553 'level' : 'NOTSET',
2554 'stream' : 'ext://sys.stdout',
2555 },
2556 },
2557 'loggers' : {
2558 'compiler' : {
2559 'level' : 'DEBUG',
2560 'handlers' : ['hand1'],
2561 },
2562 'compiler.lexer' : {
2563 },
2564 },
2565 'root' : {
2566 'level' : 'WARNING',
2567 },
2568 }
2569
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002570 # config8a disables existing loggers
2571 config8a = {
2572 'version': 1,
2573 'disable_existing_loggers' : True,
2574 'formatters': {
2575 'form1' : {
2576 'format' : '%(levelname)s ++ %(message)s',
2577 },
2578 },
2579 'handlers' : {
2580 'hand1' : {
2581 'class' : 'logging.StreamHandler',
2582 'formatter' : 'form1',
2583 'level' : 'NOTSET',
2584 'stream' : 'ext://sys.stdout',
2585 },
2586 },
2587 'loggers' : {
2588 'compiler' : {
2589 'level' : 'DEBUG',
2590 'handlers' : ['hand1'],
2591 },
2592 'compiler.lexer' : {
2593 },
2594 },
2595 'root' : {
2596 'level' : 'WARNING',
2597 },
2598 }
2599
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002600 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002601 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002602 'formatters': {
2603 'form1' : {
2604 'format' : '%(levelname)s ++ %(message)s',
2605 },
2606 },
2607 'handlers' : {
2608 'hand1' : {
2609 'class' : 'logging.StreamHandler',
2610 'formatter' : 'form1',
2611 'level' : 'WARNING',
2612 'stream' : 'ext://sys.stdout',
2613 },
2614 },
2615 'loggers' : {
2616 'compiler.parser' : {
2617 'level' : 'WARNING',
2618 'handlers' : ['hand1'],
2619 },
2620 },
2621 'root' : {
2622 'level' : 'NOTSET',
2623 },
2624 }
2625
2626 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002627 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002628 'incremental' : True,
2629 'handlers' : {
2630 'hand1' : {
2631 'level' : 'WARNING',
2632 },
2633 },
2634 'loggers' : {
2635 'compiler.parser' : {
2636 'level' : 'INFO',
2637 },
2638 },
2639 }
2640
2641 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002642 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002643 'incremental' : True,
2644 'handlers' : {
2645 'hand1' : {
2646 'level' : 'INFO',
2647 },
2648 },
2649 'loggers' : {
2650 'compiler.parser' : {
2651 'level' : 'INFO',
2652 },
2653 },
2654 }
2655
Mike53f7a7c2017-12-14 14:04:53 +03002656 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002657 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002658 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002659 'formatters': {
2660 'form1' : {
2661 'format' : '%(levelname)s ++ %(message)s',
2662 },
2663 },
2664 'filters' : {
2665 'filt1' : {
2666 'name' : 'compiler.parser',
2667 },
2668 },
2669 'handlers' : {
2670 'hand1' : {
2671 'class' : 'logging.StreamHandler',
2672 'formatter' : 'form1',
2673 'level' : 'NOTSET',
2674 'stream' : 'ext://sys.stdout',
2675 'filters' : ['filt1'],
2676 },
2677 },
2678 'loggers' : {
2679 'compiler.parser' : {
2680 'level' : 'DEBUG',
2681 'filters' : ['filt1'],
2682 },
2683 },
2684 'root' : {
2685 'level' : 'WARNING',
2686 'handlers' : ['hand1'],
2687 },
2688 }
2689
Mike53f7a7c2017-12-14 14:04:53 +03002690 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002691 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002692 'version': 1,
2693 'true_formatters': {
2694 'form1' : {
2695 'format' : '%(levelname)s ++ %(message)s',
2696 },
2697 },
2698 'handler_configs': {
2699 'hand1' : {
2700 'class' : 'logging.StreamHandler',
2701 'formatter' : 'form1',
2702 'level' : 'NOTSET',
2703 'stream' : 'ext://sys.stdout',
2704 },
2705 },
2706 'formatters' : 'cfg://true_formatters',
2707 'handlers' : {
2708 'hand1' : 'cfg://handler_configs[hand1]',
2709 },
2710 'loggers' : {
2711 'compiler.parser' : {
2712 'level' : 'DEBUG',
2713 'handlers' : ['hand1'],
2714 },
2715 },
2716 'root' : {
2717 'level' : 'WARNING',
2718 },
2719 }
2720
Mike53f7a7c2017-12-14 14:04:53 +03002721 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002722 config12 = {
2723 'true_formatters': {
2724 'form1' : {
2725 'format' : '%(levelname)s ++ %(message)s',
2726 },
2727 },
2728 'handler_configs': {
2729 'hand1' : {
2730 'class' : 'logging.StreamHandler',
2731 'formatter' : 'form1',
2732 'level' : 'NOTSET',
2733 'stream' : 'ext://sys.stdout',
2734 },
2735 },
2736 'formatters' : 'cfg://true_formatters',
2737 'handlers' : {
2738 'hand1' : 'cfg://handler_configs[hand1]',
2739 },
2740 'loggers' : {
2741 'compiler.parser' : {
2742 'level' : 'DEBUG',
2743 'handlers' : ['hand1'],
2744 },
2745 },
2746 'root' : {
2747 'level' : 'WARNING',
2748 },
2749 }
2750
Mike53f7a7c2017-12-14 14:04:53 +03002751 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002752 config13 = {
2753 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002754 'true_formatters': {
2755 'form1' : {
2756 'format' : '%(levelname)s ++ %(message)s',
2757 },
2758 },
2759 'handler_configs': {
2760 'hand1' : {
2761 'class' : 'logging.StreamHandler',
2762 'formatter' : 'form1',
2763 'level' : 'NOTSET',
2764 'stream' : 'ext://sys.stdout',
2765 },
2766 },
2767 'formatters' : 'cfg://true_formatters',
2768 'handlers' : {
2769 'hand1' : 'cfg://handler_configs[hand1]',
2770 },
2771 'loggers' : {
2772 'compiler.parser' : {
2773 'level' : 'DEBUG',
2774 'handlers' : ['hand1'],
2775 },
2776 },
2777 'root' : {
2778 'level' : 'WARNING',
2779 },
2780 }
2781
Vinay Sajip8d270232012-11-15 14:20:18 +00002782 # As config0, but with properties
2783 config14 = {
2784 'version': 1,
2785 'formatters': {
2786 'form1' : {
2787 'format' : '%(levelname)s ++ %(message)s',
2788 },
2789 },
2790 'handlers' : {
2791 'hand1' : {
2792 'class' : 'logging.StreamHandler',
2793 'formatter' : 'form1',
2794 'level' : 'NOTSET',
2795 'stream' : 'ext://sys.stdout',
2796 '.': {
2797 'foo': 'bar',
2798 'terminator': '!\n',
2799 }
2800 },
2801 },
2802 'root' : {
2803 'level' : 'WARNING',
2804 'handlers' : ['hand1'],
2805 },
2806 }
2807
Vinay Sajip3f885b52013-03-22 15:19:54 +00002808 out_of_order = {
2809 "version": 1,
2810 "formatters": {
2811 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002812 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2813 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002814 }
2815 },
2816 "handlers": {
2817 "fileGlobal": {
2818 "class": "logging.StreamHandler",
2819 "level": "DEBUG",
2820 "formatter": "mySimpleFormatter"
2821 },
2822 "bufferGlobal": {
2823 "class": "logging.handlers.MemoryHandler",
2824 "capacity": 5,
2825 "formatter": "mySimpleFormatter",
2826 "target": "fileGlobal",
2827 "level": "DEBUG"
2828 }
2829 },
2830 "loggers": {
2831 "mymodule": {
2832 "level": "DEBUG",
2833 "handlers": ["bufferGlobal"],
2834 "propagate": "true"
2835 }
2836 }
2837 }
2838
BNMetrics18fb1fb2018-10-15 19:41:36 +01002839 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2840 custom_formatter_class_validate = {
2841 'version': 1,
2842 'formatters': {
2843 'form1': {
2844 '()': __name__ + '.ExceptionFormatter',
2845 'format': '%(levelname)s:%(name)s:%(message)s',
2846 'validate': False,
2847 },
2848 },
2849 'handlers' : {
2850 'hand1' : {
2851 'class': 'logging.StreamHandler',
2852 'formatter': 'form1',
2853 'level': 'NOTSET',
2854 'stream': 'ext://sys.stdout',
2855 },
2856 },
2857 "loggers": {
2858 "my_test_logger_custom_formatter": {
2859 "level": "DEBUG",
2860 "handlers": ["hand1"],
2861 "propagate": "true"
2862 }
2863 }
2864 }
2865
2866 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2867 custom_formatter_class_validate2 = {
2868 'version': 1,
2869 'formatters': {
2870 'form1': {
2871 'class': __name__ + '.ExceptionFormatter',
2872 'format': '%(levelname)s:%(name)s:%(message)s',
2873 'validate': False,
2874 },
2875 },
2876 'handlers' : {
2877 'hand1' : {
2878 'class': 'logging.StreamHandler',
2879 'formatter': 'form1',
2880 'level': 'NOTSET',
2881 'stream': 'ext://sys.stdout',
2882 },
2883 },
2884 "loggers": {
2885 "my_test_logger_custom_formatter": {
2886 "level": "DEBUG",
2887 "handlers": ["hand1"],
2888 "propagate": "true"
2889 }
2890 }
2891 }
2892
2893 # Configuration with custom class that is not inherited from logging.Formatter
2894 custom_formatter_class_validate3 = {
2895 'version': 1,
2896 'formatters': {
2897 'form1': {
2898 'class': __name__ + '.myCustomFormatter',
2899 'format': '%(levelname)s:%(name)s:%(message)s',
2900 'validate': False,
2901 },
2902 },
2903 'handlers' : {
2904 'hand1' : {
2905 'class': 'logging.StreamHandler',
2906 'formatter': 'form1',
2907 'level': 'NOTSET',
2908 'stream': 'ext://sys.stdout',
2909 },
2910 },
2911 "loggers": {
2912 "my_test_logger_custom_formatter": {
2913 "level": "DEBUG",
2914 "handlers": ["hand1"],
2915 "propagate": "true"
2916 }
2917 }
2918 }
2919
2920 # Configuration with custom function and 'validate' set to False
2921 custom_formatter_with_function = {
2922 'version': 1,
2923 'formatters': {
2924 'form1': {
2925 '()': formatFunc,
2926 'format': '%(levelname)s:%(name)s:%(message)s',
2927 'validate': False,
2928 },
2929 },
2930 'handlers' : {
2931 'hand1' : {
2932 'class': 'logging.StreamHandler',
2933 'formatter': 'form1',
2934 'level': 'NOTSET',
2935 'stream': 'ext://sys.stdout',
2936 },
2937 },
2938 "loggers": {
2939 "my_test_logger_custom_formatter": {
2940 "level": "DEBUG",
2941 "handlers": ["hand1"],
2942 "propagate": "true"
2943 }
2944 }
2945 }
2946
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002947 def apply_config(self, conf):
2948 logging.config.dictConfig(conf)
2949
2950 def test_config0_ok(self):
2951 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002952 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002953 self.apply_config(self.config0)
2954 logger = logging.getLogger()
2955 # Won't output anything
2956 logger.info(self.next_message())
2957 # Outputs a message
2958 logger.error(self.next_message())
2959 self.assert_log_lines([
2960 ('ERROR', '2'),
2961 ], stream=output)
2962 # Original logger output is empty.
2963 self.assert_log_lines([])
2964
2965 def test_config1_ok(self, config=config1):
2966 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002967 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002968 self.apply_config(config)
2969 logger = logging.getLogger("compiler.parser")
2970 # Both will output a message
2971 logger.info(self.next_message())
2972 logger.error(self.next_message())
2973 self.assert_log_lines([
2974 ('INFO', '1'),
2975 ('ERROR', '2'),
2976 ], stream=output)
2977 # Original logger output is empty.
2978 self.assert_log_lines([])
2979
2980 def test_config2_failure(self):
2981 # A simple config which overrides the default settings.
2982 self.assertRaises(Exception, self.apply_config, self.config2)
2983
2984 def test_config2a_failure(self):
2985 # A simple config which overrides the default settings.
2986 self.assertRaises(Exception, self.apply_config, self.config2a)
2987
2988 def test_config2b_failure(self):
2989 # A simple config which overrides the default settings.
2990 self.assertRaises(Exception, self.apply_config, self.config2b)
2991
2992 def test_config3_failure(self):
2993 # A simple config which overrides the default settings.
2994 self.assertRaises(Exception, self.apply_config, self.config3)
2995
2996 def test_config4_ok(self):
2997 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002998 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002999 self.apply_config(self.config4)
3000 #logger = logging.getLogger()
3001 try:
3002 raise RuntimeError()
3003 except RuntimeError:
3004 logging.exception("just testing")
3005 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003006 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003007 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3008 # Original logger output is empty
3009 self.assert_log_lines([])
3010
3011 def test_config4a_ok(self):
3012 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003013 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003014 self.apply_config(self.config4a)
3015 #logger = logging.getLogger()
3016 try:
3017 raise RuntimeError()
3018 except RuntimeError:
3019 logging.exception("just testing")
3020 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003021 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003022 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3023 # Original logger output is empty
3024 self.assert_log_lines([])
3025
3026 def test_config5_ok(self):
3027 self.test_config1_ok(config=self.config5)
3028
3029 def test_config6_failure(self):
3030 self.assertRaises(Exception, self.apply_config, self.config6)
3031
3032 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003033 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003034 self.apply_config(self.config1)
3035 logger = logging.getLogger("compiler.parser")
3036 # Both will output a message
3037 logger.info(self.next_message())
3038 logger.error(self.next_message())
3039 self.assert_log_lines([
3040 ('INFO', '1'),
3041 ('ERROR', '2'),
3042 ], stream=output)
3043 # Original logger output is empty.
3044 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003045 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003046 self.apply_config(self.config7)
3047 logger = logging.getLogger("compiler.parser")
3048 self.assertTrue(logger.disabled)
3049 logger = logging.getLogger("compiler.lexer")
3050 # Both will output a message
3051 logger.info(self.next_message())
3052 logger.error(self.next_message())
3053 self.assert_log_lines([
3054 ('INFO', '3'),
3055 ('ERROR', '4'),
3056 ], stream=output)
3057 # Original logger output is empty.
3058 self.assert_log_lines([])
3059
Mike53f7a7c2017-12-14 14:04:53 +03003060 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003061 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003062 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003063 self.apply_config(self.config1)
3064 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003065 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003066 logger.info(self.next_message())
3067 logger.error(self.next_message())
3068 self.assert_log_lines([
3069 ('INFO', '1'),
3070 ('ERROR', '2'),
3071 ], stream=output)
3072 # Original logger output is empty.
3073 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003074 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003075 self.apply_config(self.config8)
3076 logger = logging.getLogger("compiler.parser")
3077 self.assertFalse(logger.disabled)
3078 # Both will output a message
3079 logger.info(self.next_message())
3080 logger.error(self.next_message())
3081 logger = logging.getLogger("compiler.lexer")
3082 # Both will output a message
3083 logger.info(self.next_message())
3084 logger.error(self.next_message())
3085 self.assert_log_lines([
3086 ('INFO', '3'),
3087 ('ERROR', '4'),
3088 ('INFO', '5'),
3089 ('ERROR', '6'),
3090 ], stream=output)
3091 # Original logger output is empty.
3092 self.assert_log_lines([])
3093
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003094 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003095 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003096 self.apply_config(self.config1a)
3097 logger = logging.getLogger("compiler.parser")
3098 # See issue #11424. compiler-hyphenated sorts
3099 # between compiler and compiler.xyz and this
3100 # was preventing compiler.xyz from being included
3101 # in the child loggers of compiler because of an
3102 # overzealous loop termination condition.
3103 hyphenated = logging.getLogger('compiler-hyphenated')
3104 # All will output a message
3105 logger.info(self.next_message())
3106 logger.error(self.next_message())
3107 hyphenated.critical(self.next_message())
3108 self.assert_log_lines([
3109 ('INFO', '1'),
3110 ('ERROR', '2'),
3111 ('CRITICAL', '3'),
3112 ], stream=output)
3113 # Original logger output is empty.
3114 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003115 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003116 self.apply_config(self.config8a)
3117 logger = logging.getLogger("compiler.parser")
3118 self.assertFalse(logger.disabled)
3119 # Both will output a message
3120 logger.info(self.next_message())
3121 logger.error(self.next_message())
3122 logger = logging.getLogger("compiler.lexer")
3123 # Both will output a message
3124 logger.info(self.next_message())
3125 logger.error(self.next_message())
3126 # Will not appear
3127 hyphenated.critical(self.next_message())
3128 self.assert_log_lines([
3129 ('INFO', '4'),
3130 ('ERROR', '5'),
3131 ('INFO', '6'),
3132 ('ERROR', '7'),
3133 ], stream=output)
3134 # Original logger output is empty.
3135 self.assert_log_lines([])
3136
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003137 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003138 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003139 self.apply_config(self.config9)
3140 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003141 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003142 logger.info(self.next_message())
3143 self.assert_log_lines([], stream=output)
3144 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003145 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003146 logger.info(self.next_message())
3147 self.assert_log_lines([], stream=output)
3148 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003149 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003150 logger.info(self.next_message())
3151 self.assert_log_lines([
3152 ('INFO', '3'),
3153 ], stream=output)
3154
3155 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003156 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003157 self.apply_config(self.config10)
3158 logger = logging.getLogger("compiler.parser")
3159 logger.warning(self.next_message())
3160 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003161 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003162 logger.warning(self.next_message())
3163 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003164 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003165 logger.warning(self.next_message())
3166 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003167 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003168 logger.error(self.next_message())
3169 self.assert_log_lines([
3170 ('WARNING', '1'),
3171 ('ERROR', '4'),
3172 ], stream=output)
3173
3174 def test_config11_ok(self):
3175 self.test_config1_ok(self.config11)
3176
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003177 def test_config12_failure(self):
3178 self.assertRaises(Exception, self.apply_config, self.config12)
3179
3180 def test_config13_failure(self):
3181 self.assertRaises(Exception, self.apply_config, self.config13)
3182
Vinay Sajip8d270232012-11-15 14:20:18 +00003183 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003184 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003185 self.apply_config(self.config14)
3186 h = logging._handlers['hand1']
3187 self.assertEqual(h.foo, 'bar')
3188 self.assertEqual(h.terminator, '!\n')
3189 logging.warning('Exclamation')
3190 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3191
Xtreak087570a2018-07-02 14:27:46 +05303192 def test_config15_ok(self):
3193
3194 def cleanup(h1, fn):
3195 h1.close()
3196 os.remove(fn)
3197
3198 with self.check_no_resource_warning():
3199 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3200 os.close(fd)
3201
3202 config = {
3203 "version": 1,
3204 "handlers": {
3205 "file": {
3206 "class": "logging.FileHandler",
3207 "filename": fn
3208 }
3209 },
3210 "root": {
3211 "handlers": ["file"]
3212 }
3213 }
3214
3215 self.apply_config(config)
3216 self.apply_config(config)
3217
3218 handler = logging.root.handlers[0]
3219 self.addCleanup(cleanup, handler, fn)
3220
Vinay Sajip4ded5512012-10-02 15:56:16 +01003221 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003222 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003223 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003224 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003225 t.start()
3226 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003227 # Now get the port allocated
3228 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003229 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003230 try:
3231 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3232 sock.settimeout(2.0)
3233 sock.connect(('localhost', port))
3234
3235 slen = struct.pack('>L', len(text))
3236 s = slen + text
3237 sentsofar = 0
3238 left = len(s)
3239 while left > 0:
3240 sent = sock.send(s[sentsofar:])
3241 sentsofar += sent
3242 left -= sent
3243 sock.close()
3244 finally:
3245 t.ready.wait(2.0)
3246 logging.config.stopListening()
Hai Shie80697d2020-05-28 06:10:27 +08003247 threading_helper.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003248
3249 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003250 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003251 self.setup_via_listener(json.dumps(self.config10))
3252 logger = logging.getLogger("compiler.parser")
3253 logger.warning(self.next_message())
3254 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003255 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003256 logger.warning(self.next_message())
3257 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003258 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003259 logger.warning(self.next_message())
3260 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003261 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003262 logger.error(self.next_message())
3263 self.assert_log_lines([
3264 ('WARNING', '1'),
3265 ('ERROR', '4'),
3266 ], stream=output)
3267
3268 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003269 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003270 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3271 logger = logging.getLogger("compiler.parser")
3272 # Both will output a message
3273 logger.info(self.next_message())
3274 logger.error(self.next_message())
3275 self.assert_log_lines([
3276 ('INFO', '1'),
3277 ('ERROR', '2'),
3278 ], stream=output)
3279 # Original logger output is empty.
3280 self.assert_log_lines([])
3281
Vinay Sajip4ded5512012-10-02 15:56:16 +01003282 def test_listen_verify(self):
3283
3284 def verify_fail(stuff):
3285 return None
3286
3287 def verify_reverse(stuff):
3288 return stuff[::-1]
3289
3290 logger = logging.getLogger("compiler.parser")
3291 to_send = textwrap.dedent(ConfigFileTest.config1)
3292 # First, specify a verification function that will fail.
3293 # We expect to see no output, since our configuration
3294 # never took effect.
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, verify_fail)
3297 # Both will output a message
3298 logger.info(self.next_message())
3299 logger.error(self.next_message())
3300 self.assert_log_lines([], stream=output)
3301 # Original logger output has the stuff we logged.
3302 self.assert_log_lines([
3303 ('INFO', '1'),
3304 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003305 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003306
3307 # Now, perform no verification. Our configuration
3308 # should take effect.
3309
Vinay Sajip1feedb42014-05-31 08:19:12 +01003310 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003311 self.setup_via_listener(to_send) # no verify callable specified
3312 logger = logging.getLogger("compiler.parser")
3313 # Both will output a message
3314 logger.info(self.next_message())
3315 logger.error(self.next_message())
3316 self.assert_log_lines([
3317 ('INFO', '3'),
3318 ('ERROR', '4'),
3319 ], stream=output)
3320 # Original logger output still has the stuff we logged before.
3321 self.assert_log_lines([
3322 ('INFO', '1'),
3323 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003324 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003325
3326 # Now, perform verification which transforms the bytes.
3327
Vinay Sajip1feedb42014-05-31 08:19:12 +01003328 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003329 self.setup_via_listener(to_send[::-1], verify_reverse)
3330 logger = logging.getLogger("compiler.parser")
3331 # Both will output a message
3332 logger.info(self.next_message())
3333 logger.error(self.next_message())
3334 self.assert_log_lines([
3335 ('INFO', '5'),
3336 ('ERROR', '6'),
3337 ], stream=output)
3338 # Original logger output still has the stuff we logged before.
3339 self.assert_log_lines([
3340 ('INFO', '1'),
3341 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003342 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003343
Vinay Sajip3f885b52013-03-22 15:19:54 +00003344 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003345 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3346
3347 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003348 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003349 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3350
3351 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003352 handler = logging.getLogger('mymodule').handlers[0]
3353 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003354 self.assertIsInstance(handler.formatter._style,
3355 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003356
BNMetrics18fb1fb2018-10-15 19:41:36 +01003357 def test_custom_formatter_class_with_validate(self):
3358 self.apply_config(self.custom_formatter_class_validate)
3359 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3360 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3361
3362 def test_custom_formatter_class_with_validate2(self):
3363 self.apply_config(self.custom_formatter_class_validate2)
3364 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3365 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3366
3367 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3368 config = self.custom_formatter_class_validate.copy()
3369 config['formatters']['form1']['style'] = "$"
3370
3371 # Exception should not be raise as we have configured 'validate' to False
3372 self.apply_config(config)
3373 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3374 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3375
3376 def test_custom_formatter_class_with_validate3(self):
3377 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3378
3379 def test_custom_formatter_function_with_validate(self):
3380 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3381
Vinay Sajip373baef2011-04-26 20:05:24 +01003382 def test_baseconfig(self):
3383 d = {
3384 'atuple': (1, 2, 3),
3385 'alist': ['a', 'b', 'c'],
3386 'adict': {'d': 'e', 'f': 3 },
3387 'nest1': ('g', ('h', 'i'), 'j'),
3388 'nest2': ['k', ['l', 'm'], 'n'],
3389 'nest3': ['o', 'cfg://alist', 'p'],
3390 }
3391 bc = logging.config.BaseConfigurator(d)
3392 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3393 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3394 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3395 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3396 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3397 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3398 v = bc.convert('cfg://nest3')
3399 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3400 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3401 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3402 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003403
Vinay Sajip46abfc12020-01-01 19:32:11 +00003404 def test_namedtuple(self):
3405 # see bpo-39142
3406 from collections import namedtuple
3407
3408 class MyHandler(logging.StreamHandler):
3409 def __init__(self, resource, *args, **kwargs):
3410 super().__init__(*args, **kwargs)
3411 self.resource: namedtuple = resource
3412
3413 def emit(self, record):
3414 record.msg += f' {self.resource.type}'
3415 return super().emit(record)
3416
3417 Resource = namedtuple('Resource', ['type', 'labels'])
3418 resource = Resource(type='my_type', labels=['a'])
3419
3420 config = {
3421 'version': 1,
3422 'handlers': {
3423 'myhandler': {
3424 '()': MyHandler,
3425 'resource': resource
3426 }
3427 },
3428 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3429 }
3430 with support.captured_stderr() as stderr:
3431 self.apply_config(config)
3432 logging.info('some log')
3433 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3434
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003435class ManagerTest(BaseTest):
3436 def test_manager_loggerclass(self):
3437 logged = []
3438
3439 class MyLogger(logging.Logger):
3440 def _log(self, level, msg, args, exc_info=None, extra=None):
3441 logged.append(msg)
3442
3443 man = logging.Manager(None)
3444 self.assertRaises(TypeError, man.setLoggerClass, int)
3445 man.setLoggerClass(MyLogger)
3446 logger = man.getLogger('test')
3447 logger.warning('should appear in logged')
3448 logging.warning('should not appear in logged')
3449
3450 self.assertEqual(logged, ['should appear in logged'])
3451
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003452 def test_set_log_record_factory(self):
3453 man = logging.Manager(None)
3454 expected = object()
3455 man.setLogRecordFactory(expected)
3456 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003457
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003458class ChildLoggerTest(BaseTest):
3459 def test_child_loggers(self):
3460 r = logging.getLogger()
3461 l1 = logging.getLogger('abc')
3462 l2 = logging.getLogger('def.ghi')
3463 c1 = r.getChild('xyz')
3464 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003465 self.assertIs(c1, logging.getLogger('xyz'))
3466 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003467 c1 = l1.getChild('def')
3468 c2 = c1.getChild('ghi')
3469 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003470 self.assertIs(c1, logging.getLogger('abc.def'))
3471 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3472 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003473
3474
Vinay Sajip6fac8172010-10-19 20:44:14 +00003475class DerivedLogRecord(logging.LogRecord):
3476 pass
3477
Vinay Sajip61561522010-12-03 11:50:38 +00003478class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003479
3480 def setUp(self):
3481 class CheckingFilter(logging.Filter):
3482 def __init__(self, cls):
3483 self.cls = cls
3484
3485 def filter(self, record):
3486 t = type(record)
3487 if t is not self.cls:
3488 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3489 self.cls)
3490 raise TypeError(msg)
3491 return True
3492
3493 BaseTest.setUp(self)
3494 self.filter = CheckingFilter(DerivedLogRecord)
3495 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003496 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003497
3498 def tearDown(self):
3499 self.root_logger.removeFilter(self.filter)
3500 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003501 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003502
3503 def test_logrecord_class(self):
3504 self.assertRaises(TypeError, self.root_logger.warning,
3505 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003506 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003507 self.root_logger.error(self.next_message())
3508 self.assert_log_lines([
3509 ('root', 'ERROR', '2'),
3510 ])
3511
3512
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003513class QueueHandlerTest(BaseTest):
3514 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003515 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003516
3517 def setUp(self):
3518 BaseTest.setUp(self)
3519 self.queue = queue.Queue(-1)
3520 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003521 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003522 self.que_logger = logging.getLogger('que')
3523 self.que_logger.propagate = False
3524 self.que_logger.setLevel(logging.WARNING)
3525 self.que_logger.addHandler(self.que_hdlr)
3526
3527 def tearDown(self):
3528 self.que_hdlr.close()
3529 BaseTest.tearDown(self)
3530
3531 def test_queue_handler(self):
3532 self.que_logger.debug(self.next_message())
3533 self.assertRaises(queue.Empty, self.queue.get_nowait)
3534 self.que_logger.info(self.next_message())
3535 self.assertRaises(queue.Empty, self.queue.get_nowait)
3536 msg = self.next_message()
3537 self.que_logger.warning(msg)
3538 data = self.queue.get_nowait()
3539 self.assertTrue(isinstance(data, logging.LogRecord))
3540 self.assertEqual(data.name, self.que_logger.name)
3541 self.assertEqual((data.msg, data.args), (msg, None))
3542
favlladfe3442017-08-01 20:12:26 +02003543 def test_formatting(self):
3544 msg = self.next_message()
3545 levelname = logging.getLevelName(logging.WARNING)
3546 log_format_str = '{name} -> {levelname}: {message}'
3547 formatted_msg = log_format_str.format(name=self.name,
3548 levelname=levelname, message=msg)
3549 formatter = logging.Formatter(self.log_format)
3550 self.que_hdlr.setFormatter(formatter)
3551 self.que_logger.warning(msg)
3552 log_record = self.queue.get_nowait()
3553 self.assertEqual(formatted_msg, log_record.msg)
3554 self.assertEqual(formatted_msg, log_record.message)
3555
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003556 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3557 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003558 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003559 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003560 listener = logging.handlers.QueueListener(self.queue, handler)
3561 listener.start()
3562 try:
3563 self.que_logger.warning(self.next_message())
3564 self.que_logger.error(self.next_message())
3565 self.que_logger.critical(self.next_message())
3566 finally:
3567 listener.stop()
3568 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3569 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3570 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003571 handler.close()
3572
3573 # Now test with respect_handler_level set
3574
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003575 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003576 handler.setLevel(logging.CRITICAL)
3577 listener = logging.handlers.QueueListener(self.queue, handler,
3578 respect_handler_level=True)
3579 listener.start()
3580 try:
3581 self.que_logger.warning(self.next_message())
3582 self.que_logger.error(self.next_message())
3583 self.que_logger.critical(self.next_message())
3584 finally:
3585 listener.stop()
3586 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3587 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3588 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003589 handler.close()
3590
3591 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3592 'logging.handlers.QueueListener required for this test')
3593 def test_queue_listener_with_StreamHandler(self):
3594 # Test that traceback only appends once (bpo-34334).
3595 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3596 listener.start()
3597 try:
3598 1 / 0
3599 except ZeroDivisionError as e:
3600 exc = e
3601 self.que_logger.exception(self.next_message(), exc_info=exc)
3602 listener.stop()
3603 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003604
Xtreak2dad9602019-04-07 13:21:27 +05303605 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3606 'logging.handlers.QueueListener required for this test')
3607 def test_queue_listener_with_multiple_handlers(self):
3608 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3609 self.que_hdlr.setFormatter(self.root_formatter)
3610 self.que_logger.addHandler(self.root_hdlr)
3611
3612 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3613 listener.start()
3614 self.que_logger.error("error")
3615 listener.stop()
3616 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3617
Vinay Sajipd61910c2016-09-08 01:13:39 +01003618if hasattr(logging.handlers, 'QueueListener'):
3619 import multiprocessing
3620 from unittest.mock import patch
3621
3622 class QueueListenerTest(BaseTest):
3623 """
3624 Tests based on patch submitted for issue #27930. Ensure that
3625 QueueListener handles all log messages.
3626 """
3627
3628 repeat = 20
3629
3630 @staticmethod
3631 def setup_and_log(log_queue, ident):
3632 """
3633 Creates a logger with a QueueHandler that logs to a queue read by a
3634 QueueListener. Starts the listener, logs five messages, and stops
3635 the listener.
3636 """
3637 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3638 logger.setLevel(logging.DEBUG)
3639 handler = logging.handlers.QueueHandler(log_queue)
3640 logger.addHandler(handler)
3641 listener = logging.handlers.QueueListener(log_queue)
3642 listener.start()
3643
3644 logger.info('one')
3645 logger.info('two')
3646 logger.info('three')
3647 logger.info('four')
3648 logger.info('five')
3649
3650 listener.stop()
3651 logger.removeHandler(handler)
3652 handler.close()
3653
3654 @patch.object(logging.handlers.QueueListener, 'handle')
3655 def test_handle_called_with_queue_queue(self, mock_handle):
3656 for i in range(self.repeat):
3657 log_queue = queue.Queue()
3658 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3659 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3660 'correct number of handled log messages')
3661
3662 @patch.object(logging.handlers.QueueListener, 'handle')
3663 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003664 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003665 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003666 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003667 for i in range(self.repeat):
3668 log_queue = multiprocessing.Queue()
3669 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003670 log_queue.close()
3671 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003672 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3673 'correct number of handled log messages')
3674
3675 @staticmethod
3676 def get_all_from_queue(log_queue):
3677 try:
3678 while True:
3679 yield log_queue.get_nowait()
3680 except queue.Empty:
3681 return []
3682
3683 def test_no_messages_in_queue_after_stop(self):
3684 """
3685 Five messages are logged then the QueueListener is stopped. This
3686 test then gets everything off the queue. Failure of this test
3687 indicates that messages were not registered on the queue until
3688 _after_ the QueueListener stopped.
3689 """
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003690 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003691 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003692 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003693 for i in range(self.repeat):
3694 queue = multiprocessing.Queue()
3695 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3696 # time.sleep(1)
3697 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003698 queue.close()
3699 queue.join_thread()
3700
Vinay Sajipd61910c2016-09-08 01:13:39 +01003701 expected = [[], [logging.handlers.QueueListener._sentinel]]
3702 self.assertIn(items, expected,
3703 'Found unexpected messages in queue: %s' % (
3704 [m.msg if isinstance(m, logging.LogRecord)
3705 else m for m in items]))
3706
Bar Harel6b282e12019-06-01 12:19:09 +03003707 def test_calls_task_done_after_stop(self):
3708 # Issue 36813: Make sure queue.join does not deadlock.
3709 log_queue = queue.Queue()
3710 listener = logging.handlers.QueueListener(log_queue)
3711 listener.start()
3712 listener.stop()
3713 with self.assertRaises(ValueError):
3714 # Make sure all tasks are done and .join won't block.
3715 log_queue.task_done()
3716
Vinay Sajipe723e962011-04-15 22:27:17 +01003717
Vinay Sajip37eb3382011-04-26 20:26:41 +01003718ZERO = datetime.timedelta(0)
3719
3720class UTC(datetime.tzinfo):
3721 def utcoffset(self, dt):
3722 return ZERO
3723
3724 dst = utcoffset
3725
3726 def tzname(self, dt):
3727 return 'UTC'
3728
3729utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003730
Eric Larson9fdb76c2020-09-25 14:08:50 -04003731class AssertErrorMessage:
3732
3733 def assert_error_message(self, exception, message, *args, **kwargs):
3734 try:
3735 self.assertRaises((), *args, **kwargs)
3736 except exception as e:
3737 self.assertEqual(message, str(e))
3738
3739class FormatterTest(unittest.TestCase, AssertErrorMessage):
Vinay Sajipa39c5712010-10-25 13:57:39 +00003740 def setUp(self):
3741 self.common = {
3742 'name': 'formatter.test',
3743 'level': logging.DEBUG,
3744 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3745 'lineno': 42,
3746 'exc_info': None,
3747 'func': None,
3748 'msg': 'Message with %d %s',
3749 'args': (2, 'placeholders'),
3750 }
3751 self.variants = {
Bar Harel8f192d12020-06-18 17:18:58 +03003752 'custom': {
3753 'custom': 1234
3754 }
Vinay Sajipa39c5712010-10-25 13:57:39 +00003755 }
3756
3757 def get_record(self, name=None):
3758 result = dict(self.common)
3759 if name is not None:
3760 result.update(self.variants[name])
3761 return logging.makeLogRecord(result)
3762
3763 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003764 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003765 r = self.get_record()
3766 f = logging.Formatter('${%(message)s}')
3767 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3768 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003769 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003770 self.assertFalse(f.usesTime())
3771 f = logging.Formatter('%(asctime)s')
3772 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003773 f = logging.Formatter('%(asctime)-15s')
3774 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003775 f = logging.Formatter('%(asctime)#15s')
3776 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003777
3778 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003779 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003780 r = self.get_record()
3781 f = logging.Formatter('$%{message}%$', style='{')
3782 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3783 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003784 self.assertRaises(ValueError, f.format, r)
3785 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003786 self.assertFalse(f.usesTime())
3787 f = logging.Formatter('{asctime}', style='{')
3788 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003789 f = logging.Formatter('{asctime!s:15}', style='{')
3790 self.assertTrue(f.usesTime())
3791 f = logging.Formatter('{asctime:15}', style='{')
3792 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003793
3794 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003795 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003796 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003797 f = logging.Formatter('${message}', style='$')
3798 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003799 f = logging.Formatter('$message', style='$')
3800 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3801 f = logging.Formatter('$$%${message}%$$', style='$')
3802 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3803 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003804 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003805 self.assertFalse(f.usesTime())
3806 f = logging.Formatter('${asctime}', style='$')
3807 self.assertTrue(f.usesTime())
3808 f = logging.Formatter('$asctime', style='$')
3809 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003810 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003811 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003812 f = logging.Formatter('${asctime}--', style='$')
3813 self.assertTrue(f.usesTime())
3814
3815 def test_format_validate(self):
3816 # Check correct formatting
3817 # Percentage style
3818 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3819 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3820 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3821 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3822 f = logging.Formatter("%(process)#+027.23X")
3823 self.assertEqual(f._fmt, "%(process)#+027.23X")
3824 f = logging.Formatter("%(foo)#.*g")
3825 self.assertEqual(f._fmt, "%(foo)#.*g")
3826
3827 # StrFormat Style
3828 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3829 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3830 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3831 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3832 f = logging.Formatter("{customfield!s:#<30}", style="{")
3833 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3834 f = logging.Formatter("{message!r}", style="{")
3835 self.assertEqual(f._fmt, "{message!r}")
3836 f = logging.Formatter("{message!s}", style="{")
3837 self.assertEqual(f._fmt, "{message!s}")
3838 f = logging.Formatter("{message!a}", style="{")
3839 self.assertEqual(f._fmt, "{message!a}")
3840 f = logging.Formatter("{process!r:4.2}", style="{")
3841 self.assertEqual(f._fmt, "{process!r:4.2}")
3842 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3843 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3844 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3845 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3846 f = logging.Formatter("{foo:12.{p}}", style="{")
3847 self.assertEqual(f._fmt, "{foo:12.{p}}")
3848 f = logging.Formatter("{foo:{w}.6}", style="{")
3849 self.assertEqual(f._fmt, "{foo:{w}.6}")
3850 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3851 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3852 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3853 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3854 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3855 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3856
3857 # Dollar style
3858 f = logging.Formatter("${asctime} - $message", style="$")
3859 self.assertEqual(f._fmt, "${asctime} - $message")
3860 f = logging.Formatter("$bar $$", style="$")
3861 self.assertEqual(f._fmt, "$bar $$")
3862 f = logging.Formatter("$bar $$$$", style="$")
3863 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3864
3865 # Testing when ValueError being raised from incorrect format
3866 # Percentage Style
3867 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3868 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3869 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3870 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3871 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3872 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3873 self.assertRaises(ValueError, logging.Formatter, '${message}')
3874 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3875 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3876
3877 # StrFormat Style
3878 # Testing failure for '-' in field name
3879 self.assert_error_message(
3880 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003881 "invalid format: invalid field name/expression: 'name-thing'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003882 logging.Formatter, "{name-thing}", style="{"
3883 )
3884 # Testing failure for style mismatch
3885 self.assert_error_message(
3886 ValueError,
3887 "invalid format: no fields",
3888 logging.Formatter, '%(asctime)s', style='{'
3889 )
3890 # Testing failure for invalid conversion
3891 self.assert_error_message(
3892 ValueError,
3893 "invalid conversion: 'Z'"
3894 )
3895 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3896 self.assert_error_message(
3897 ValueError,
3898 "invalid format: expected ':' after conversion specifier",
3899 logging.Formatter, '{asctime!aa:15}', style='{'
3900 )
3901 # Testing failure for invalid spec
3902 self.assert_error_message(
3903 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003904 "invalid format: bad specifier: '.2ff'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003905 logging.Formatter, '{process:.2ff}', style='{'
3906 )
3907 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3908 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3909 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3910 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3911 # Testing failure for mismatch braces
3912 self.assert_error_message(
3913 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003914 "invalid format: expected '}' before end of string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003915 logging.Formatter, '{process', style='{'
3916 )
3917 self.assert_error_message(
3918 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003919 "invalid format: Single '}' encountered in format string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003920 logging.Formatter, 'process}', style='{'
3921 )
3922 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3923 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3924 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3925 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3926 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3927 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3928 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3929 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3930 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3931
3932 # Dollar style
3933 # Testing failure for mismatch bare $
3934 self.assert_error_message(
3935 ValueError,
3936 "invalid format: bare \'$\' not allowed",
3937 logging.Formatter, '$bar $$$', style='$'
3938 )
3939 self.assert_error_message(
3940 ValueError,
3941 "invalid format: bare \'$\' not allowed",
3942 logging.Formatter, 'bar $', style='$'
3943 )
3944 self.assert_error_message(
3945 ValueError,
3946 "invalid format: bare \'$\' not allowed",
3947 logging.Formatter, 'foo $.', style='$'
3948 )
3949 # Testing failure for mismatch style
3950 self.assert_error_message(
3951 ValueError,
3952 "invalid format: no fields",
3953 logging.Formatter, '{asctime}', style='$'
3954 )
3955 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3956
3957 # Testing failure for incorrect fields
3958 self.assert_error_message(
3959 ValueError,
3960 "invalid format: no fields",
3961 logging.Formatter, 'foo', style='$'
3962 )
3963 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003964
Bar Harel8f192d12020-06-18 17:18:58 +03003965 def test_defaults_parameter(self):
3966 fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message']
3967 styles = ['%', '{', '$']
3968 for fmt, style in zip(fmts, styles):
3969 f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'})
3970 r = self.get_record()
3971 self.assertEqual(f.format(r), 'Default Message with 2 placeholders')
3972 r = self.get_record("custom")
3973 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3974
3975 # Without default
3976 f = logging.Formatter(fmt, style=style)
3977 r = self.get_record()
3978 self.assertRaises(ValueError, f.format, r)
3979
3980 # Non-existing default is ignored
3981 f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'})
3982 r = self.get_record("custom")
3983 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3984
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003985 def test_invalid_style(self):
3986 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3987
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003988 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003989 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003990 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3991 # We use None to indicate we want the local timezone
3992 # We're essentially converting a UTC time to local time
3993 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003994 r.msecs = 123
3995 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003996 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003997 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
3998 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01003999 f.format(r)
4000 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
4001
Mariusz Felisiak06a35542020-04-17 18:02:47 +02004002 def test_default_msec_format_none(self):
4003 class NoMsecFormatter(logging.Formatter):
4004 default_msec_format = None
4005 default_time_format = '%d/%m/%Y %H:%M:%S'
4006
4007 r = self.get_record()
4008 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
4009 r.created = time.mktime(dt.astimezone(None).timetuple())
4010 f = NoMsecFormatter()
4011 f.converter = time.gmtime
4012 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
4013
4014
Vinay Sajip985ef872011-04-26 19:34:04 +01004015class TestBufferingFormatter(logging.BufferingFormatter):
4016 def formatHeader(self, records):
4017 return '[(%d)' % len(records)
4018
4019 def formatFooter(self, records):
4020 return '(%d)]' % len(records)
4021
4022class BufferingFormatterTest(unittest.TestCase):
4023 def setUp(self):
4024 self.records = [
4025 logging.makeLogRecord({'msg': 'one'}),
4026 logging.makeLogRecord({'msg': 'two'}),
4027 ]
4028
4029 def test_default(self):
4030 f = logging.BufferingFormatter()
4031 self.assertEqual('', f.format([]))
4032 self.assertEqual('onetwo', f.format(self.records))
4033
4034 def test_custom(self):
4035 f = TestBufferingFormatter()
4036 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
4037 lf = logging.Formatter('<%(message)s>')
4038 f = TestBufferingFormatter(lf)
4039 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004040
4041class ExceptionTest(BaseTest):
4042 def test_formatting(self):
4043 r = self.root_logger
4044 h = RecordingHandler()
4045 r.addHandler(h)
4046 try:
4047 raise RuntimeError('deliberate mistake')
4048 except:
4049 logging.exception('failed', stack_info=True)
4050 r.removeHandler(h)
4051 h.close()
4052 r = h.records[0]
4053 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
4054 'call last):\n'))
4055 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4056 'deliberate mistake'))
4057 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4058 'call last):\n'))
4059 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4060 'stack_info=True)'))
4061
4062
Vinay Sajip5a27d402010-12-10 11:42:57 +00004063class LastResortTest(BaseTest):
4064 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004065 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004066 root = self.root_logger
4067 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004068 old_lastresort = logging.lastResort
4069 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004070
Vinay Sajip5a27d402010-12-10 11:42:57 +00004071 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004072 with support.captured_stderr() as stderr:
4073 root.debug('This should not appear')
4074 self.assertEqual(stderr.getvalue(), '')
4075 root.warning('Final chance!')
4076 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4077
4078 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004079 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004080 with support.captured_stderr() as stderr:
4081 root.warning('Final chance!')
4082 msg = 'No handlers could be found for logger "root"\n'
4083 self.assertEqual(stderr.getvalue(), msg)
4084
Vinay Sajip5a27d402010-12-10 11:42:57 +00004085 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004086 with support.captured_stderr() as stderr:
4087 root.warning('Final chance!')
4088 self.assertEqual(stderr.getvalue(), '')
4089
4090 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004091 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004092 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004093 with support.captured_stderr() as stderr:
4094 root.warning('Final chance!')
4095 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004096 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004097 root.addHandler(self.root_hdlr)
4098 logging.lastResort = old_lastresort
4099 logging.raiseExceptions = old_raise_exceptions
4100
4101
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004102class FakeHandler:
4103
4104 def __init__(self, identifier, called):
4105 for method in ('acquire', 'flush', 'close', 'release'):
4106 setattr(self, method, self.record_call(identifier, method, called))
4107
4108 def record_call(self, identifier, method_name, called):
4109 def inner():
4110 called.append('{} - {}'.format(identifier, method_name))
4111 return inner
4112
4113
4114class RecordingHandler(logging.NullHandler):
4115
4116 def __init__(self, *args, **kwargs):
4117 super(RecordingHandler, self).__init__(*args, **kwargs)
4118 self.records = []
4119
4120 def handle(self, record):
4121 """Keep track of all the emitted records."""
4122 self.records.append(record)
4123
4124
4125class ShutdownTest(BaseTest):
4126
Vinay Sajip5e66b162011-04-20 15:41:14 +01004127 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004128
4129 def setUp(self):
4130 super(ShutdownTest, self).setUp()
4131 self.called = []
4132
4133 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004134 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004135
4136 def raise_error(self, error):
4137 def inner():
4138 raise error()
4139 return inner
4140
4141 def test_no_failure(self):
4142 # create some fake handlers
4143 handler0 = FakeHandler(0, self.called)
4144 handler1 = FakeHandler(1, self.called)
4145 handler2 = FakeHandler(2, self.called)
4146
4147 # create live weakref to those handlers
4148 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4149
4150 logging.shutdown(handlerList=list(handlers))
4151
4152 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4153 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4154 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4155 self.assertEqual(expected, self.called)
4156
4157 def _test_with_failure_in_method(self, method, error):
4158 handler = FakeHandler(0, self.called)
4159 setattr(handler, method, self.raise_error(error))
4160 handlers = [logging.weakref.ref(handler)]
4161
4162 logging.shutdown(handlerList=list(handlers))
4163
4164 self.assertEqual('0 - release', self.called[-1])
4165
4166 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004167 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004168
4169 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004170 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004171
4172 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004173 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004174
4175 def test_with_valueerror_in_acquire(self):
4176 self._test_with_failure_in_method('acquire', ValueError)
4177
4178 def test_with_valueerror_in_flush(self):
4179 self._test_with_failure_in_method('flush', ValueError)
4180
4181 def test_with_valueerror_in_close(self):
4182 self._test_with_failure_in_method('close', ValueError)
4183
4184 def test_with_other_error_in_acquire_without_raise(self):
4185 logging.raiseExceptions = False
4186 self._test_with_failure_in_method('acquire', IndexError)
4187
4188 def test_with_other_error_in_flush_without_raise(self):
4189 logging.raiseExceptions = False
4190 self._test_with_failure_in_method('flush', IndexError)
4191
4192 def test_with_other_error_in_close_without_raise(self):
4193 logging.raiseExceptions = False
4194 self._test_with_failure_in_method('close', IndexError)
4195
4196 def test_with_other_error_in_acquire_with_raise(self):
4197 logging.raiseExceptions = True
4198 self.assertRaises(IndexError, self._test_with_failure_in_method,
4199 'acquire', IndexError)
4200
4201 def test_with_other_error_in_flush_with_raise(self):
4202 logging.raiseExceptions = True
4203 self.assertRaises(IndexError, self._test_with_failure_in_method,
4204 'flush', IndexError)
4205
4206 def test_with_other_error_in_close_with_raise(self):
4207 logging.raiseExceptions = True
4208 self.assertRaises(IndexError, self._test_with_failure_in_method,
4209 'close', IndexError)
4210
4211
4212class ModuleLevelMiscTest(BaseTest):
4213
Vinay Sajip5e66b162011-04-20 15:41:14 +01004214 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004215
4216 def test_disable(self):
4217 old_disable = logging.root.manager.disable
4218 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004219 self.assertEqual(old_disable, 0)
4220 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004221
4222 logging.disable(83)
4223 self.assertEqual(logging.root.manager.disable, 83)
4224
Matthias Bussonnierb32d8b42020-12-16 01:43:39 -08004225 self.assertRaises(ValueError, logging.disable, "doesnotexists")
4226
4227 class _NotAnIntOrString:
4228 pass
4229
4230 self.assertRaises(TypeError, logging.disable, _NotAnIntOrString())
4231
4232 logging.disable("WARN")
4233
Vinay Sajipd489ac92016-12-31 11:40:11 +00004234 # test the default value introduced in 3.7
4235 # (Issue #28524)
4236 logging.disable()
4237 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4238
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004239 def _test_log(self, method, level=None):
4240 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004241 support.patch(self, logging, 'basicConfig',
4242 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004243
4244 recording = RecordingHandler()
4245 logging.root.addHandler(recording)
4246
4247 log_method = getattr(logging, method)
4248 if level is not None:
4249 log_method(level, "test me: %r", recording)
4250 else:
4251 log_method("test me: %r", recording)
4252
4253 self.assertEqual(len(recording.records), 1)
4254 record = recording.records[0]
4255 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4256
4257 expected_level = level if level is not None else getattr(logging, method.upper())
4258 self.assertEqual(record.levelno, expected_level)
4259
4260 # basicConfig was not called!
4261 self.assertEqual(called, [])
4262
4263 def test_log(self):
4264 self._test_log('log', logging.ERROR)
4265
4266 def test_debug(self):
4267 self._test_log('debug')
4268
4269 def test_info(self):
4270 self._test_log('info')
4271
4272 def test_warning(self):
4273 self._test_log('warning')
4274
4275 def test_error(self):
4276 self._test_log('error')
4277
4278 def test_critical(self):
4279 self._test_log('critical')
4280
4281 def test_set_logger_class(self):
4282 self.assertRaises(TypeError, logging.setLoggerClass, object)
4283
4284 class MyLogger(logging.Logger):
4285 pass
4286
4287 logging.setLoggerClass(MyLogger)
4288 self.assertEqual(logging.getLoggerClass(), MyLogger)
4289
4290 logging.setLoggerClass(logging.Logger)
4291 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4292
Vinay Sajip01500012019-06-19 11:46:53 +01004293 def test_subclass_logger_cache(self):
4294 # bpo-37258
4295 message = []
4296
4297 class MyLogger(logging.getLoggerClass()):
4298 def __init__(self, name='MyLogger', level=logging.NOTSET):
4299 super().__init__(name, level)
4300 message.append('initialized')
4301
4302 logging.setLoggerClass(MyLogger)
4303 logger = logging.getLogger('just_some_logger')
4304 self.assertEqual(message, ['initialized'])
4305 stream = io.StringIO()
4306 h = logging.StreamHandler(stream)
4307 logger.addHandler(h)
4308 try:
4309 logger.setLevel(logging.DEBUG)
4310 logger.debug("hello")
4311 self.assertEqual(stream.getvalue().strip(), "hello")
4312
4313 stream.truncate(0)
4314 stream.seek(0)
4315
4316 logger.setLevel(logging.INFO)
4317 logger.debug("hello")
4318 self.assertEqual(stream.getvalue(), "")
4319 finally:
4320 logger.removeHandler(h)
4321 h.close()
4322 logging.setLoggerClass(logging.Logger)
4323
Antoine Pitrou712cb732013-12-21 15:51:54 +01004324 def test_logging_at_shutdown(self):
Victor Stinner45df61f2020-11-02 23:17:46 +01004325 # bpo-20037: Doing text I/O late at interpreter shutdown must not crash
4326 code = textwrap.dedent("""
Antoine Pitrou712cb732013-12-21 15:51:54 +01004327 import logging
4328
4329 class A:
4330 def __del__(self):
4331 try:
4332 raise ValueError("some error")
4333 except Exception:
4334 logging.exception("exception in __del__")
4335
Victor Stinner45df61f2020-11-02 23:17:46 +01004336 a = A()
4337 """)
Antoine Pitrou712cb732013-12-21 15:51:54 +01004338 rc, out, err = assert_python_ok("-c", code)
4339 err = err.decode()
4340 self.assertIn("exception in __del__", err)
4341 self.assertIn("ValueError: some error", err)
4342
Victor Stinner45df61f2020-11-02 23:17:46 +01004343 def test_logging_at_shutdown_open(self):
4344 # bpo-26789: FileHandler keeps a reference to the builtin open()
4345 # function to be able to open or reopen the file during Python
4346 # finalization.
4347 filename = os_helper.TESTFN
4348 self.addCleanup(os_helper.unlink, filename)
4349
4350 code = textwrap.dedent(f"""
4351 import builtins
4352 import logging
4353
4354 class A:
4355 def __del__(self):
4356 logging.error("log in __del__")
4357
4358 # basicConfig() opens the file, but logging.shutdown() closes
4359 # it at Python exit. When A.__del__() is called,
4360 # FileHandler._open() must be called again to re-open the file.
Inada Naokifb786922021-04-06 11:18:41 +09004361 logging.basicConfig(filename={filename!r}, encoding="utf-8")
Victor Stinner45df61f2020-11-02 23:17:46 +01004362
4363 a = A()
4364
4365 # Simulate the Python finalization which removes the builtin
4366 # open() function.
4367 del builtins.open
4368 """)
4369 assert_python_ok("-c", code)
4370
Inada Naokifb786922021-04-06 11:18:41 +09004371 with open(filename, encoding="utf-8") as fp:
Victor Stinner45df61f2020-11-02 23:17:46 +01004372 self.assertEqual(fp.read().rstrip(), "ERROR:root:log in __del__")
4373
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004374 def test_recursion_error(self):
4375 # Issue 36272
Victor Stinner45df61f2020-11-02 23:17:46 +01004376 code = textwrap.dedent("""
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004377 import logging
4378
4379 def rec():
4380 logging.error("foo")
4381 rec()
4382
Victor Stinner45df61f2020-11-02 23:17:46 +01004383 rec()
4384 """)
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004385 rc, out, err = assert_python_failure("-c", code)
4386 err = err.decode()
4387 self.assertNotIn("Cannot recover from stack overflow.", err)
4388 self.assertEqual(rc, 1)
4389
Antoine Pitrou712cb732013-12-21 15:51:54 +01004390
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004391class LogRecordTest(BaseTest):
4392 def test_str_rep(self):
4393 r = logging.makeLogRecord({})
4394 s = str(r)
4395 self.assertTrue(s.startswith('<LogRecord: '))
4396 self.assertTrue(s.endswith('>'))
4397
4398 def test_dict_arg(self):
4399 h = RecordingHandler()
4400 r = logging.getLogger()
4401 r.addHandler(h)
4402 d = {'less' : 'more' }
4403 logging.warning('less is %(less)s', d)
4404 self.assertIs(h.records[0].args, d)
4405 self.assertEqual(h.records[0].message, 'less is more')
4406 r.removeHandler(h)
4407 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004408
Irit Katriel3fd69992020-09-08 20:40:04 +01004409 @staticmethod # pickled as target of child process in the following test
4410 def _extract_logrecord_process_name(key, logMultiprocessing, conn=None):
4411 prev_logMultiprocessing = logging.logMultiprocessing
4412 logging.logMultiprocessing = logMultiprocessing
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004413 try:
4414 import multiprocessing as mp
Irit Katriel3fd69992020-09-08 20:40:04 +01004415 name = mp.current_process().name
4416
4417 r1 = logging.makeLogRecord({'msg': f'msg1_{key}'})
4418 del sys.modules['multiprocessing']
4419 r2 = logging.makeLogRecord({'msg': f'msg2_{key}'})
4420
4421 results = {'processName' : name,
4422 'r1.processName': r1.processName,
4423 'r2.processName': r2.processName,
4424 }
4425 finally:
4426 logging.logMultiprocessing = prev_logMultiprocessing
4427 if conn:
4428 conn.send(results)
4429 else:
4430 return results
4431
4432 def test_multiprocessing(self):
4433 multiprocessing_imported = 'multiprocessing' in sys.modules
4434 try:
4435 # logMultiprocessing is True by default
4436 self.assertEqual(logging.logMultiprocessing, True)
4437
4438 LOG_MULTI_PROCESSING = True
4439 # When logMultiprocessing == True:
4440 # In the main process processName = 'MainProcess'
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004441 r = logging.makeLogRecord({})
Irit Katriel3fd69992020-09-08 20:40:04 +01004442 self.assertEqual(r.processName, 'MainProcess')
4443
4444 results = self._extract_logrecord_process_name(1, LOG_MULTI_PROCESSING)
4445 self.assertEqual('MainProcess', results['processName'])
4446 self.assertEqual('MainProcess', results['r1.processName'])
4447 self.assertEqual('MainProcess', results['r2.processName'])
4448
4449 # In other processes, processName is correct when multiprocessing in imported,
4450 # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762).
4451 import multiprocessing
4452 parent_conn, child_conn = multiprocessing.Pipe()
4453 p = multiprocessing.Process(
4454 target=self._extract_logrecord_process_name,
4455 args=(2, LOG_MULTI_PROCESSING, child_conn,)
4456 )
4457 p.start()
4458 results = parent_conn.recv()
4459 self.assertNotEqual('MainProcess', results['processName'])
4460 self.assertEqual(results['processName'], results['r1.processName'])
4461 self.assertEqual('MainProcess', results['r2.processName'])
4462 p.join()
4463
4464 finally:
4465 if multiprocessing_imported:
4466 import multiprocessing
4467
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004468
4469 def test_optional(self):
4470 r = logging.makeLogRecord({})
4471 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004472 NOT_NONE(r.thread)
4473 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004474 NOT_NONE(r.process)
4475 NOT_NONE(r.processName)
4476 log_threads = logging.logThreads
4477 log_processes = logging.logProcesses
4478 log_multiprocessing = logging.logMultiprocessing
4479 try:
4480 logging.logThreads = False
4481 logging.logProcesses = False
4482 logging.logMultiprocessing = False
4483 r = logging.makeLogRecord({})
4484 NONE = self.assertIsNone
4485 NONE(r.thread)
4486 NONE(r.threadName)
4487 NONE(r.process)
4488 NONE(r.processName)
4489 finally:
4490 logging.logThreads = log_threads
4491 logging.logProcesses = log_processes
4492 logging.logMultiprocessing = log_multiprocessing
4493
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004494class BasicConfigTest(unittest.TestCase):
4495
Vinay Sajip95bf5042011-04-20 11:50:56 +01004496 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004497
4498 def setUp(self):
4499 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004500 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004501 self.saved_handlers = logging._handlers.copy()
4502 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004503 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004504 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004505 logging.root.handlers = []
4506
4507 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004508 for h in logging.root.handlers[:]:
4509 logging.root.removeHandler(h)
4510 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004511 super(BasicConfigTest, self).tearDown()
4512
Vinay Sajip3def7e02011-04-20 10:58:06 +01004513 def cleanup(self):
4514 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004515 logging._handlers.clear()
4516 logging._handlers.update(self.saved_handlers)
4517 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004518 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004519
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004520 def test_no_kwargs(self):
4521 logging.basicConfig()
4522
4523 # handler defaults to a StreamHandler to sys.stderr
4524 self.assertEqual(len(logging.root.handlers), 1)
4525 handler = logging.root.handlers[0]
4526 self.assertIsInstance(handler, logging.StreamHandler)
4527 self.assertEqual(handler.stream, sys.stderr)
4528
4529 formatter = handler.formatter
4530 # format defaults to logging.BASIC_FORMAT
4531 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4532 # datefmt defaults to None
4533 self.assertIsNone(formatter.datefmt)
4534 # style defaults to %
4535 self.assertIsInstance(formatter._style, logging.PercentStyle)
4536
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004537 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004538 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004539
Vinay Sajip1fd12022014-01-13 21:59:56 +00004540 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004541 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004542 logging.basicConfig(stream=sys.stdout, style="{")
4543 logging.error("Log an error")
4544 sys.stdout.seek(0)
4545 self.assertEqual(output.getvalue().strip(),
4546 "ERROR:root:Log an error")
4547
4548 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004549 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004550 logging.basicConfig(stream=sys.stdout, style="$")
4551 logging.error("Log an error")
4552 sys.stdout.seek(0)
4553 self.assertEqual(output.getvalue().strip(),
4554 "ERROR:root:Log an error")
4555
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004556 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004557
4558 def cleanup(h1, h2, fn):
4559 h1.close()
4560 h2.close()
4561 os.remove(fn)
4562
Inada Naokifb786922021-04-06 11:18:41 +09004563 logging.basicConfig(filename='test.log', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004564
4565 self.assertEqual(len(logging.root.handlers), 1)
4566 handler = logging.root.handlers[0]
4567 self.assertIsInstance(handler, logging.FileHandler)
4568
Inada Naokifb786922021-04-06 11:18:41 +09004569 expected = logging.FileHandler('test.log', 'a', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004570 self.assertEqual(handler.stream.mode, expected.stream.mode)
4571 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004572 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004573
4574 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004575
4576 def cleanup(h1, h2, fn):
4577 h1.close()
4578 h2.close()
4579 os.remove(fn)
4580
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004581 logging.basicConfig(filename='test.log', filemode='wb')
4582
4583 handler = logging.root.handlers[0]
4584 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004585 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004586 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004587
4588 def test_stream(self):
4589 stream = io.StringIO()
4590 self.addCleanup(stream.close)
4591 logging.basicConfig(stream=stream)
4592
4593 self.assertEqual(len(logging.root.handlers), 1)
4594 handler = logging.root.handlers[0]
4595 self.assertIsInstance(handler, logging.StreamHandler)
4596 self.assertEqual(handler.stream, stream)
4597
4598 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004599 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004600
4601 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004602 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004603
4604 def test_datefmt(self):
4605 logging.basicConfig(datefmt='bar')
4606
4607 formatter = logging.root.handlers[0].formatter
4608 self.assertEqual(formatter.datefmt, 'bar')
4609
4610 def test_style(self):
4611 logging.basicConfig(style='$')
4612
4613 formatter = logging.root.handlers[0].formatter
4614 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4615
4616 def test_level(self):
4617 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004618 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004619
4620 logging.basicConfig(level=57)
4621 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004622 # Test that second call has no effect
4623 logging.basicConfig(level=58)
4624 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004625
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004626 def test_incompatible(self):
4627 assertRaises = self.assertRaises
4628 handlers = [logging.StreamHandler()]
4629 stream = sys.stderr
4630 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004631 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004632 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004633 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004634 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004635 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004636 # Issue 23207: test for invalid kwargs
4637 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4638 # Should pop both filename and filemode even if filename is None
4639 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004640
4641 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004642 handlers = [
4643 logging.StreamHandler(),
4644 logging.StreamHandler(sys.stdout),
4645 logging.StreamHandler(),
4646 ]
4647 f = logging.Formatter()
4648 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004649 logging.basicConfig(handlers=handlers)
4650 self.assertIs(handlers[0], logging.root.handlers[0])
4651 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004652 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004653 self.assertIsNotNone(handlers[0].formatter)
4654 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004655 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004656 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4657
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004658 def test_force(self):
4659 old_string_io = io.StringIO()
4660 new_string_io = io.StringIO()
4661 old_handlers = [logging.StreamHandler(old_string_io)]
4662 new_handlers = [logging.StreamHandler(new_string_io)]
4663 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4664 logging.warning('warn')
4665 logging.info('info')
4666 logging.debug('debug')
4667 self.assertEqual(len(logging.root.handlers), 1)
4668 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4669 force=True)
4670 logging.warning('warn')
4671 logging.info('info')
4672 logging.debug('debug')
4673 self.assertEqual(len(logging.root.handlers), 1)
4674 self.assertEqual(old_string_io.getvalue().strip(),
4675 'WARNING:root:warn')
4676 self.assertEqual(new_string_io.getvalue().strip(),
4677 'WARNING:root:warn\nINFO:root:info')
4678
Vinay Sajipca7b5042019-06-17 17:40:52 +01004679 def test_encoding(self):
4680 try:
4681 encoding = 'utf-8'
4682 logging.basicConfig(filename='test.log', encoding=encoding,
4683 errors='strict',
4684 format='%(message)s', level=logging.DEBUG)
4685
4686 self.assertEqual(len(logging.root.handlers), 1)
4687 handler = logging.root.handlers[0]
4688 self.assertIsInstance(handler, logging.FileHandler)
4689 self.assertEqual(handler.encoding, encoding)
4690 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4691 finally:
4692 handler.close()
4693 with open('test.log', encoding='utf-8') as f:
4694 data = f.read().strip()
4695 os.remove('test.log')
4696 self.assertEqual(data,
4697 'The Øresund Bridge joins Copenhagen to Malmö')
4698
4699 def test_encoding_errors(self):
4700 try:
4701 encoding = 'ascii'
4702 logging.basicConfig(filename='test.log', encoding=encoding,
4703 errors='ignore',
4704 format='%(message)s', level=logging.DEBUG)
4705
4706 self.assertEqual(len(logging.root.handlers), 1)
4707 handler = logging.root.handlers[0]
4708 self.assertIsInstance(handler, logging.FileHandler)
4709 self.assertEqual(handler.encoding, encoding)
4710 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4711 finally:
4712 handler.close()
4713 with open('test.log', encoding='utf-8') as f:
4714 data = f.read().strip()
4715 os.remove('test.log')
4716 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4717
4718 def test_encoding_errors_default(self):
4719 try:
4720 encoding = 'ascii'
4721 logging.basicConfig(filename='test.log', encoding=encoding,
4722 format='%(message)s', level=logging.DEBUG)
4723
4724 self.assertEqual(len(logging.root.handlers), 1)
4725 handler = logging.root.handlers[0]
4726 self.assertIsInstance(handler, logging.FileHandler)
4727 self.assertEqual(handler.encoding, encoding)
4728 self.assertEqual(handler.errors, 'backslashreplace')
4729 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4730 finally:
4731 handler.close()
4732 with open('test.log', encoding='utf-8') as f:
4733 data = f.read().strip()
4734 os.remove('test.log')
4735 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4736 r'Bridge joins Copenhagen to Malm\xf6')
4737
4738 def test_encoding_errors_none(self):
4739 # Specifying None should behave as 'strict'
4740 try:
4741 encoding = 'ascii'
4742 logging.basicConfig(filename='test.log', encoding=encoding,
4743 errors=None,
4744 format='%(message)s', level=logging.DEBUG)
4745
4746 self.assertEqual(len(logging.root.handlers), 1)
4747 handler = logging.root.handlers[0]
4748 self.assertIsInstance(handler, logging.FileHandler)
4749 self.assertEqual(handler.encoding, encoding)
4750 self.assertIsNone(handler.errors)
4751
4752 message = []
4753
4754 def dummy_handle_error(record):
4755 _, v, _ = sys.exc_info()
4756 message.append(str(v))
4757
4758 handler.handleError = dummy_handle_error
4759 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4760 self.assertTrue(message)
4761 self.assertIn("'ascii' codec can't encode "
4762 "character '\\xd8' in position 4:", message[0])
4763 finally:
4764 handler.close()
4765 with open('test.log', encoding='utf-8') as f:
4766 data = f.read().strip()
4767 os.remove('test.log')
4768 # didn't write anything due to the encoding error
4769 self.assertEqual(data, r'')
4770
4771
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004772 def _test_log(self, method, level=None):
4773 # logging.root has no handlers so basicConfig should be called
4774 called = []
4775
4776 old_basic_config = logging.basicConfig
4777 def my_basic_config(*a, **kw):
4778 old_basic_config()
4779 old_level = logging.root.level
4780 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004781 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004782 called.append((a, kw))
4783
Vinay Sajip1feedb42014-05-31 08:19:12 +01004784 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004785
4786 log_method = getattr(logging, method)
4787 if level is not None:
4788 log_method(level, "test me")
4789 else:
4790 log_method("test me")
4791
4792 # basicConfig was called with no arguments
4793 self.assertEqual(called, [((), {})])
4794
4795 def test_log(self):
4796 self._test_log('log', logging.WARNING)
4797
4798 def test_debug(self):
4799 self._test_log('debug')
4800
4801 def test_info(self):
4802 self._test_log('info')
4803
4804 def test_warning(self):
4805 self._test_log('warning')
4806
4807 def test_error(self):
4808 self._test_log('error')
4809
4810 def test_critical(self):
4811 self._test_log('critical')
4812
4813
4814class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004815 def setUp(self):
4816 super(LoggerAdapterTest, self).setUp()
4817 old_handler_list = logging._handlerList[:]
4818
4819 self.recording = RecordingHandler()
4820 self.logger = logging.root
4821 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004822 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004823 self.addCleanup(self.recording.close)
4824
4825 def cleanup():
4826 logging._handlerList[:] = old_handler_list
4827
4828 self.addCleanup(cleanup)
4829 self.addCleanup(logging.shutdown)
4830 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4831
4832 def test_exception(self):
4833 msg = 'testing exception: %r'
4834 exc = None
4835 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004836 1 / 0
4837 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004838 exc = e
4839 self.adapter.exception(msg, self.recording)
4840
4841 self.assertEqual(len(self.recording.records), 1)
4842 record = self.recording.records[0]
4843 self.assertEqual(record.levelno, logging.ERROR)
4844 self.assertEqual(record.msg, msg)
4845 self.assertEqual(record.args, (self.recording,))
4846 self.assertEqual(record.exc_info,
4847 (exc.__class__, exc, exc.__traceback__))
4848
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004849 def test_exception_excinfo(self):
4850 try:
4851 1 / 0
4852 except ZeroDivisionError as e:
4853 exc = e
4854
4855 self.adapter.exception('exc_info test', exc_info=exc)
4856
4857 self.assertEqual(len(self.recording.records), 1)
4858 record = self.recording.records[0]
4859 self.assertEqual(record.exc_info,
4860 (exc.__class__, exc, exc.__traceback__))
4861
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004862 def test_critical(self):
4863 msg = 'critical test! %r'
4864 self.adapter.critical(msg, self.recording)
4865
4866 self.assertEqual(len(self.recording.records), 1)
4867 record = self.recording.records[0]
4868 self.assertEqual(record.levelno, logging.CRITICAL)
4869 self.assertEqual(record.msg, msg)
4870 self.assertEqual(record.args, (self.recording,))
4871
4872 def test_is_enabled_for(self):
4873 old_disable = self.adapter.logger.manager.disable
4874 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004875 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4876 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004877 self.assertFalse(self.adapter.isEnabledFor(32))
4878
4879 def test_has_handlers(self):
4880 self.assertTrue(self.adapter.hasHandlers())
4881
4882 for handler in self.logger.handlers:
4883 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004884
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004885 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004886 self.assertFalse(self.adapter.hasHandlers())
4887
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004888 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004889 class Adapter(logging.LoggerAdapter):
4890 prefix = 'Adapter'
4891
4892 def process(self, msg, kwargs):
4893 return f"{self.prefix} {msg}", kwargs
4894
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004895 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004896 adapter = Adapter(logger=self.logger, extra=None)
4897 adapter_adapter = Adapter(logger=adapter, extra=None)
4898 adapter_adapter.prefix = 'AdapterAdapter'
4899 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004900 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004901 self.assertEqual(len(self.recording.records), 1)
4902 record = self.recording.records[0]
4903 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004904 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004905 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004906 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004907 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004908 self.assertIs(self.logger.manager, orig_manager)
4909 temp_manager = object()
4910 try:
4911 adapter_adapter.manager = temp_manager
4912 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004913 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004914 self.assertIs(self.logger.manager, temp_manager)
4915 finally:
4916 adapter_adapter.manager = orig_manager
4917 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004918 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004919 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004920
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004921
Eric Larson9fdb76c2020-09-25 14:08:50 -04004922class LoggerTest(BaseTest, AssertErrorMessage):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004923
4924 def setUp(self):
4925 super(LoggerTest, self).setUp()
4926 self.recording = RecordingHandler()
4927 self.logger = logging.Logger(name='blah')
4928 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004929 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004930 self.addCleanup(self.recording.close)
4931 self.addCleanup(logging.shutdown)
4932
4933 def test_set_invalid_level(self):
Eric Larson9fdb76c2020-09-25 14:08:50 -04004934 self.assert_error_message(
4935 TypeError, 'Level not an integer or a valid string: None',
4936 self.logger.setLevel, None)
4937 self.assert_error_message(
4938 TypeError, 'Level not an integer or a valid string: (0, 0)',
4939 self.logger.setLevel, (0, 0))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004940
4941 def test_exception(self):
4942 msg = 'testing exception: %r'
4943 exc = None
4944 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004945 1 / 0
4946 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004947 exc = e
4948 self.logger.exception(msg, self.recording)
4949
4950 self.assertEqual(len(self.recording.records), 1)
4951 record = self.recording.records[0]
4952 self.assertEqual(record.levelno, logging.ERROR)
4953 self.assertEqual(record.msg, msg)
4954 self.assertEqual(record.args, (self.recording,))
4955 self.assertEqual(record.exc_info,
4956 (exc.__class__, exc, exc.__traceback__))
4957
4958 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004959 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004960 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004961
4962 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004963 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004964 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004965
4966 def test_find_caller_with_stack_info(self):
4967 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004968 support.patch(self, logging.traceback, 'print_stack',
4969 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004970
4971 self.logger.findCaller(stack_info=True)
4972
4973 self.assertEqual(len(called), 1)
4974 self.assertEqual('Stack (most recent call last):\n', called[0])
4975
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004976 def test_find_caller_with_stacklevel(self):
4977 the_level = 1
4978
4979 def innermost():
4980 self.logger.warning('test', stacklevel=the_level)
4981
4982 def inner():
4983 innermost()
4984
4985 def outer():
4986 inner()
4987
4988 records = self.recording.records
4989 outer()
4990 self.assertEqual(records[-1].funcName, 'innermost')
4991 lineno = records[-1].lineno
4992 the_level += 1
4993 outer()
4994 self.assertEqual(records[-1].funcName, 'inner')
4995 self.assertGreater(records[-1].lineno, lineno)
4996 lineno = records[-1].lineno
4997 the_level += 1
4998 outer()
4999 self.assertEqual(records[-1].funcName, 'outer')
5000 self.assertGreater(records[-1].lineno, lineno)
5001 lineno = records[-1].lineno
5002 the_level += 1
5003 outer()
5004 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5005 self.assertGreater(records[-1].lineno, lineno)
5006
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005007 def test_make_record_with_extra_overwrite(self):
5008 name = 'my record'
5009 level = 13
5010 fn = lno = msg = args = exc_info = func = sinfo = None
5011 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
5012 exc_info, func, sinfo)
5013
5014 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
5015 extra = {key: 'some value'}
5016 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
5017 fn, lno, msg, args, exc_info,
5018 extra=extra, sinfo=sinfo)
5019
5020 def test_make_record_with_extra_no_overwrite(self):
5021 name = 'my record'
5022 level = 13
5023 fn = lno = msg = args = exc_info = func = sinfo = None
5024 extra = {'valid_key': 'some value'}
5025 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
5026 exc_info, extra=extra, sinfo=sinfo)
5027 self.assertIn('valid_key', result.__dict__)
5028
5029 def test_has_handlers(self):
5030 self.assertTrue(self.logger.hasHandlers())
5031
5032 for handler in self.logger.handlers:
5033 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005034 self.assertFalse(self.logger.hasHandlers())
5035
5036 def test_has_handlers_no_propagate(self):
5037 child_logger = logging.getLogger('blah.child')
5038 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005039 self.assertFalse(child_logger.hasHandlers())
5040
5041 def test_is_enabled_for(self):
5042 old_disable = self.logger.manager.disable
5043 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01005044 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005045 self.assertFalse(self.logger.isEnabledFor(22))
5046
Timo Furrer6e3ca642018-06-01 09:29:46 +02005047 def test_is_enabled_for_disabled_logger(self):
5048 old_disabled = self.logger.disabled
5049 old_disable = self.logger.manager.disable
5050
5051 self.logger.disabled = True
5052 self.logger.manager.disable = 21
5053
5054 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
5055 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
5056
5057 self.assertFalse(self.logger.isEnabledFor(22))
5058
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005059 def test_root_logger_aliases(self):
5060 root = logging.getLogger()
5061 self.assertIs(root, logging.root)
5062 self.assertIs(root, logging.getLogger(None))
5063 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01005064 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005065 self.assertIs(root, logging.getLogger('foo').root)
5066 self.assertIs(root, logging.getLogger('foo.bar').root)
5067 self.assertIs(root, logging.getLogger('foo').parent)
5068
5069 self.assertIsNot(root, logging.getLogger('\0'))
5070 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
5071
5072 def test_invalid_names(self):
5073 self.assertRaises(TypeError, logging.getLogger, any)
5074 self.assertRaises(TypeError, logging.getLogger, b'foo')
5075
Vinay Sajip6260d9f2017-06-06 16:34:29 +01005076 def test_pickling(self):
5077 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
5078 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
5079 logger = logging.getLogger(name)
5080 s = pickle.dumps(logger, proto)
5081 unpickled = pickle.loads(s)
5082 self.assertIs(unpickled, logger)
5083
Avram Lubkin78c18a92017-07-30 05:36:33 -04005084 def test_caching(self):
5085 root = self.root_logger
5086 logger1 = logging.getLogger("abc")
5087 logger2 = logging.getLogger("abc.def")
5088
5089 # Set root logger level and ensure cache is empty
5090 root.setLevel(logging.ERROR)
5091 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
5092 self.assertEqual(logger2._cache, {})
5093
5094 # Ensure cache is populated and calls are consistent
5095 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5096 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
5097 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
5098 self.assertEqual(root._cache, {})
5099 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5100
5101 # Ensure root cache gets populated
5102 self.assertEqual(root._cache, {})
5103 self.assertTrue(root.isEnabledFor(logging.ERROR))
5104 self.assertEqual(root._cache, {logging.ERROR: True})
5105
5106 # Set parent logger level and ensure caches are emptied
5107 logger1.setLevel(logging.CRITICAL)
5108 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5109 self.assertEqual(logger2._cache, {})
5110
5111 # Ensure logger2 uses parent logger's effective level
5112 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5113
5114 # Set level to NOTSET and ensure caches are empty
5115 logger2.setLevel(logging.NOTSET)
5116 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5117 self.assertEqual(logger2._cache, {})
5118 self.assertEqual(logger1._cache, {})
5119 self.assertEqual(root._cache, {})
5120
5121 # Verify logger2 follows parent and not root
5122 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5123 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
5124 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
5125 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
5126 self.assertTrue(root.isEnabledFor(logging.ERROR))
5127
5128 # Disable logging in manager and ensure caches are clear
5129 logging.disable()
5130 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5131 self.assertEqual(logger2._cache, {})
5132 self.assertEqual(logger1._cache, {})
5133 self.assertEqual(root._cache, {})
5134
5135 # Ensure no loggers are enabled
5136 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5137 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5138 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5139
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005140
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005141class BaseFileTest(BaseTest):
5142 "Base class for handler tests that write log files"
5143
5144 def setUp(self):
5145 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005146 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5147 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005148 self.rmfiles = []
5149
5150 def tearDown(self):
5151 for fn in self.rmfiles:
5152 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005153 if os.path.exists(self.fn):
5154 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005155 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005156
5157 def assertLogFile(self, filename):
5158 "Assert a log file is there and register it for deletion"
5159 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005160 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005161 self.rmfiles.append(filename)
5162
5163
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005164class FileHandlerTest(BaseFileTest):
5165 def test_delay(self):
5166 os.unlink(self.fn)
Inada Naokifb786922021-04-06 11:18:41 +09005167 fh = logging.FileHandler(self.fn, encoding='utf-8', delay=True)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005168 self.assertIsNone(fh.stream)
5169 self.assertFalse(os.path.exists(self.fn))
5170 fh.handle(logging.makeLogRecord({}))
5171 self.assertIsNotNone(fh.stream)
5172 self.assertTrue(os.path.exists(self.fn))
5173 fh.close()
5174
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005175class RotatingFileHandlerTest(BaseFileTest):
5176 def next_rec(self):
5177 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5178 self.next_message(), None, None, None)
5179
5180 def test_should_not_rollover(self):
5181 # If maxbytes is zero rollover never occurs
Inada Naokifb786922021-04-06 11:18:41 +09005182 rh = logging.handlers.RotatingFileHandler(
5183 self.fn, encoding="utf-8", maxBytes=0)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005184 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005185 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005186
5187 def test_should_rollover(self):
Inada Naokifb786922021-04-06 11:18:41 +09005188 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8", maxBytes=1)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005189 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005190 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005191
5192 def test_file_created(self):
5193 # checks that the file is created and assumes it was created
5194 # by us
Inada Naokifb786922021-04-06 11:18:41 +09005195 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8")
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005196 rh.emit(self.next_rec())
5197 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005198 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005199
5200 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005201 def namer(name):
5202 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005203 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005204 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005205 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005206 rh.emit(self.next_rec())
5207 self.assertLogFile(self.fn)
5208 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005209 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005210 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005211 self.assertLogFile(namer(self.fn + ".2"))
5212 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5213 rh.close()
5214
l0rb519cb872019-11-06 22:21:40 +01005215 def test_namer_rotator_inheritance(self):
5216 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5217 def namer(self, name):
5218 return name + ".test"
5219
5220 def rotator(self, source, dest):
5221 if os.path.exists(source):
5222 os.rename(source, dest + ".rotated")
5223
5224 rh = HandlerWithNamerAndRotator(
Inada Naokifb786922021-04-06 11:18:41 +09005225 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
l0rb519cb872019-11-06 22:21:40 +01005226 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5227 rh.emit(self.next_rec())
5228 self.assertLogFile(self.fn)
5229 rh.emit(self.next_rec())
5230 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5231 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5232 rh.close()
5233
Hai Shia3ec3ad2020-05-19 06:02:57 +08005234 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005235 def test_rotator(self):
5236 def namer(name):
5237 return name + ".gz"
5238
5239 def rotator(source, dest):
5240 with open(source, "rb") as sf:
5241 data = sf.read()
5242 compressed = zlib.compress(data, 9)
5243 with open(dest, "wb") as df:
5244 df.write(compressed)
5245 os.remove(source)
5246
5247 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005248 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005249 rh.rotator = rotator
5250 rh.namer = namer
5251 m1 = self.next_rec()
5252 rh.emit(m1)
5253 self.assertLogFile(self.fn)
5254 m2 = self.next_rec()
5255 rh.emit(m2)
5256 fn = namer(self.fn + ".1")
5257 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005258 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005259 with open(fn, "rb") as f:
5260 compressed = f.read()
5261 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005262 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005263 rh.emit(self.next_rec())
5264 fn = namer(self.fn + ".2")
5265 self.assertLogFile(fn)
5266 with open(fn, "rb") as f:
5267 compressed = f.read()
5268 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005269 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005270 rh.emit(self.next_rec())
5271 fn = namer(self.fn + ".2")
5272 with open(fn, "rb") as f:
5273 compressed = f.read()
5274 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005275 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005276 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005277 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005278
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005279class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005280 # other test methods added below
5281 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01005282 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
5283 backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005284 fmt = logging.Formatter('%(asctime)s %(message)s')
5285 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005286 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005287 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005288 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005289 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005290 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005291 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005292 fh.close()
5293 # At this point, we should have a recent rotated file which we
5294 # can test for the existence of. However, in practice, on some
5295 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005296 # in time to go to look for the log file. So, we go back a fair
5297 # bit, and stop as soon as we see a rotated file. In theory this
5298 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005299 found = False
5300 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005301 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005302 for secs in range(GO_BACK):
5303 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005304 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5305 found = os.path.exists(fn)
5306 if found:
5307 self.rmfiles.append(fn)
5308 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005309 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5310 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005311 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005312 dn, fn = os.path.split(self.fn)
5313 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005314 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5315 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005316 for f in files:
5317 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005318 path = os.path.join(dn, f)
5319 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005320 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005321 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005322
Vinay Sajip0372e102011-05-05 12:59:14 +01005323 def test_invalid(self):
5324 assertRaises = self.assertRaises
5325 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005326 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005327 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005328 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005329 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01005330 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005331
Vinay Sajipa7130792013-04-12 17:04:23 +01005332 def test_compute_rollover_daily_attime(self):
5333 currentTime = 0
5334 atTime = datetime.time(12, 0, 0)
5335 rh = logging.handlers.TimedRotatingFileHandler(
5336 self.fn, when='MIDNIGHT', interval=1, backupCount=0, utc=True,
5337 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005338 try:
5339 actual = rh.computeRollover(currentTime)
5340 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005341
Vinay Sajipd86ac962013-04-14 12:20:46 +01005342 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5343 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5344 finally:
5345 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005346
Vinay Sajip10e8c492013-05-18 10:19:54 -07005347 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005348 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005349 currentTime = int(time.time())
5350 today = currentTime - currentTime % 86400
5351
Vinay Sajipa7130792013-04-12 17:04:23 +01005352 atTime = datetime.time(12, 0, 0)
5353
Vinay Sajip10e8c492013-05-18 10:19:54 -07005354 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005355 for day in range(7):
5356 rh = logging.handlers.TimedRotatingFileHandler(
5357 self.fn, when='W%d' % day, interval=1, backupCount=0, utc=True,
5358 atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005359 try:
5360 if wday > day:
5361 # The rollover day has already passed this week, so we
5362 # go over into next week
5363 expected = (7 - wday + day)
5364 else:
5365 expected = (day - wday)
5366 # At this point expected is in days from now, convert to seconds
5367 expected *= 24 * 60 * 60
5368 # Add in the rollover time
5369 expected += 12 * 60 * 60
5370 # Add in adjustment for today
5371 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005372 actual = rh.computeRollover(today)
5373 if actual != expected:
5374 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005375 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005376 self.assertEqual(actual, expected)
5377 if day == wday:
5378 # goes into following week
5379 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005380 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005381 if actual != expected:
5382 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005383 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005384 self.assertEqual(actual, expected)
5385 finally:
5386 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005387
5388
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005389def secs(**kw):
5390 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5391
5392for when, exp in (('S', 1),
5393 ('M', 60),
5394 ('H', 60 * 60),
5395 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005396 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005397 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005398 ('W0', secs(days=4, hours=24)),
5399 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005400 def test_compute_rollover(self, when=when, exp=exp):
5401 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00005402 self.fn, when=when, interval=1, backupCount=0, utc=True)
5403 currentTime = 0.0
5404 actual = rh.computeRollover(currentTime)
5405 if exp != actual:
5406 # Failures occur on some systems for MIDNIGHT and W0.
5407 # Print detailed calculation for MIDNIGHT so we can try to see
5408 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005409 if when == 'MIDNIGHT':
5410 try:
5411 if rh.utc:
5412 t = time.gmtime(currentTime)
5413 else:
5414 t = time.localtime(currentTime)
5415 currentHour = t[3]
5416 currentMinute = t[4]
5417 currentSecond = t[5]
5418 # r is the number of seconds left between now and midnight
5419 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5420 currentMinute) * 60 +
5421 currentSecond)
5422 result = currentTime + r
5423 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5424 print('currentHour: %s' % currentHour, file=sys.stderr)
5425 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5426 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5427 print('r: %s' % r, file=sys.stderr)
5428 print('result: %s' % result, file=sys.stderr)
5429 except Exception:
5430 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5431 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005432 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005433 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5434
Vinay Sajip60ccd822011-05-09 17:32:09 +01005435
Vinay Sajip223349c2015-10-01 20:37:54 +01005436@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005437class NTEventLogHandlerTest(BaseTest):
5438 def test_basic(self):
5439 logtype = 'Application'
5440 elh = win32evtlog.OpenEventLog(None, logtype)
5441 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005442
5443 try:
5444 h = logging.handlers.NTEventLogHandler('test_logging')
5445 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005446 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005447 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005448 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005449
Vinay Sajip60ccd822011-05-09 17:32:09 +01005450 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5451 h.handle(r)
5452 h.close()
5453 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005454 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005455 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5456 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5457 found = False
5458 GO_BACK = 100
5459 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5460 for e in events:
5461 if e.SourceName != 'test_logging':
5462 continue
5463 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5464 if msg != 'Test Log Message\r\n':
5465 continue
5466 found = True
5467 break
5468 msg = 'Record not found in event log, went back %d records' % GO_BACK
5469 self.assertTrue(found, msg=msg)
5470
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005471
5472class MiscTestCase(unittest.TestCase):
5473 def test__all__(self):
Victor Stinnerfbf43f02020-08-17 07:20:40 +02005474 not_exported = {
5475 'logThreads', 'logMultiprocessing', 'logProcesses', 'currentframe',
5476 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5477 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', 'root',
5478 'threading'}
5479 support.check__all__(self, logging, not_exported=not_exported)
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005480
5481
Christian Heimes180510d2008-03-03 19:15:45 +00005482# Set the locale to the platform-dependent default. I have no idea
5483# why the test does this, but in any case we save the current locale
5484# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005485@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005486def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005487 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005488 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5489 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5490 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5491 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5492 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5493 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5494 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5495 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005496 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005497 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005498 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005499 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005500 ]
5501 if hasattr(logging.handlers, 'QueueListener'):
5502 tests.append(QueueListenerTest)
5503 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005504
Christian Heimes180510d2008-03-03 19:15:45 +00005505if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005506 test_main()