blob: ee00a32026f65ec39c72f73f4f1aa60b198f3d20 [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}",)
Inada Naokifa51c0c2021-04-29 11:34:56 +09001431 kwargs={{"encoding": "utf-8"}}
Xtreak087570a2018-07-02 14:27:46 +05301432 """
1433
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001434 disable_test = """
1435 [loggers]
1436 keys=root
1437
1438 [handlers]
1439 keys=screen
1440
1441 [formatters]
1442 keys=
1443
1444 [logger_root]
1445 level=DEBUG
1446 handlers=screen
1447
1448 [handler_screen]
1449 level=DEBUG
1450 class=StreamHandler
1451 args=(sys.stdout,)
1452 formatter=
1453 """
1454
1455 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001456 file = io.StringIO(textwrap.dedent(conf))
Inada Naokifa51c0c2021-04-29 11:34:56 +09001457 logging.config.fileConfig(file, encoding="utf-8", **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001458
1459 def test_config0_ok(self):
1460 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001461 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001462 self.apply_config(self.config0)
1463 logger = logging.getLogger()
1464 # Won't output anything
1465 logger.info(self.next_message())
1466 # Outputs a message
1467 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001468 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001469 ('ERROR', '2'),
1470 ], stream=output)
1471 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001472 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001473
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001474 def test_config0_using_cp_ok(self):
1475 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001476 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001477 file = io.StringIO(textwrap.dedent(self.config0))
1478 cp = configparser.ConfigParser()
1479 cp.read_file(file)
1480 logging.config.fileConfig(cp)
1481 logger = logging.getLogger()
1482 # Won't output anything
1483 logger.info(self.next_message())
1484 # Outputs a message
1485 logger.error(self.next_message())
1486 self.assert_log_lines([
1487 ('ERROR', '2'),
1488 ], stream=output)
1489 # Original logger output is empty.
1490 self.assert_log_lines([])
1491
Georg Brandl3dbca812008-07-23 16:10:53 +00001492 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001493 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001494 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001495 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001496 logger = logging.getLogger("compiler.parser")
1497 # Both will output a message
1498 logger.info(self.next_message())
1499 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001500 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001501 ('INFO', '1'),
1502 ('ERROR', '2'),
1503 ], stream=output)
1504 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001505 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001506
1507 def test_config2_failure(self):
1508 # A simple config file which overrides the default settings.
1509 self.assertRaises(Exception, self.apply_config, self.config2)
1510
1511 def test_config3_failure(self):
1512 # A simple config file which overrides the default settings.
1513 self.assertRaises(Exception, self.apply_config, self.config3)
1514
1515 def test_config4_ok(self):
1516 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001517 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001518 self.apply_config(self.config4)
1519 logger = logging.getLogger()
1520 try:
1521 raise RuntimeError()
1522 except RuntimeError:
1523 logging.exception("just testing")
1524 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001525 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001526 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1527 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001528 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001529
Georg Brandl3dbca812008-07-23 16:10:53 +00001530 def test_config5_ok(self):
1531 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001532
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001533 def test_config6_ok(self):
1534 self.test_config1_ok(config=self.config6)
1535
Vinay Sajip3f84b072011-03-07 17:49:33 +00001536 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001537 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001538 self.apply_config(self.config1a)
1539 logger = logging.getLogger("compiler.parser")
1540 # See issue #11424. compiler-hyphenated sorts
1541 # between compiler and compiler.xyz and this
1542 # was preventing compiler.xyz from being included
1543 # in the child loggers of compiler because of an
1544 # overzealous loop termination condition.
1545 hyphenated = logging.getLogger('compiler-hyphenated')
1546 # All will output a message
1547 logger.info(self.next_message())
1548 logger.error(self.next_message())
1549 hyphenated.critical(self.next_message())
1550 self.assert_log_lines([
1551 ('INFO', '1'),
1552 ('ERROR', '2'),
1553 ('CRITICAL', '3'),
1554 ], stream=output)
1555 # Original logger output is empty.
1556 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001557 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001558 self.apply_config(self.config7)
1559 logger = logging.getLogger("compiler.parser")
1560 self.assertFalse(logger.disabled)
1561 # Both will output a message
1562 logger.info(self.next_message())
1563 logger.error(self.next_message())
1564 logger = logging.getLogger("compiler.lexer")
1565 # Both will output a message
1566 logger.info(self.next_message())
1567 logger.error(self.next_message())
1568 # Will not appear
1569 hyphenated.critical(self.next_message())
1570 self.assert_log_lines([
1571 ('INFO', '4'),
1572 ('ERROR', '5'),
1573 ('INFO', '6'),
1574 ('ERROR', '7'),
1575 ], stream=output)
1576 # Original logger output is empty.
1577 self.assert_log_lines([])
1578
Xtreak087570a2018-07-02 14:27:46 +05301579 def test_config8_ok(self):
1580
1581 def cleanup(h1, fn):
1582 h1.close()
1583 os.remove(fn)
1584
Inada Naoki53dd6c92021-04-29 20:37:32 +09001585 with self.check_no_resource_warning():
Xtreak087570a2018-07-02 14:27:46 +05301586 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1587 os.close(fd)
1588
1589 # Replace single backslash with double backslash in windows
1590 # to avoid unicode error during string formatting
1591 if os.name == "nt":
1592 fn = fn.replace("\\", "\\\\")
1593
1594 config8 = self.config8.format(tempfile=fn)
1595
1596 self.apply_config(config8)
1597 self.apply_config(config8)
1598
1599 handler = logging.root.handlers[0]
1600 self.addCleanup(cleanup, handler, fn)
1601
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001602 def test_logger_disabling(self):
1603 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001604 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001605 self.assertFalse(logger.disabled)
1606 self.apply_config(self.disable_test)
1607 self.assertTrue(logger.disabled)
1608 self.apply_config(self.disable_test, disable_existing_loggers=False)
1609 self.assertFalse(logger.disabled)
1610
Lucas Cimonb15100f2019-10-31 09:06:25 +01001611 def test_config_set_handler_names(self):
1612 test_config = """
1613 [loggers]
1614 keys=root
1615
1616 [handlers]
1617 keys=hand1
1618
1619 [formatters]
1620 keys=form1
1621
1622 [logger_root]
1623 handlers=hand1
1624
1625 [handler_hand1]
1626 class=StreamHandler
1627 formatter=form1
1628
1629 [formatter_form1]
1630 format=%(levelname)s ++ %(message)s
1631 """
1632 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001633 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001634
Łukasz Langa214f18e2018-06-08 04:02:48 -07001635 def test_defaults_do_no_interpolation(self):
1636 """bpo-33802 defaults should not get interpolated"""
1637 ini = textwrap.dedent("""
1638 [formatters]
1639 keys=default
1640
1641 [formatter_default]
1642
1643 [handlers]
1644 keys=console
1645
1646 [handler_console]
1647 class=logging.StreamHandler
1648 args=tuple()
1649
1650 [loggers]
1651 keys=root
1652
1653 [logger_root]
1654 formatter=default
1655 handlers=console
1656 """).strip()
1657 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1658 try:
1659 os.write(fd, ini.encode('ascii'))
1660 os.close(fd)
1661 logging.config.fileConfig(
1662 fn,
Inada Naokifa51c0c2021-04-29 11:34:56 +09001663 encoding="utf-8",
Łukasz Langa214f18e2018-06-08 04:02:48 -07001664 defaults=dict(
1665 version=1,
1666 disable_existing_loggers=False,
1667 formatters={
1668 "generic": {
1669 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1670 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1671 "class": "logging.Formatter"
1672 },
1673 },
1674 )
1675 )
1676 finally:
1677 os.unlink(fn)
1678
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001679
Christian Heimes180510d2008-03-03 19:15:45 +00001680class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001681
Christian Heimes180510d2008-03-03 19:15:45 +00001682 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001683
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001684 server_class = TestTCPServer
1685 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001686
Christian Heimes180510d2008-03-03 19:15:45 +00001687 def setUp(self):
1688 """Set up a TCP server to receive log messages, and a SocketHandler
1689 pointing to that server's address and port."""
1690 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001691 # Issue #29177: deal with errors that happen during setup
1692 self.server = self.sock_hdlr = self.server_exception = None
1693 try:
1694 self.server = server = self.server_class(self.address,
1695 self.handle_socket, 0.01)
1696 server.start()
1697 # Uncomment next line to test error recovery in setUp()
1698 # raise OSError('dummy error raised')
1699 except OSError as e:
1700 self.server_exception = e
1701 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001702 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001703 hcls = logging.handlers.SocketHandler
1704 if isinstance(server.server_address, tuple):
1705 self.sock_hdlr = hcls('localhost', server.port)
1706 else:
1707 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001708 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001709 self.root_logger.removeHandler(self.root_logger.handlers[0])
1710 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001711 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001712
Christian Heimes180510d2008-03-03 19:15:45 +00001713 def tearDown(self):
1714 """Shutdown the TCP server."""
1715 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001716 if self.sock_hdlr:
1717 self.root_logger.removeHandler(self.sock_hdlr)
1718 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001719 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001720 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001721 finally:
1722 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001723
Vinay Sajip7367d082011-05-02 13:17:27 +01001724 def handle_socket(self, request):
1725 conn = request.connection
1726 while True:
1727 chunk = conn.recv(4)
1728 if len(chunk) < 4:
1729 break
1730 slen = struct.unpack(">L", chunk)[0]
1731 chunk = conn.recv(slen)
1732 while len(chunk) < slen:
1733 chunk = chunk + conn.recv(slen - len(chunk))
1734 obj = pickle.loads(chunk)
1735 record = logging.makeLogRecord(obj)
1736 self.log_output += record.msg + '\n'
1737 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001738
Christian Heimes180510d2008-03-03 19:15:45 +00001739 def test_output(self):
1740 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001741 if self.server_exception:
1742 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001743 logger = logging.getLogger("tcp")
1744 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001745 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001746 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001747 self.handled.acquire()
1748 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001749
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001750 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001751 if self.server_exception:
1752 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001753 # Avoid timing-related failures due to SocketHandler's own hard-wired
1754 # one-second timeout on socket.create_connection() (issue #16264).
1755 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001756 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001757 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001758 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001759 try:
1760 raise RuntimeError('Deliberate mistake')
1761 except RuntimeError:
1762 self.root_logger.exception('Never sent')
1763 self.root_logger.error('Never sent, either')
1764 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001765 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001766 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1767 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001768
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001769def _get_temp_domain_socket():
1770 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1771 os.close(fd)
1772 # just need a name - file can't be present, or we'll get an
1773 # 'address already in use' error.
1774 os.remove(fn)
1775 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001776
Victor Stinnerec5a8602014-06-02 14:41:51 +02001777@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001778class UnixSocketHandlerTest(SocketHandlerTest):
1779
1780 """Test for SocketHandler with unix sockets."""
1781
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001782 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001783 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001784
1785 def setUp(self):
1786 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001787 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001788 SocketHandlerTest.setUp(self)
1789
1790 def tearDown(self):
1791 SocketHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001792 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001793
Vinay Sajip7367d082011-05-02 13:17:27 +01001794class DatagramHandlerTest(BaseTest):
1795
1796 """Test for DatagramHandler."""
1797
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001798 server_class = TestUDPServer
1799 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001800
Vinay Sajip7367d082011-05-02 13:17:27 +01001801 def setUp(self):
1802 """Set up a UDP server to receive log messages, and a DatagramHandler
1803 pointing to that server's address and port."""
1804 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001805 # Issue #29177: deal with errors that happen during setup
1806 self.server = self.sock_hdlr = self.server_exception = None
1807 try:
1808 self.server = server = self.server_class(self.address,
1809 self.handle_datagram, 0.01)
1810 server.start()
1811 # Uncomment next line to test error recovery in setUp()
1812 # raise OSError('dummy error raised')
1813 except OSError as e:
1814 self.server_exception = e
1815 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001816 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001817 hcls = logging.handlers.DatagramHandler
1818 if isinstance(server.server_address, tuple):
1819 self.sock_hdlr = hcls('localhost', server.port)
1820 else:
1821 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001822 self.log_output = ''
1823 self.root_logger.removeHandler(self.root_logger.handlers[0])
1824 self.root_logger.addHandler(self.sock_hdlr)
1825 self.handled = threading.Event()
1826
1827 def tearDown(self):
1828 """Shutdown the UDP server."""
1829 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001830 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001831 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001832 if self.sock_hdlr:
1833 self.root_logger.removeHandler(self.sock_hdlr)
1834 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001835 finally:
1836 BaseTest.tearDown(self)
1837
1838 def handle_datagram(self, request):
1839 slen = struct.pack('>L', 0) # length of prefix
1840 packet = request.packet[len(slen):]
1841 obj = pickle.loads(packet)
1842 record = logging.makeLogRecord(obj)
1843 self.log_output += record.msg + '\n'
1844 self.handled.set()
1845
1846 def test_output(self):
1847 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001848 if self.server_exception:
1849 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001850 logger = logging.getLogger("udp")
1851 logger.error("spam")
1852 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001853 self.handled.clear()
1854 logger.error("eggs")
1855 self.handled.wait()
1856 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001857
Victor Stinnerec5a8602014-06-02 14:41:51 +02001858@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001859class UnixDatagramHandlerTest(DatagramHandlerTest):
1860
1861 """Test for DatagramHandler using Unix sockets."""
1862
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001863 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001864 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001865
1866 def setUp(self):
1867 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001868 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001869 DatagramHandlerTest.setUp(self)
1870
1871 def tearDown(self):
1872 DatagramHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001873 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001874
Vinay Sajip7367d082011-05-02 13:17:27 +01001875class SysLogHandlerTest(BaseTest):
1876
1877 """Test for SysLogHandler using UDP."""
1878
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001879 server_class = TestUDPServer
1880 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001881
Vinay Sajip7367d082011-05-02 13:17:27 +01001882 def setUp(self):
1883 """Set up a UDP server to receive log messages, and a SysLogHandler
1884 pointing to that server's address and port."""
1885 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001886 # Issue #29177: deal with errors that happen during setup
1887 self.server = self.sl_hdlr = self.server_exception = None
1888 try:
1889 self.server = server = self.server_class(self.address,
1890 self.handle_datagram, 0.01)
1891 server.start()
1892 # Uncomment next line to test error recovery in setUp()
1893 # raise OSError('dummy error raised')
1894 except OSError as e:
1895 self.server_exception = e
1896 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001897 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001898 hcls = logging.handlers.SysLogHandler
1899 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001900 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001901 else:
1902 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001903 self.log_output = ''
1904 self.root_logger.removeHandler(self.root_logger.handlers[0])
1905 self.root_logger.addHandler(self.sl_hdlr)
1906 self.handled = threading.Event()
1907
1908 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001909 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001910 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001911 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001912 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001913 if self.sl_hdlr:
1914 self.root_logger.removeHandler(self.sl_hdlr)
1915 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001916 finally:
1917 BaseTest.tearDown(self)
1918
1919 def handle_datagram(self, request):
1920 self.log_output = request.packet
1921 self.handled.set()
1922
1923 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001924 if self.server_exception:
1925 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001926 # The log message sent to the SysLogHandler is properly received.
1927 logger = logging.getLogger("slh")
1928 logger.error("sp\xe4m")
1929 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001930 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001931 self.handled.clear()
1932 self.sl_hdlr.append_nul = False
1933 logger.error("sp\xe4m")
1934 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001935 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001936 self.handled.clear()
1937 self.sl_hdlr.ident = "h\xe4m-"
1938 logger.error("sp\xe4m")
1939 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001940 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001941
Victor Stinnerec5a8602014-06-02 14:41:51 +02001942@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001943class UnixSysLogHandlerTest(SysLogHandlerTest):
1944
1945 """Test for SysLogHandler with Unix sockets."""
1946
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001947 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001948 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001949
1950 def setUp(self):
1951 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001952 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001953 SysLogHandlerTest.setUp(self)
1954
1955 def tearDown(self):
1956 SysLogHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001957 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001958
Serhiy Storchaka16994912020-04-25 10:06:29 +03001959@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001960 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001961class IPv6SysLogHandlerTest(SysLogHandlerTest):
1962
1963 """Test for SysLogHandler with IPv6 host."""
1964
1965 server_class = TestUDPServer
1966 address = ('::1', 0)
1967
1968 def setUp(self):
1969 self.server_class.address_family = socket.AF_INET6
1970 super(IPv6SysLogHandlerTest, self).setUp()
1971
1972 def tearDown(self):
1973 self.server_class.address_family = socket.AF_INET
1974 super(IPv6SysLogHandlerTest, self).tearDown()
1975
Vinay Sajip7367d082011-05-02 13:17:27 +01001976class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001977 """Test for HTTPHandler."""
1978
1979 def setUp(self):
1980 """Set up an HTTP server to receive log messages, and a HTTPHandler
1981 pointing to that server's address and port."""
1982 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001983 self.handled = threading.Event()
1984
Vinay Sajip7367d082011-05-02 13:17:27 +01001985 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001986 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001987 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001988 if self.command == 'POST':
1989 try:
1990 rlen = int(request.headers['Content-Length'])
1991 self.post_data = request.rfile.read(rlen)
1992 except:
1993 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001994 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001995 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001996 self.handled.set()
1997
1998 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001999 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01002000 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01002001 root_logger = self.root_logger
2002 root_logger.removeHandler(self.root_logger.handlers[0])
2003 for secure in (False, True):
2004 addr = ('localhost', 0)
2005 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01002006 try:
2007 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01002008 except ImportError:
2009 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002010 else:
2011 here = os.path.dirname(__file__)
2012 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02002013 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002014 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06002015
2016 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01002017 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01002018 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06002019 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01002020 self.server = server = TestHTTPServer(addr, self.handle_request,
2021 0.01, sslctx=sslctx)
2022 server.start()
2023 server.ready.wait()
2024 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01002025 secure_client = secure and sslctx
2026 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06002027 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01002028 context=context,
2029 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01002030 self.log_data = None
2031 root_logger.addHandler(self.h_hdlr)
2032
2033 for method in ('GET', 'POST'):
2034 self.h_hdlr.method = method
2035 self.handled.clear()
2036 msg = "sp\xe4m"
2037 logger.error(msg)
2038 self.handled.wait()
2039 self.assertEqual(self.log_data.path, '/frob')
2040 self.assertEqual(self.command, method)
2041 if method == 'GET':
2042 d = parse_qs(self.log_data.query)
2043 else:
2044 d = parse_qs(self.post_data.decode('utf-8'))
2045 self.assertEqual(d['name'], ['http'])
2046 self.assertEqual(d['funcName'], ['test_output'])
2047 self.assertEqual(d['msg'], [msg])
2048
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002049 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002050 self.root_logger.removeHandler(self.h_hdlr)
2051 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002052
Christian Heimes180510d2008-03-03 19:15:45 +00002053class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002054
Christian Heimes180510d2008-03-03 19:15:45 +00002055 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002056
Christian Heimes180510d2008-03-03 19:15:45 +00002057 def setUp(self):
2058 """Create a dict to remember potentially destroyed objects."""
2059 BaseTest.setUp(self)
2060 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002061
Christian Heimes180510d2008-03-03 19:15:45 +00002062 def _watch_for_survival(self, *args):
2063 """Watch the given objects for survival, by creating weakrefs to
2064 them."""
2065 for obj in args:
2066 key = id(obj), repr(obj)
2067 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002068
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002069 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002070 """Assert that all objects watched for survival have survived."""
2071 # Trigger cycle breaking.
2072 gc.collect()
2073 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002074 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002075 if ref() is None:
2076 dead.append(repr_)
2077 if dead:
2078 self.fail("%d objects should have survived "
2079 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002080
Christian Heimes180510d2008-03-03 19:15:45 +00002081 def test_persistent_loggers(self):
2082 # Logger objects are persistent and retain their configuration, even
2083 # if visible references are destroyed.
2084 self.root_logger.setLevel(logging.INFO)
2085 foo = logging.getLogger("foo")
2086 self._watch_for_survival(foo)
2087 foo.setLevel(logging.DEBUG)
2088 self.root_logger.debug(self.next_message())
2089 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002090 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002091 ('foo', 'DEBUG', '2'),
2092 ])
2093 del foo
2094 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002095 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002096 # foo has retained its settings.
2097 bar = logging.getLogger("foo")
2098 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002099 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002100 ('foo', 'DEBUG', '2'),
2101 ('foo', 'DEBUG', '3'),
2102 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002103
Benjamin Petersonf91df042009-02-13 02:50:59 +00002104
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002105class EncodingTest(BaseTest):
2106 def test_encoding_plain_file(self):
2107 # In Python 2.x, a plain file object is treated as having no encoding.
2108 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002109 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2110 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002111 # the non-ascii data we write to the log.
2112 data = "foo\x80"
2113 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002114 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002115 log.addHandler(handler)
2116 try:
2117 # write non-ascii data to the log.
2118 log.warning(data)
2119 finally:
2120 log.removeHandler(handler)
2121 handler.close()
2122 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002123 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002124 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002125 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002126 finally:
2127 f.close()
2128 finally:
2129 if os.path.isfile(fn):
2130 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002131
Benjamin Petersonf91df042009-02-13 02:50:59 +00002132 def test_encoding_cyrillic_unicode(self):
2133 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002134 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002135 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002136 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002137 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002138 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002139 stream = io.BytesIO()
2140 writer = writer_class(stream, 'strict')
2141 handler = logging.StreamHandler(writer)
2142 log.addHandler(handler)
2143 try:
2144 log.warning(message)
2145 finally:
2146 log.removeHandler(handler)
2147 handler.close()
2148 # check we wrote exactly those bytes, ignoring trailing \n etc
2149 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002150 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002151 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2152
2153
Georg Brandlf9734072008-12-07 15:30:06 +00002154class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002155
Georg Brandlf9734072008-12-07 15:30:06 +00002156 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002157 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002158 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002159 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002160 warnings.filterwarnings("always", category=UserWarning)
2161 stream = io.StringIO()
2162 h = logging.StreamHandler(stream)
2163 logger = logging.getLogger("py.warnings")
2164 logger.addHandler(h)
2165 warnings.warn("I'm warning you...")
2166 logger.removeHandler(h)
2167 s = stream.getvalue()
2168 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002169 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002170
Mike53f7a7c2017-12-14 14:04:53 +03002171 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002172 a_file = io.StringIO()
2173 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2174 a_file, "Dummy line")
2175 s = a_file.getvalue()
2176 a_file.close()
2177 self.assertEqual(s,
2178 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2179
2180 def test_warnings_no_handlers(self):
2181 with warnings.catch_warnings():
2182 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002183 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002184
2185 # confirm our assumption: no loggers are set
2186 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002187 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002188
2189 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002190 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002191 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002192
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002193
2194def formatFunc(format, datefmt=None):
2195 return logging.Formatter(format, datefmt)
2196
BNMetrics18fb1fb2018-10-15 19:41:36 +01002197class myCustomFormatter:
2198 def __init__(self, fmt, datefmt=None):
2199 pass
2200
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002201def handlerFunc():
2202 return logging.StreamHandler()
2203
2204class CustomHandler(logging.StreamHandler):
2205 pass
2206
2207class ConfigDictTest(BaseTest):
2208
2209 """Reading logging config from a dictionary."""
2210
Hai Shi3ddc6342020-06-30 21:46:06 +08002211 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002212 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002213
2214 # config0 is a standard configuration.
2215 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002216 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002217 'formatters': {
2218 'form1' : {
2219 'format' : '%(levelname)s ++ %(message)s',
2220 },
2221 },
2222 'handlers' : {
2223 'hand1' : {
2224 'class' : 'logging.StreamHandler',
2225 'formatter' : 'form1',
2226 'level' : 'NOTSET',
2227 'stream' : 'ext://sys.stdout',
2228 },
2229 },
2230 'root' : {
2231 'level' : 'WARNING',
2232 'handlers' : ['hand1'],
2233 },
2234 }
2235
2236 # config1 adds a little to the standard configuration.
2237 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002238 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002239 'formatters': {
2240 'form1' : {
2241 'format' : '%(levelname)s ++ %(message)s',
2242 },
2243 },
2244 'handlers' : {
2245 'hand1' : {
2246 'class' : 'logging.StreamHandler',
2247 'formatter' : 'form1',
2248 'level' : 'NOTSET',
2249 'stream' : 'ext://sys.stdout',
2250 },
2251 },
2252 'loggers' : {
2253 'compiler.parser' : {
2254 'level' : 'DEBUG',
2255 'handlers' : ['hand1'],
2256 },
2257 },
2258 'root' : {
2259 'level' : 'WARNING',
2260 },
2261 }
2262
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002263 # config1a moves the handler to the root. Used with config8a
2264 config1a = {
2265 'version': 1,
2266 'formatters': {
2267 'form1' : {
2268 'format' : '%(levelname)s ++ %(message)s',
2269 },
2270 },
2271 'handlers' : {
2272 'hand1' : {
2273 'class' : 'logging.StreamHandler',
2274 'formatter' : 'form1',
2275 'level' : 'NOTSET',
2276 'stream' : 'ext://sys.stdout',
2277 },
2278 },
2279 'loggers' : {
2280 'compiler.parser' : {
2281 'level' : 'DEBUG',
2282 },
2283 },
2284 'root' : {
2285 'level' : 'WARNING',
2286 'handlers' : ['hand1'],
2287 },
2288 }
2289
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002290 # config2 has a subtle configuration error that should be reported
2291 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002292 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002293 'formatters': {
2294 'form1' : {
2295 'format' : '%(levelname)s ++ %(message)s',
2296 },
2297 },
2298 'handlers' : {
2299 'hand1' : {
2300 'class' : 'logging.StreamHandler',
2301 'formatter' : 'form1',
2302 'level' : 'NOTSET',
2303 'stream' : 'ext://sys.stdbout',
2304 },
2305 },
2306 'loggers' : {
2307 'compiler.parser' : {
2308 'level' : 'DEBUG',
2309 'handlers' : ['hand1'],
2310 },
2311 },
2312 'root' : {
2313 'level' : 'WARNING',
2314 },
2315 }
2316
Mike53f7a7c2017-12-14 14:04:53 +03002317 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002318 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002319 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002320 'formatters': {
2321 'form1' : {
2322 'format' : '%(levelname)s ++ %(message)s',
2323 },
2324 },
2325 'handlers' : {
2326 'hand1' : {
2327 'class' : 'logging.StreamHandler',
2328 'formatter' : 'form1',
2329 'level' : 'NTOSET',
2330 'stream' : 'ext://sys.stdout',
2331 },
2332 },
2333 'loggers' : {
2334 'compiler.parser' : {
2335 'level' : 'DEBUG',
2336 'handlers' : ['hand1'],
2337 },
2338 },
2339 'root' : {
2340 'level' : 'WARNING',
2341 },
2342 }
2343
2344
Mike53f7a7c2017-12-14 14:04:53 +03002345 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002346 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002347 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002348 'formatters': {
2349 'form1' : {
2350 'format' : '%(levelname)s ++ %(message)s',
2351 },
2352 },
2353 'handlers' : {
2354 'hand1' : {
2355 'class' : 'logging.StreamHandler',
2356 'formatter' : 'form1',
2357 'level' : 'NOTSET',
2358 'stream' : 'ext://sys.stdout',
2359 },
2360 },
2361 'loggers' : {
2362 'compiler.parser' : {
2363 'level' : 'DEBUG',
2364 'handlers' : ['hand1'],
2365 },
2366 },
2367 'root' : {
2368 'level' : 'WRANING',
2369 },
2370 }
2371
2372 # config3 has a less subtle configuration error
2373 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002374 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002375 'formatters': {
2376 'form1' : {
2377 'format' : '%(levelname)s ++ %(message)s',
2378 },
2379 },
2380 'handlers' : {
2381 'hand1' : {
2382 'class' : 'logging.StreamHandler',
2383 'formatter' : 'misspelled_name',
2384 'level' : 'NOTSET',
2385 'stream' : 'ext://sys.stdout',
2386 },
2387 },
2388 'loggers' : {
2389 'compiler.parser' : {
2390 'level' : 'DEBUG',
2391 'handlers' : ['hand1'],
2392 },
2393 },
2394 'root' : {
2395 'level' : 'WARNING',
2396 },
2397 }
2398
2399 # config4 specifies a custom formatter class to be loaded
2400 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002401 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002402 'formatters': {
2403 'form1' : {
2404 '()' : __name__ + '.ExceptionFormatter',
2405 'format' : '%(levelname)s:%(name)s:%(message)s',
2406 },
2407 },
2408 'handlers' : {
2409 'hand1' : {
2410 'class' : 'logging.StreamHandler',
2411 'formatter' : 'form1',
2412 'level' : 'NOTSET',
2413 'stream' : 'ext://sys.stdout',
2414 },
2415 },
2416 'root' : {
2417 'level' : 'NOTSET',
2418 'handlers' : ['hand1'],
2419 },
2420 }
2421
2422 # As config4 but using an actual callable rather than a string
2423 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002424 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002425 'formatters': {
2426 'form1' : {
2427 '()' : ExceptionFormatter,
2428 'format' : '%(levelname)s:%(name)s:%(message)s',
2429 },
2430 'form2' : {
2431 '()' : __name__ + '.formatFunc',
2432 'format' : '%(levelname)s:%(name)s:%(message)s',
2433 },
2434 'form3' : {
2435 '()' : formatFunc,
2436 'format' : '%(levelname)s:%(name)s:%(message)s',
2437 },
2438 },
2439 'handlers' : {
2440 'hand1' : {
2441 'class' : 'logging.StreamHandler',
2442 'formatter' : 'form1',
2443 'level' : 'NOTSET',
2444 'stream' : 'ext://sys.stdout',
2445 },
2446 'hand2' : {
2447 '()' : handlerFunc,
2448 },
2449 },
2450 'root' : {
2451 'level' : 'NOTSET',
2452 'handlers' : ['hand1'],
2453 },
2454 }
2455
2456 # config5 specifies a custom handler class to be loaded
2457 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002458 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002459 'formatters': {
2460 'form1' : {
2461 'format' : '%(levelname)s ++ %(message)s',
2462 },
2463 },
2464 'handlers' : {
2465 'hand1' : {
2466 'class' : __name__ + '.CustomHandler',
2467 'formatter' : 'form1',
2468 'level' : 'NOTSET',
2469 'stream' : 'ext://sys.stdout',
2470 },
2471 },
2472 'loggers' : {
2473 'compiler.parser' : {
2474 'level' : 'DEBUG',
2475 'handlers' : ['hand1'],
2476 },
2477 },
2478 'root' : {
2479 'level' : 'WARNING',
2480 },
2481 }
2482
2483 # config6 specifies a custom handler class to be loaded
2484 # but has bad arguments
2485 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002486 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002487 'formatters': {
2488 'form1' : {
2489 'format' : '%(levelname)s ++ %(message)s',
2490 },
2491 },
2492 'handlers' : {
2493 'hand1' : {
2494 'class' : __name__ + '.CustomHandler',
2495 'formatter' : 'form1',
2496 'level' : 'NOTSET',
2497 'stream' : 'ext://sys.stdout',
2498 '9' : 'invalid parameter name',
2499 },
2500 },
2501 'loggers' : {
2502 'compiler.parser' : {
2503 'level' : 'DEBUG',
2504 'handlers' : ['hand1'],
2505 },
2506 },
2507 'root' : {
2508 'level' : 'WARNING',
2509 },
2510 }
2511
Mike53f7a7c2017-12-14 14:04:53 +03002512 # config 7 does not define compiler.parser but defines compiler.lexer
2513 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002514 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002515 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002516 'formatters': {
2517 'form1' : {
2518 'format' : '%(levelname)s ++ %(message)s',
2519 },
2520 },
2521 'handlers' : {
2522 'hand1' : {
2523 'class' : 'logging.StreamHandler',
2524 'formatter' : 'form1',
2525 'level' : 'NOTSET',
2526 'stream' : 'ext://sys.stdout',
2527 },
2528 },
2529 'loggers' : {
2530 'compiler.lexer' : {
2531 'level' : 'DEBUG',
2532 'handlers' : ['hand1'],
2533 },
2534 },
2535 'root' : {
2536 'level' : 'WARNING',
2537 },
2538 }
2539
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002540 # config8 defines both compiler and compiler.lexer
2541 # so compiler.parser should not be disabled (since
2542 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002543 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002544 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002545 'disable_existing_loggers' : False,
2546 'formatters': {
2547 'form1' : {
2548 'format' : '%(levelname)s ++ %(message)s',
2549 },
2550 },
2551 'handlers' : {
2552 'hand1' : {
2553 'class' : 'logging.StreamHandler',
2554 'formatter' : 'form1',
2555 'level' : 'NOTSET',
2556 'stream' : 'ext://sys.stdout',
2557 },
2558 },
2559 'loggers' : {
2560 'compiler' : {
2561 'level' : 'DEBUG',
2562 'handlers' : ['hand1'],
2563 },
2564 'compiler.lexer' : {
2565 },
2566 },
2567 'root' : {
2568 'level' : 'WARNING',
2569 },
2570 }
2571
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002572 # config8a disables existing loggers
2573 config8a = {
2574 'version': 1,
2575 'disable_existing_loggers' : True,
2576 'formatters': {
2577 'form1' : {
2578 'format' : '%(levelname)s ++ %(message)s',
2579 },
2580 },
2581 'handlers' : {
2582 'hand1' : {
2583 'class' : 'logging.StreamHandler',
2584 'formatter' : 'form1',
2585 'level' : 'NOTSET',
2586 'stream' : 'ext://sys.stdout',
2587 },
2588 },
2589 'loggers' : {
2590 'compiler' : {
2591 'level' : 'DEBUG',
2592 'handlers' : ['hand1'],
2593 },
2594 'compiler.lexer' : {
2595 },
2596 },
2597 'root' : {
2598 'level' : 'WARNING',
2599 },
2600 }
2601
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002602 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002603 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002604 'formatters': {
2605 'form1' : {
2606 'format' : '%(levelname)s ++ %(message)s',
2607 },
2608 },
2609 'handlers' : {
2610 'hand1' : {
2611 'class' : 'logging.StreamHandler',
2612 'formatter' : 'form1',
2613 'level' : 'WARNING',
2614 'stream' : 'ext://sys.stdout',
2615 },
2616 },
2617 'loggers' : {
2618 'compiler.parser' : {
2619 'level' : 'WARNING',
2620 'handlers' : ['hand1'],
2621 },
2622 },
2623 'root' : {
2624 'level' : 'NOTSET',
2625 },
2626 }
2627
2628 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002629 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002630 'incremental' : True,
2631 'handlers' : {
2632 'hand1' : {
2633 'level' : 'WARNING',
2634 },
2635 },
2636 'loggers' : {
2637 'compiler.parser' : {
2638 'level' : 'INFO',
2639 },
2640 },
2641 }
2642
2643 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002644 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002645 'incremental' : True,
2646 'handlers' : {
2647 'hand1' : {
2648 'level' : 'INFO',
2649 },
2650 },
2651 'loggers' : {
2652 'compiler.parser' : {
2653 'level' : 'INFO',
2654 },
2655 },
2656 }
2657
Mike53f7a7c2017-12-14 14:04:53 +03002658 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002659 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002660 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002661 'formatters': {
2662 'form1' : {
2663 'format' : '%(levelname)s ++ %(message)s',
2664 },
2665 },
2666 'filters' : {
2667 'filt1' : {
2668 'name' : 'compiler.parser',
2669 },
2670 },
2671 'handlers' : {
2672 'hand1' : {
2673 'class' : 'logging.StreamHandler',
2674 'formatter' : 'form1',
2675 'level' : 'NOTSET',
2676 'stream' : 'ext://sys.stdout',
2677 'filters' : ['filt1'],
2678 },
2679 },
2680 'loggers' : {
2681 'compiler.parser' : {
2682 'level' : 'DEBUG',
2683 'filters' : ['filt1'],
2684 },
2685 },
2686 'root' : {
2687 'level' : 'WARNING',
2688 'handlers' : ['hand1'],
2689 },
2690 }
2691
Mike53f7a7c2017-12-14 14:04:53 +03002692 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002693 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002694 'version': 1,
2695 'true_formatters': {
2696 'form1' : {
2697 'format' : '%(levelname)s ++ %(message)s',
2698 },
2699 },
2700 'handler_configs': {
2701 'hand1' : {
2702 'class' : 'logging.StreamHandler',
2703 'formatter' : 'form1',
2704 'level' : 'NOTSET',
2705 'stream' : 'ext://sys.stdout',
2706 },
2707 },
2708 'formatters' : 'cfg://true_formatters',
2709 'handlers' : {
2710 'hand1' : 'cfg://handler_configs[hand1]',
2711 },
2712 'loggers' : {
2713 'compiler.parser' : {
2714 'level' : 'DEBUG',
2715 'handlers' : ['hand1'],
2716 },
2717 },
2718 'root' : {
2719 'level' : 'WARNING',
2720 },
2721 }
2722
Mike53f7a7c2017-12-14 14:04:53 +03002723 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002724 config12 = {
2725 'true_formatters': {
2726 'form1' : {
2727 'format' : '%(levelname)s ++ %(message)s',
2728 },
2729 },
2730 'handler_configs': {
2731 'hand1' : {
2732 'class' : 'logging.StreamHandler',
2733 'formatter' : 'form1',
2734 'level' : 'NOTSET',
2735 'stream' : 'ext://sys.stdout',
2736 },
2737 },
2738 'formatters' : 'cfg://true_formatters',
2739 'handlers' : {
2740 'hand1' : 'cfg://handler_configs[hand1]',
2741 },
2742 'loggers' : {
2743 'compiler.parser' : {
2744 'level' : 'DEBUG',
2745 'handlers' : ['hand1'],
2746 },
2747 },
2748 'root' : {
2749 'level' : 'WARNING',
2750 },
2751 }
2752
Mike53f7a7c2017-12-14 14:04:53 +03002753 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002754 config13 = {
2755 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002756 'true_formatters': {
2757 'form1' : {
2758 'format' : '%(levelname)s ++ %(message)s',
2759 },
2760 },
2761 'handler_configs': {
2762 'hand1' : {
2763 'class' : 'logging.StreamHandler',
2764 'formatter' : 'form1',
2765 'level' : 'NOTSET',
2766 'stream' : 'ext://sys.stdout',
2767 },
2768 },
2769 'formatters' : 'cfg://true_formatters',
2770 'handlers' : {
2771 'hand1' : 'cfg://handler_configs[hand1]',
2772 },
2773 'loggers' : {
2774 'compiler.parser' : {
2775 'level' : 'DEBUG',
2776 'handlers' : ['hand1'],
2777 },
2778 },
2779 'root' : {
2780 'level' : 'WARNING',
2781 },
2782 }
2783
Vinay Sajip8d270232012-11-15 14:20:18 +00002784 # As config0, but with properties
2785 config14 = {
2786 'version': 1,
2787 'formatters': {
2788 'form1' : {
2789 'format' : '%(levelname)s ++ %(message)s',
2790 },
2791 },
2792 'handlers' : {
2793 'hand1' : {
2794 'class' : 'logging.StreamHandler',
2795 'formatter' : 'form1',
2796 'level' : 'NOTSET',
2797 'stream' : 'ext://sys.stdout',
2798 '.': {
2799 'foo': 'bar',
2800 'terminator': '!\n',
2801 }
2802 },
2803 },
2804 'root' : {
2805 'level' : 'WARNING',
2806 'handlers' : ['hand1'],
2807 },
2808 }
2809
Vinay Sajip3f885b52013-03-22 15:19:54 +00002810 out_of_order = {
2811 "version": 1,
2812 "formatters": {
2813 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002814 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2815 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002816 }
2817 },
2818 "handlers": {
2819 "fileGlobal": {
2820 "class": "logging.StreamHandler",
2821 "level": "DEBUG",
2822 "formatter": "mySimpleFormatter"
2823 },
2824 "bufferGlobal": {
2825 "class": "logging.handlers.MemoryHandler",
2826 "capacity": 5,
2827 "formatter": "mySimpleFormatter",
2828 "target": "fileGlobal",
2829 "level": "DEBUG"
2830 }
2831 },
2832 "loggers": {
2833 "mymodule": {
2834 "level": "DEBUG",
2835 "handlers": ["bufferGlobal"],
2836 "propagate": "true"
2837 }
2838 }
2839 }
2840
BNMetrics18fb1fb2018-10-15 19:41:36 +01002841 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2842 custom_formatter_class_validate = {
2843 'version': 1,
2844 'formatters': {
2845 'form1': {
2846 '()': __name__ + '.ExceptionFormatter',
2847 'format': '%(levelname)s:%(name)s:%(message)s',
2848 'validate': False,
2849 },
2850 },
2851 'handlers' : {
2852 'hand1' : {
2853 'class': 'logging.StreamHandler',
2854 'formatter': 'form1',
2855 'level': 'NOTSET',
2856 'stream': 'ext://sys.stdout',
2857 },
2858 },
2859 "loggers": {
2860 "my_test_logger_custom_formatter": {
2861 "level": "DEBUG",
2862 "handlers": ["hand1"],
2863 "propagate": "true"
2864 }
2865 }
2866 }
2867
2868 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2869 custom_formatter_class_validate2 = {
2870 'version': 1,
2871 'formatters': {
2872 'form1': {
2873 'class': __name__ + '.ExceptionFormatter',
2874 'format': '%(levelname)s:%(name)s:%(message)s',
2875 'validate': False,
2876 },
2877 },
2878 'handlers' : {
2879 'hand1' : {
2880 'class': 'logging.StreamHandler',
2881 'formatter': 'form1',
2882 'level': 'NOTSET',
2883 'stream': 'ext://sys.stdout',
2884 },
2885 },
2886 "loggers": {
2887 "my_test_logger_custom_formatter": {
2888 "level": "DEBUG",
2889 "handlers": ["hand1"],
2890 "propagate": "true"
2891 }
2892 }
2893 }
2894
2895 # Configuration with custom class that is not inherited from logging.Formatter
2896 custom_formatter_class_validate3 = {
2897 'version': 1,
2898 'formatters': {
2899 'form1': {
2900 'class': __name__ + '.myCustomFormatter',
2901 'format': '%(levelname)s:%(name)s:%(message)s',
2902 'validate': False,
2903 },
2904 },
2905 'handlers' : {
2906 'hand1' : {
2907 'class': 'logging.StreamHandler',
2908 'formatter': 'form1',
2909 'level': 'NOTSET',
2910 'stream': 'ext://sys.stdout',
2911 },
2912 },
2913 "loggers": {
2914 "my_test_logger_custom_formatter": {
2915 "level": "DEBUG",
2916 "handlers": ["hand1"],
2917 "propagate": "true"
2918 }
2919 }
2920 }
2921
2922 # Configuration with custom function and 'validate' set to False
2923 custom_formatter_with_function = {
2924 'version': 1,
2925 'formatters': {
2926 'form1': {
2927 '()': formatFunc,
2928 'format': '%(levelname)s:%(name)s:%(message)s',
2929 'validate': False,
2930 },
2931 },
2932 'handlers' : {
2933 'hand1' : {
2934 'class': 'logging.StreamHandler',
2935 'formatter': 'form1',
2936 'level': 'NOTSET',
2937 'stream': 'ext://sys.stdout',
2938 },
2939 },
2940 "loggers": {
2941 "my_test_logger_custom_formatter": {
2942 "level": "DEBUG",
2943 "handlers": ["hand1"],
2944 "propagate": "true"
2945 }
2946 }
2947 }
2948
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002949 def apply_config(self, conf):
2950 logging.config.dictConfig(conf)
2951
2952 def test_config0_ok(self):
2953 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002954 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002955 self.apply_config(self.config0)
2956 logger = logging.getLogger()
2957 # Won't output anything
2958 logger.info(self.next_message())
2959 # Outputs a message
2960 logger.error(self.next_message())
2961 self.assert_log_lines([
2962 ('ERROR', '2'),
2963 ], stream=output)
2964 # Original logger output is empty.
2965 self.assert_log_lines([])
2966
2967 def test_config1_ok(self, config=config1):
2968 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002969 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002970 self.apply_config(config)
2971 logger = logging.getLogger("compiler.parser")
2972 # Both will output a message
2973 logger.info(self.next_message())
2974 logger.error(self.next_message())
2975 self.assert_log_lines([
2976 ('INFO', '1'),
2977 ('ERROR', '2'),
2978 ], stream=output)
2979 # Original logger output is empty.
2980 self.assert_log_lines([])
2981
2982 def test_config2_failure(self):
2983 # A simple config which overrides the default settings.
2984 self.assertRaises(Exception, self.apply_config, self.config2)
2985
2986 def test_config2a_failure(self):
2987 # A simple config which overrides the default settings.
2988 self.assertRaises(Exception, self.apply_config, self.config2a)
2989
2990 def test_config2b_failure(self):
2991 # A simple config which overrides the default settings.
2992 self.assertRaises(Exception, self.apply_config, self.config2b)
2993
2994 def test_config3_failure(self):
2995 # A simple config which overrides the default settings.
2996 self.assertRaises(Exception, self.apply_config, self.config3)
2997
2998 def test_config4_ok(self):
2999 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003000 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003001 self.apply_config(self.config4)
3002 #logger = logging.getLogger()
3003 try:
3004 raise RuntimeError()
3005 except RuntimeError:
3006 logging.exception("just testing")
3007 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003008 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003009 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3010 # Original logger output is empty
3011 self.assert_log_lines([])
3012
3013 def test_config4a_ok(self):
3014 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003015 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003016 self.apply_config(self.config4a)
3017 #logger = logging.getLogger()
3018 try:
3019 raise RuntimeError()
3020 except RuntimeError:
3021 logging.exception("just testing")
3022 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003023 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003024 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3025 # Original logger output is empty
3026 self.assert_log_lines([])
3027
3028 def test_config5_ok(self):
3029 self.test_config1_ok(config=self.config5)
3030
3031 def test_config6_failure(self):
3032 self.assertRaises(Exception, self.apply_config, self.config6)
3033
3034 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003035 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003036 self.apply_config(self.config1)
3037 logger = logging.getLogger("compiler.parser")
3038 # Both will output a message
3039 logger.info(self.next_message())
3040 logger.error(self.next_message())
3041 self.assert_log_lines([
3042 ('INFO', '1'),
3043 ('ERROR', '2'),
3044 ], stream=output)
3045 # Original logger output is empty.
3046 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003047 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003048 self.apply_config(self.config7)
3049 logger = logging.getLogger("compiler.parser")
3050 self.assertTrue(logger.disabled)
3051 logger = logging.getLogger("compiler.lexer")
3052 # Both will output a message
3053 logger.info(self.next_message())
3054 logger.error(self.next_message())
3055 self.assert_log_lines([
3056 ('INFO', '3'),
3057 ('ERROR', '4'),
3058 ], stream=output)
3059 # Original logger output is empty.
3060 self.assert_log_lines([])
3061
Mike53f7a7c2017-12-14 14:04:53 +03003062 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003063 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003064 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003065 self.apply_config(self.config1)
3066 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003067 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003068 logger.info(self.next_message())
3069 logger.error(self.next_message())
3070 self.assert_log_lines([
3071 ('INFO', '1'),
3072 ('ERROR', '2'),
3073 ], stream=output)
3074 # Original logger output is empty.
3075 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003076 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003077 self.apply_config(self.config8)
3078 logger = logging.getLogger("compiler.parser")
3079 self.assertFalse(logger.disabled)
3080 # Both will output a message
3081 logger.info(self.next_message())
3082 logger.error(self.next_message())
3083 logger = logging.getLogger("compiler.lexer")
3084 # Both will output a message
3085 logger.info(self.next_message())
3086 logger.error(self.next_message())
3087 self.assert_log_lines([
3088 ('INFO', '3'),
3089 ('ERROR', '4'),
3090 ('INFO', '5'),
3091 ('ERROR', '6'),
3092 ], stream=output)
3093 # Original logger output is empty.
3094 self.assert_log_lines([])
3095
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003096 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003097 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003098 self.apply_config(self.config1a)
3099 logger = logging.getLogger("compiler.parser")
3100 # See issue #11424. compiler-hyphenated sorts
3101 # between compiler and compiler.xyz and this
3102 # was preventing compiler.xyz from being included
3103 # in the child loggers of compiler because of an
3104 # overzealous loop termination condition.
3105 hyphenated = logging.getLogger('compiler-hyphenated')
3106 # All will output a message
3107 logger.info(self.next_message())
3108 logger.error(self.next_message())
3109 hyphenated.critical(self.next_message())
3110 self.assert_log_lines([
3111 ('INFO', '1'),
3112 ('ERROR', '2'),
3113 ('CRITICAL', '3'),
3114 ], stream=output)
3115 # Original logger output is empty.
3116 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003117 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003118 self.apply_config(self.config8a)
3119 logger = logging.getLogger("compiler.parser")
3120 self.assertFalse(logger.disabled)
3121 # Both will output a message
3122 logger.info(self.next_message())
3123 logger.error(self.next_message())
3124 logger = logging.getLogger("compiler.lexer")
3125 # Both will output a message
3126 logger.info(self.next_message())
3127 logger.error(self.next_message())
3128 # Will not appear
3129 hyphenated.critical(self.next_message())
3130 self.assert_log_lines([
3131 ('INFO', '4'),
3132 ('ERROR', '5'),
3133 ('INFO', '6'),
3134 ('ERROR', '7'),
3135 ], stream=output)
3136 # Original logger output is empty.
3137 self.assert_log_lines([])
3138
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003139 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003140 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003141 self.apply_config(self.config9)
3142 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003143 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003144 logger.info(self.next_message())
3145 self.assert_log_lines([], stream=output)
3146 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003147 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003148 logger.info(self.next_message())
3149 self.assert_log_lines([], stream=output)
3150 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003151 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003152 logger.info(self.next_message())
3153 self.assert_log_lines([
3154 ('INFO', '3'),
3155 ], stream=output)
3156
3157 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003158 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003159 self.apply_config(self.config10)
3160 logger = logging.getLogger("compiler.parser")
3161 logger.warning(self.next_message())
3162 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003163 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003164 logger.warning(self.next_message())
3165 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003166 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003167 logger.warning(self.next_message())
3168 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003169 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003170 logger.error(self.next_message())
3171 self.assert_log_lines([
3172 ('WARNING', '1'),
3173 ('ERROR', '4'),
3174 ], stream=output)
3175
3176 def test_config11_ok(self):
3177 self.test_config1_ok(self.config11)
3178
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003179 def test_config12_failure(self):
3180 self.assertRaises(Exception, self.apply_config, self.config12)
3181
3182 def test_config13_failure(self):
3183 self.assertRaises(Exception, self.apply_config, self.config13)
3184
Vinay Sajip8d270232012-11-15 14:20:18 +00003185 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003186 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003187 self.apply_config(self.config14)
3188 h = logging._handlers['hand1']
3189 self.assertEqual(h.foo, 'bar')
3190 self.assertEqual(h.terminator, '!\n')
3191 logging.warning('Exclamation')
3192 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3193
Xtreak087570a2018-07-02 14:27:46 +05303194 def test_config15_ok(self):
3195
3196 def cleanup(h1, fn):
3197 h1.close()
3198 os.remove(fn)
3199
3200 with self.check_no_resource_warning():
3201 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3202 os.close(fd)
3203
3204 config = {
3205 "version": 1,
3206 "handlers": {
3207 "file": {
3208 "class": "logging.FileHandler",
Inada Naokifa51c0c2021-04-29 11:34:56 +09003209 "filename": fn,
3210 "encoding": "utf-8",
Xtreak087570a2018-07-02 14:27:46 +05303211 }
3212 },
3213 "root": {
3214 "handlers": ["file"]
3215 }
3216 }
3217
3218 self.apply_config(config)
3219 self.apply_config(config)
3220
3221 handler = logging.root.handlers[0]
3222 self.addCleanup(cleanup, handler, fn)
3223
Vinay Sajip4ded5512012-10-02 15:56:16 +01003224 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003225 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003226 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003227 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003228 t.start()
3229 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003230 # Now get the port allocated
3231 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003232 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003233 try:
3234 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3235 sock.settimeout(2.0)
3236 sock.connect(('localhost', port))
3237
3238 slen = struct.pack('>L', len(text))
3239 s = slen + text
3240 sentsofar = 0
3241 left = len(s)
3242 while left > 0:
3243 sent = sock.send(s[sentsofar:])
3244 sentsofar += sent
3245 left -= sent
3246 sock.close()
3247 finally:
3248 t.ready.wait(2.0)
3249 logging.config.stopListening()
Hai Shie80697d2020-05-28 06:10:27 +08003250 threading_helper.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003251
3252 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003253 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003254 self.setup_via_listener(json.dumps(self.config10))
3255 logger = logging.getLogger("compiler.parser")
3256 logger.warning(self.next_message())
3257 logger = logging.getLogger('compiler')
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.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003261 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003262 logger.warning(self.next_message())
3263 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003264 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003265 logger.error(self.next_message())
3266 self.assert_log_lines([
3267 ('WARNING', '1'),
3268 ('ERROR', '4'),
3269 ], stream=output)
3270
3271 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003272 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003273 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3274 logger = logging.getLogger("compiler.parser")
3275 # Both will output a message
3276 logger.info(self.next_message())
3277 logger.error(self.next_message())
3278 self.assert_log_lines([
3279 ('INFO', '1'),
3280 ('ERROR', '2'),
3281 ], stream=output)
3282 # Original logger output is empty.
3283 self.assert_log_lines([])
3284
Vinay Sajip4ded5512012-10-02 15:56:16 +01003285 def test_listen_verify(self):
3286
3287 def verify_fail(stuff):
3288 return None
3289
3290 def verify_reverse(stuff):
3291 return stuff[::-1]
3292
3293 logger = logging.getLogger("compiler.parser")
3294 to_send = textwrap.dedent(ConfigFileTest.config1)
3295 # First, specify a verification function that will fail.
3296 # We expect to see no output, since our configuration
3297 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003298 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003299 self.setup_via_listener(to_send, verify_fail)
3300 # Both will output a message
3301 logger.info(self.next_message())
3302 logger.error(self.next_message())
3303 self.assert_log_lines([], stream=output)
3304 # Original logger output has the stuff we logged.
3305 self.assert_log_lines([
3306 ('INFO', '1'),
3307 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003308 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003309
3310 # Now, perform no verification. Our configuration
3311 # should take effect.
3312
Vinay Sajip1feedb42014-05-31 08:19:12 +01003313 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003314 self.setup_via_listener(to_send) # no verify callable specified
3315 logger = logging.getLogger("compiler.parser")
3316 # Both will output a message
3317 logger.info(self.next_message())
3318 logger.error(self.next_message())
3319 self.assert_log_lines([
3320 ('INFO', '3'),
3321 ('ERROR', '4'),
3322 ], stream=output)
3323 # Original logger output still has the stuff we logged before.
3324 self.assert_log_lines([
3325 ('INFO', '1'),
3326 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003327 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003328
3329 # Now, perform verification which transforms the bytes.
3330
Vinay Sajip1feedb42014-05-31 08:19:12 +01003331 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003332 self.setup_via_listener(to_send[::-1], verify_reverse)
3333 logger = logging.getLogger("compiler.parser")
3334 # Both will output a message
3335 logger.info(self.next_message())
3336 logger.error(self.next_message())
3337 self.assert_log_lines([
3338 ('INFO', '5'),
3339 ('ERROR', '6'),
3340 ], stream=output)
3341 # Original logger output still has the stuff we logged before.
3342 self.assert_log_lines([
3343 ('INFO', '1'),
3344 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003345 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003346
Vinay Sajip3f885b52013-03-22 15:19:54 +00003347 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003348 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3349
3350 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003351 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003352 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3353
3354 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003355 handler = logging.getLogger('mymodule').handlers[0]
3356 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003357 self.assertIsInstance(handler.formatter._style,
3358 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003359
BNMetrics18fb1fb2018-10-15 19:41:36 +01003360 def test_custom_formatter_class_with_validate(self):
3361 self.apply_config(self.custom_formatter_class_validate)
3362 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3363 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3364
3365 def test_custom_formatter_class_with_validate2(self):
3366 self.apply_config(self.custom_formatter_class_validate2)
3367 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3368 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3369
3370 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3371 config = self.custom_formatter_class_validate.copy()
3372 config['formatters']['form1']['style'] = "$"
3373
3374 # Exception should not be raise as we have configured 'validate' to False
3375 self.apply_config(config)
3376 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3377 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3378
3379 def test_custom_formatter_class_with_validate3(self):
3380 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3381
3382 def test_custom_formatter_function_with_validate(self):
3383 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3384
Vinay Sajip373baef2011-04-26 20:05:24 +01003385 def test_baseconfig(self):
3386 d = {
3387 'atuple': (1, 2, 3),
3388 'alist': ['a', 'b', 'c'],
3389 'adict': {'d': 'e', 'f': 3 },
3390 'nest1': ('g', ('h', 'i'), 'j'),
3391 'nest2': ['k', ['l', 'm'], 'n'],
3392 'nest3': ['o', 'cfg://alist', 'p'],
3393 }
3394 bc = logging.config.BaseConfigurator(d)
3395 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3396 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3397 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3398 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3399 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3400 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3401 v = bc.convert('cfg://nest3')
3402 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3403 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3404 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3405 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003406
Vinay Sajip46abfc12020-01-01 19:32:11 +00003407 def test_namedtuple(self):
3408 # see bpo-39142
3409 from collections import namedtuple
3410
3411 class MyHandler(logging.StreamHandler):
3412 def __init__(self, resource, *args, **kwargs):
3413 super().__init__(*args, **kwargs)
3414 self.resource: namedtuple = resource
3415
3416 def emit(self, record):
3417 record.msg += f' {self.resource.type}'
3418 return super().emit(record)
3419
3420 Resource = namedtuple('Resource', ['type', 'labels'])
3421 resource = Resource(type='my_type', labels=['a'])
3422
3423 config = {
3424 'version': 1,
3425 'handlers': {
3426 'myhandler': {
3427 '()': MyHandler,
3428 'resource': resource
3429 }
3430 },
3431 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3432 }
3433 with support.captured_stderr() as stderr:
3434 self.apply_config(config)
3435 logging.info('some log')
3436 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3437
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003438class ManagerTest(BaseTest):
3439 def test_manager_loggerclass(self):
3440 logged = []
3441
3442 class MyLogger(logging.Logger):
3443 def _log(self, level, msg, args, exc_info=None, extra=None):
3444 logged.append(msg)
3445
3446 man = logging.Manager(None)
3447 self.assertRaises(TypeError, man.setLoggerClass, int)
3448 man.setLoggerClass(MyLogger)
3449 logger = man.getLogger('test')
3450 logger.warning('should appear in logged')
3451 logging.warning('should not appear in logged')
3452
3453 self.assertEqual(logged, ['should appear in logged'])
3454
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003455 def test_set_log_record_factory(self):
3456 man = logging.Manager(None)
3457 expected = object()
3458 man.setLogRecordFactory(expected)
3459 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003460
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003461class ChildLoggerTest(BaseTest):
3462 def test_child_loggers(self):
3463 r = logging.getLogger()
3464 l1 = logging.getLogger('abc')
3465 l2 = logging.getLogger('def.ghi')
3466 c1 = r.getChild('xyz')
3467 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003468 self.assertIs(c1, logging.getLogger('xyz'))
3469 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003470 c1 = l1.getChild('def')
3471 c2 = c1.getChild('ghi')
3472 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003473 self.assertIs(c1, logging.getLogger('abc.def'))
3474 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3475 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003476
3477
Vinay Sajip6fac8172010-10-19 20:44:14 +00003478class DerivedLogRecord(logging.LogRecord):
3479 pass
3480
Vinay Sajip61561522010-12-03 11:50:38 +00003481class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003482
3483 def setUp(self):
3484 class CheckingFilter(logging.Filter):
3485 def __init__(self, cls):
3486 self.cls = cls
3487
3488 def filter(self, record):
3489 t = type(record)
3490 if t is not self.cls:
3491 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3492 self.cls)
3493 raise TypeError(msg)
3494 return True
3495
3496 BaseTest.setUp(self)
3497 self.filter = CheckingFilter(DerivedLogRecord)
3498 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003499 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003500
3501 def tearDown(self):
3502 self.root_logger.removeFilter(self.filter)
3503 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003504 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003505
3506 def test_logrecord_class(self):
3507 self.assertRaises(TypeError, self.root_logger.warning,
3508 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003509 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003510 self.root_logger.error(self.next_message())
3511 self.assert_log_lines([
3512 ('root', 'ERROR', '2'),
3513 ])
3514
3515
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003516class QueueHandlerTest(BaseTest):
3517 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003518 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003519
3520 def setUp(self):
3521 BaseTest.setUp(self)
3522 self.queue = queue.Queue(-1)
3523 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003524 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003525 self.que_logger = logging.getLogger('que')
3526 self.que_logger.propagate = False
3527 self.que_logger.setLevel(logging.WARNING)
3528 self.que_logger.addHandler(self.que_hdlr)
3529
3530 def tearDown(self):
3531 self.que_hdlr.close()
3532 BaseTest.tearDown(self)
3533
3534 def test_queue_handler(self):
3535 self.que_logger.debug(self.next_message())
3536 self.assertRaises(queue.Empty, self.queue.get_nowait)
3537 self.que_logger.info(self.next_message())
3538 self.assertRaises(queue.Empty, self.queue.get_nowait)
3539 msg = self.next_message()
3540 self.que_logger.warning(msg)
3541 data = self.queue.get_nowait()
3542 self.assertTrue(isinstance(data, logging.LogRecord))
3543 self.assertEqual(data.name, self.que_logger.name)
3544 self.assertEqual((data.msg, data.args), (msg, None))
3545
favlladfe3442017-08-01 20:12:26 +02003546 def test_formatting(self):
3547 msg = self.next_message()
3548 levelname = logging.getLevelName(logging.WARNING)
3549 log_format_str = '{name} -> {levelname}: {message}'
3550 formatted_msg = log_format_str.format(name=self.name,
3551 levelname=levelname, message=msg)
3552 formatter = logging.Formatter(self.log_format)
3553 self.que_hdlr.setFormatter(formatter)
3554 self.que_logger.warning(msg)
3555 log_record = self.queue.get_nowait()
3556 self.assertEqual(formatted_msg, log_record.msg)
3557 self.assertEqual(formatted_msg, log_record.message)
3558
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003559 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3560 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003561 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003562 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003563 listener = logging.handlers.QueueListener(self.queue, handler)
3564 listener.start()
3565 try:
3566 self.que_logger.warning(self.next_message())
3567 self.que_logger.error(self.next_message())
3568 self.que_logger.critical(self.next_message())
3569 finally:
3570 listener.stop()
3571 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3572 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3573 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003574 handler.close()
3575
3576 # Now test with respect_handler_level set
3577
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003578 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003579 handler.setLevel(logging.CRITICAL)
3580 listener = logging.handlers.QueueListener(self.queue, handler,
3581 respect_handler_level=True)
3582 listener.start()
3583 try:
3584 self.que_logger.warning(self.next_message())
3585 self.que_logger.error(self.next_message())
3586 self.que_logger.critical(self.next_message())
3587 finally:
3588 listener.stop()
3589 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3590 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3591 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003592 handler.close()
3593
3594 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3595 'logging.handlers.QueueListener required for this test')
3596 def test_queue_listener_with_StreamHandler(self):
3597 # Test that traceback only appends once (bpo-34334).
3598 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3599 listener.start()
3600 try:
3601 1 / 0
3602 except ZeroDivisionError as e:
3603 exc = e
3604 self.que_logger.exception(self.next_message(), exc_info=exc)
3605 listener.stop()
3606 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003607
Xtreak2dad9602019-04-07 13:21:27 +05303608 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3609 'logging.handlers.QueueListener required for this test')
3610 def test_queue_listener_with_multiple_handlers(self):
3611 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3612 self.que_hdlr.setFormatter(self.root_formatter)
3613 self.que_logger.addHandler(self.root_hdlr)
3614
3615 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3616 listener.start()
3617 self.que_logger.error("error")
3618 listener.stop()
3619 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3620
Vinay Sajipd61910c2016-09-08 01:13:39 +01003621if hasattr(logging.handlers, 'QueueListener'):
3622 import multiprocessing
3623 from unittest.mock import patch
3624
3625 class QueueListenerTest(BaseTest):
3626 """
3627 Tests based on patch submitted for issue #27930. Ensure that
3628 QueueListener handles all log messages.
3629 """
3630
3631 repeat = 20
3632
3633 @staticmethod
3634 def setup_and_log(log_queue, ident):
3635 """
3636 Creates a logger with a QueueHandler that logs to a queue read by a
3637 QueueListener. Starts the listener, logs five messages, and stops
3638 the listener.
3639 """
3640 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3641 logger.setLevel(logging.DEBUG)
3642 handler = logging.handlers.QueueHandler(log_queue)
3643 logger.addHandler(handler)
3644 listener = logging.handlers.QueueListener(log_queue)
3645 listener.start()
3646
3647 logger.info('one')
3648 logger.info('two')
3649 logger.info('three')
3650 logger.info('four')
3651 logger.info('five')
3652
3653 listener.stop()
3654 logger.removeHandler(handler)
3655 handler.close()
3656
3657 @patch.object(logging.handlers.QueueListener, 'handle')
3658 def test_handle_called_with_queue_queue(self, mock_handle):
3659 for i in range(self.repeat):
3660 log_queue = queue.Queue()
3661 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3662 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3663 'correct number of handled log messages')
3664
3665 @patch.object(logging.handlers.QueueListener, 'handle')
3666 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003667 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003668 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003669 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003670 for i in range(self.repeat):
3671 log_queue = multiprocessing.Queue()
3672 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003673 log_queue.close()
3674 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003675 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3676 'correct number of handled log messages')
3677
3678 @staticmethod
3679 def get_all_from_queue(log_queue):
3680 try:
3681 while True:
3682 yield log_queue.get_nowait()
3683 except queue.Empty:
3684 return []
3685
3686 def test_no_messages_in_queue_after_stop(self):
3687 """
3688 Five messages are logged then the QueueListener is stopped. This
3689 test then gets everything off the queue. Failure of this test
3690 indicates that messages were not registered on the queue until
3691 _after_ the QueueListener stopped.
3692 """
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003693 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003694 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003695 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003696 for i in range(self.repeat):
3697 queue = multiprocessing.Queue()
3698 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3699 # time.sleep(1)
3700 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003701 queue.close()
3702 queue.join_thread()
3703
Vinay Sajipd61910c2016-09-08 01:13:39 +01003704 expected = [[], [logging.handlers.QueueListener._sentinel]]
3705 self.assertIn(items, expected,
3706 'Found unexpected messages in queue: %s' % (
3707 [m.msg if isinstance(m, logging.LogRecord)
3708 else m for m in items]))
3709
Bar Harel6b282e12019-06-01 12:19:09 +03003710 def test_calls_task_done_after_stop(self):
3711 # Issue 36813: Make sure queue.join does not deadlock.
3712 log_queue = queue.Queue()
3713 listener = logging.handlers.QueueListener(log_queue)
3714 listener.start()
3715 listener.stop()
3716 with self.assertRaises(ValueError):
3717 # Make sure all tasks are done and .join won't block.
3718 log_queue.task_done()
3719
Vinay Sajipe723e962011-04-15 22:27:17 +01003720
Vinay Sajip37eb3382011-04-26 20:26:41 +01003721ZERO = datetime.timedelta(0)
3722
3723class UTC(datetime.tzinfo):
3724 def utcoffset(self, dt):
3725 return ZERO
3726
3727 dst = utcoffset
3728
3729 def tzname(self, dt):
3730 return 'UTC'
3731
3732utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003733
Eric Larson9fdb76c2020-09-25 14:08:50 -04003734class AssertErrorMessage:
3735
3736 def assert_error_message(self, exception, message, *args, **kwargs):
3737 try:
3738 self.assertRaises((), *args, **kwargs)
3739 except exception as e:
3740 self.assertEqual(message, str(e))
3741
3742class FormatterTest(unittest.TestCase, AssertErrorMessage):
Vinay Sajipa39c5712010-10-25 13:57:39 +00003743 def setUp(self):
3744 self.common = {
3745 'name': 'formatter.test',
3746 'level': logging.DEBUG,
3747 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3748 'lineno': 42,
3749 'exc_info': None,
3750 'func': None,
3751 'msg': 'Message with %d %s',
3752 'args': (2, 'placeholders'),
3753 }
3754 self.variants = {
Bar Harel8f192d12020-06-18 17:18:58 +03003755 'custom': {
3756 'custom': 1234
3757 }
Vinay Sajipa39c5712010-10-25 13:57:39 +00003758 }
3759
3760 def get_record(self, name=None):
3761 result = dict(self.common)
3762 if name is not None:
3763 result.update(self.variants[name])
3764 return logging.makeLogRecord(result)
3765
3766 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003767 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003768 r = self.get_record()
3769 f = logging.Formatter('${%(message)s}')
3770 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3771 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003772 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003773 self.assertFalse(f.usesTime())
3774 f = logging.Formatter('%(asctime)s')
3775 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003776 f = logging.Formatter('%(asctime)-15s')
3777 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003778 f = logging.Formatter('%(asctime)#15s')
3779 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003780
3781 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003782 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003783 r = self.get_record()
3784 f = logging.Formatter('$%{message}%$', style='{')
3785 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3786 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003787 self.assertRaises(ValueError, f.format, r)
3788 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003789 self.assertFalse(f.usesTime())
3790 f = logging.Formatter('{asctime}', style='{')
3791 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003792 f = logging.Formatter('{asctime!s:15}', style='{')
3793 self.assertTrue(f.usesTime())
3794 f = logging.Formatter('{asctime:15}', style='{')
3795 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003796
3797 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003798 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003799 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003800 f = logging.Formatter('${message}', style='$')
3801 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003802 f = logging.Formatter('$message', style='$')
3803 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3804 f = logging.Formatter('$$%${message}%$$', style='$')
3805 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3806 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003807 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003808 self.assertFalse(f.usesTime())
3809 f = logging.Formatter('${asctime}', style='$')
3810 self.assertTrue(f.usesTime())
3811 f = logging.Formatter('$asctime', style='$')
3812 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003813 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003814 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003815 f = logging.Formatter('${asctime}--', style='$')
3816 self.assertTrue(f.usesTime())
3817
3818 def test_format_validate(self):
3819 # Check correct formatting
3820 # Percentage style
3821 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3822 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3823 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3824 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3825 f = logging.Formatter("%(process)#+027.23X")
3826 self.assertEqual(f._fmt, "%(process)#+027.23X")
3827 f = logging.Formatter("%(foo)#.*g")
3828 self.assertEqual(f._fmt, "%(foo)#.*g")
3829
3830 # StrFormat Style
3831 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3832 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3833 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3834 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3835 f = logging.Formatter("{customfield!s:#<30}", style="{")
3836 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3837 f = logging.Formatter("{message!r}", style="{")
3838 self.assertEqual(f._fmt, "{message!r}")
3839 f = logging.Formatter("{message!s}", style="{")
3840 self.assertEqual(f._fmt, "{message!s}")
3841 f = logging.Formatter("{message!a}", style="{")
3842 self.assertEqual(f._fmt, "{message!a}")
3843 f = logging.Formatter("{process!r:4.2}", style="{")
3844 self.assertEqual(f._fmt, "{process!r:4.2}")
3845 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3846 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3847 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3848 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3849 f = logging.Formatter("{foo:12.{p}}", style="{")
3850 self.assertEqual(f._fmt, "{foo:12.{p}}")
3851 f = logging.Formatter("{foo:{w}.6}", style="{")
3852 self.assertEqual(f._fmt, "{foo:{w}.6}")
3853 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3854 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3855 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3856 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3857 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3858 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3859
3860 # Dollar style
3861 f = logging.Formatter("${asctime} - $message", style="$")
3862 self.assertEqual(f._fmt, "${asctime} - $message")
3863 f = logging.Formatter("$bar $$", style="$")
3864 self.assertEqual(f._fmt, "$bar $$")
3865 f = logging.Formatter("$bar $$$$", style="$")
3866 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3867
3868 # Testing when ValueError being raised from incorrect format
3869 # Percentage Style
3870 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3871 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3872 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3873 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3874 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3875 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3876 self.assertRaises(ValueError, logging.Formatter, '${message}')
3877 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3878 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3879
3880 # StrFormat Style
3881 # Testing failure for '-' in field name
3882 self.assert_error_message(
3883 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003884 "invalid format: invalid field name/expression: 'name-thing'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003885 logging.Formatter, "{name-thing}", style="{"
3886 )
3887 # Testing failure for style mismatch
3888 self.assert_error_message(
3889 ValueError,
3890 "invalid format: no fields",
3891 logging.Formatter, '%(asctime)s', style='{'
3892 )
3893 # Testing failure for invalid conversion
3894 self.assert_error_message(
3895 ValueError,
3896 "invalid conversion: 'Z'"
3897 )
3898 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3899 self.assert_error_message(
3900 ValueError,
3901 "invalid format: expected ':' after conversion specifier",
3902 logging.Formatter, '{asctime!aa:15}', style='{'
3903 )
3904 # Testing failure for invalid spec
3905 self.assert_error_message(
3906 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003907 "invalid format: bad specifier: '.2ff'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003908 logging.Formatter, '{process:.2ff}', style='{'
3909 )
3910 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3911 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3912 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3913 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3914 # Testing failure for mismatch braces
3915 self.assert_error_message(
3916 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003917 "invalid format: expected '}' before end of string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003918 logging.Formatter, '{process', style='{'
3919 )
3920 self.assert_error_message(
3921 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003922 "invalid format: Single '}' encountered in format string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003923 logging.Formatter, 'process}', style='{'
3924 )
3925 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3926 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3927 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3928 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3929 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3930 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3931 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3932 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3933 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3934
3935 # Dollar style
3936 # Testing failure for mismatch bare $
3937 self.assert_error_message(
3938 ValueError,
3939 "invalid format: bare \'$\' not allowed",
3940 logging.Formatter, '$bar $$$', style='$'
3941 )
3942 self.assert_error_message(
3943 ValueError,
3944 "invalid format: bare \'$\' not allowed",
3945 logging.Formatter, 'bar $', style='$'
3946 )
3947 self.assert_error_message(
3948 ValueError,
3949 "invalid format: bare \'$\' not allowed",
3950 logging.Formatter, 'foo $.', style='$'
3951 )
3952 # Testing failure for mismatch style
3953 self.assert_error_message(
3954 ValueError,
3955 "invalid format: no fields",
3956 logging.Formatter, '{asctime}', style='$'
3957 )
3958 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3959
3960 # Testing failure for incorrect fields
3961 self.assert_error_message(
3962 ValueError,
3963 "invalid format: no fields",
3964 logging.Formatter, 'foo', style='$'
3965 )
3966 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003967
Bar Harel8f192d12020-06-18 17:18:58 +03003968 def test_defaults_parameter(self):
3969 fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message']
3970 styles = ['%', '{', '$']
3971 for fmt, style in zip(fmts, styles):
3972 f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'})
3973 r = self.get_record()
3974 self.assertEqual(f.format(r), 'Default Message with 2 placeholders')
3975 r = self.get_record("custom")
3976 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3977
3978 # Without default
3979 f = logging.Formatter(fmt, style=style)
3980 r = self.get_record()
3981 self.assertRaises(ValueError, f.format, r)
3982
3983 # Non-existing default is ignored
3984 f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'})
3985 r = self.get_record("custom")
3986 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3987
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003988 def test_invalid_style(self):
3989 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3990
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003991 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003992 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003993 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3994 # We use None to indicate we want the local timezone
3995 # We're essentially converting a UTC time to local time
3996 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003997 r.msecs = 123
3998 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01003999 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004000 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
4001 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01004002 f.format(r)
4003 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
4004
Mariusz Felisiak06a35542020-04-17 18:02:47 +02004005 def test_default_msec_format_none(self):
4006 class NoMsecFormatter(logging.Formatter):
4007 default_msec_format = None
4008 default_time_format = '%d/%m/%Y %H:%M:%S'
4009
4010 r = self.get_record()
4011 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
4012 r.created = time.mktime(dt.astimezone(None).timetuple())
4013 f = NoMsecFormatter()
4014 f.converter = time.gmtime
4015 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
4016
4017
Vinay Sajip985ef872011-04-26 19:34:04 +01004018class TestBufferingFormatter(logging.BufferingFormatter):
4019 def formatHeader(self, records):
4020 return '[(%d)' % len(records)
4021
4022 def formatFooter(self, records):
4023 return '(%d)]' % len(records)
4024
4025class BufferingFormatterTest(unittest.TestCase):
4026 def setUp(self):
4027 self.records = [
4028 logging.makeLogRecord({'msg': 'one'}),
4029 logging.makeLogRecord({'msg': 'two'}),
4030 ]
4031
4032 def test_default(self):
4033 f = logging.BufferingFormatter()
4034 self.assertEqual('', f.format([]))
4035 self.assertEqual('onetwo', f.format(self.records))
4036
4037 def test_custom(self):
4038 f = TestBufferingFormatter()
4039 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
4040 lf = logging.Formatter('<%(message)s>')
4041 f = TestBufferingFormatter(lf)
4042 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004043
4044class ExceptionTest(BaseTest):
4045 def test_formatting(self):
4046 r = self.root_logger
4047 h = RecordingHandler()
4048 r.addHandler(h)
4049 try:
4050 raise RuntimeError('deliberate mistake')
4051 except:
4052 logging.exception('failed', stack_info=True)
4053 r.removeHandler(h)
4054 h.close()
4055 r = h.records[0]
4056 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
4057 'call last):\n'))
4058 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4059 'deliberate mistake'))
4060 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4061 'call last):\n'))
4062 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4063 'stack_info=True)'))
4064
4065
Vinay Sajip5a27d402010-12-10 11:42:57 +00004066class LastResortTest(BaseTest):
4067 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004068 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004069 root = self.root_logger
4070 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004071 old_lastresort = logging.lastResort
4072 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004073
Vinay Sajip5a27d402010-12-10 11:42:57 +00004074 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004075 with support.captured_stderr() as stderr:
4076 root.debug('This should not appear')
4077 self.assertEqual(stderr.getvalue(), '')
4078 root.warning('Final chance!')
4079 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4080
4081 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004082 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004083 with support.captured_stderr() as stderr:
4084 root.warning('Final chance!')
4085 msg = 'No handlers could be found for logger "root"\n'
4086 self.assertEqual(stderr.getvalue(), msg)
4087
Vinay Sajip5a27d402010-12-10 11:42:57 +00004088 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004089 with support.captured_stderr() as stderr:
4090 root.warning('Final chance!')
4091 self.assertEqual(stderr.getvalue(), '')
4092
4093 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004094 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004095 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004096 with support.captured_stderr() as stderr:
4097 root.warning('Final chance!')
4098 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004099 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004100 root.addHandler(self.root_hdlr)
4101 logging.lastResort = old_lastresort
4102 logging.raiseExceptions = old_raise_exceptions
4103
4104
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004105class FakeHandler:
4106
4107 def __init__(self, identifier, called):
4108 for method in ('acquire', 'flush', 'close', 'release'):
4109 setattr(self, method, self.record_call(identifier, method, called))
4110
4111 def record_call(self, identifier, method_name, called):
4112 def inner():
4113 called.append('{} - {}'.format(identifier, method_name))
4114 return inner
4115
4116
4117class RecordingHandler(logging.NullHandler):
4118
4119 def __init__(self, *args, **kwargs):
4120 super(RecordingHandler, self).__init__(*args, **kwargs)
4121 self.records = []
4122
4123 def handle(self, record):
4124 """Keep track of all the emitted records."""
4125 self.records.append(record)
4126
4127
4128class ShutdownTest(BaseTest):
4129
Vinay Sajip5e66b162011-04-20 15:41:14 +01004130 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004131
4132 def setUp(self):
4133 super(ShutdownTest, self).setUp()
4134 self.called = []
4135
4136 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004137 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004138
4139 def raise_error(self, error):
4140 def inner():
4141 raise error()
4142 return inner
4143
4144 def test_no_failure(self):
4145 # create some fake handlers
4146 handler0 = FakeHandler(0, self.called)
4147 handler1 = FakeHandler(1, self.called)
4148 handler2 = FakeHandler(2, self.called)
4149
4150 # create live weakref to those handlers
4151 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4152
4153 logging.shutdown(handlerList=list(handlers))
4154
4155 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4156 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4157 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4158 self.assertEqual(expected, self.called)
4159
4160 def _test_with_failure_in_method(self, method, error):
4161 handler = FakeHandler(0, self.called)
4162 setattr(handler, method, self.raise_error(error))
4163 handlers = [logging.weakref.ref(handler)]
4164
4165 logging.shutdown(handlerList=list(handlers))
4166
4167 self.assertEqual('0 - release', self.called[-1])
4168
4169 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004170 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004171
4172 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004173 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004174
4175 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004176 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004177
4178 def test_with_valueerror_in_acquire(self):
4179 self._test_with_failure_in_method('acquire', ValueError)
4180
4181 def test_with_valueerror_in_flush(self):
4182 self._test_with_failure_in_method('flush', ValueError)
4183
4184 def test_with_valueerror_in_close(self):
4185 self._test_with_failure_in_method('close', ValueError)
4186
4187 def test_with_other_error_in_acquire_without_raise(self):
4188 logging.raiseExceptions = False
4189 self._test_with_failure_in_method('acquire', IndexError)
4190
4191 def test_with_other_error_in_flush_without_raise(self):
4192 logging.raiseExceptions = False
4193 self._test_with_failure_in_method('flush', IndexError)
4194
4195 def test_with_other_error_in_close_without_raise(self):
4196 logging.raiseExceptions = False
4197 self._test_with_failure_in_method('close', IndexError)
4198
4199 def test_with_other_error_in_acquire_with_raise(self):
4200 logging.raiseExceptions = True
4201 self.assertRaises(IndexError, self._test_with_failure_in_method,
4202 'acquire', IndexError)
4203
4204 def test_with_other_error_in_flush_with_raise(self):
4205 logging.raiseExceptions = True
4206 self.assertRaises(IndexError, self._test_with_failure_in_method,
4207 'flush', IndexError)
4208
4209 def test_with_other_error_in_close_with_raise(self):
4210 logging.raiseExceptions = True
4211 self.assertRaises(IndexError, self._test_with_failure_in_method,
4212 'close', IndexError)
4213
4214
4215class ModuleLevelMiscTest(BaseTest):
4216
Vinay Sajip5e66b162011-04-20 15:41:14 +01004217 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004218
4219 def test_disable(self):
4220 old_disable = logging.root.manager.disable
4221 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004222 self.assertEqual(old_disable, 0)
4223 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004224
4225 logging.disable(83)
4226 self.assertEqual(logging.root.manager.disable, 83)
4227
Matthias Bussonnierb32d8b42020-12-16 01:43:39 -08004228 self.assertRaises(ValueError, logging.disable, "doesnotexists")
4229
4230 class _NotAnIntOrString:
4231 pass
4232
4233 self.assertRaises(TypeError, logging.disable, _NotAnIntOrString())
4234
4235 logging.disable("WARN")
4236
Vinay Sajipd489ac92016-12-31 11:40:11 +00004237 # test the default value introduced in 3.7
4238 # (Issue #28524)
4239 logging.disable()
4240 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4241
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004242 def _test_log(self, method, level=None):
4243 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004244 support.patch(self, logging, 'basicConfig',
4245 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004246
4247 recording = RecordingHandler()
4248 logging.root.addHandler(recording)
4249
4250 log_method = getattr(logging, method)
4251 if level is not None:
4252 log_method(level, "test me: %r", recording)
4253 else:
4254 log_method("test me: %r", recording)
4255
4256 self.assertEqual(len(recording.records), 1)
4257 record = recording.records[0]
4258 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4259
4260 expected_level = level if level is not None else getattr(logging, method.upper())
4261 self.assertEqual(record.levelno, expected_level)
4262
4263 # basicConfig was not called!
4264 self.assertEqual(called, [])
4265
4266 def test_log(self):
4267 self._test_log('log', logging.ERROR)
4268
4269 def test_debug(self):
4270 self._test_log('debug')
4271
4272 def test_info(self):
4273 self._test_log('info')
4274
4275 def test_warning(self):
4276 self._test_log('warning')
4277
4278 def test_error(self):
4279 self._test_log('error')
4280
4281 def test_critical(self):
4282 self._test_log('critical')
4283
4284 def test_set_logger_class(self):
4285 self.assertRaises(TypeError, logging.setLoggerClass, object)
4286
4287 class MyLogger(logging.Logger):
4288 pass
4289
4290 logging.setLoggerClass(MyLogger)
4291 self.assertEqual(logging.getLoggerClass(), MyLogger)
4292
4293 logging.setLoggerClass(logging.Logger)
4294 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4295
Vinay Sajip01500012019-06-19 11:46:53 +01004296 def test_subclass_logger_cache(self):
4297 # bpo-37258
4298 message = []
4299
4300 class MyLogger(logging.getLoggerClass()):
4301 def __init__(self, name='MyLogger', level=logging.NOTSET):
4302 super().__init__(name, level)
4303 message.append('initialized')
4304
4305 logging.setLoggerClass(MyLogger)
4306 logger = logging.getLogger('just_some_logger')
4307 self.assertEqual(message, ['initialized'])
4308 stream = io.StringIO()
4309 h = logging.StreamHandler(stream)
4310 logger.addHandler(h)
4311 try:
4312 logger.setLevel(logging.DEBUG)
4313 logger.debug("hello")
4314 self.assertEqual(stream.getvalue().strip(), "hello")
4315
4316 stream.truncate(0)
4317 stream.seek(0)
4318
4319 logger.setLevel(logging.INFO)
4320 logger.debug("hello")
4321 self.assertEqual(stream.getvalue(), "")
4322 finally:
4323 logger.removeHandler(h)
4324 h.close()
4325 logging.setLoggerClass(logging.Logger)
4326
Antoine Pitrou712cb732013-12-21 15:51:54 +01004327 def test_logging_at_shutdown(self):
Victor Stinner45df61f2020-11-02 23:17:46 +01004328 # bpo-20037: Doing text I/O late at interpreter shutdown must not crash
4329 code = textwrap.dedent("""
Antoine Pitrou712cb732013-12-21 15:51:54 +01004330 import logging
4331
4332 class A:
4333 def __del__(self):
4334 try:
4335 raise ValueError("some error")
4336 except Exception:
4337 logging.exception("exception in __del__")
4338
Victor Stinner45df61f2020-11-02 23:17:46 +01004339 a = A()
4340 """)
Antoine Pitrou712cb732013-12-21 15:51:54 +01004341 rc, out, err = assert_python_ok("-c", code)
4342 err = err.decode()
4343 self.assertIn("exception in __del__", err)
4344 self.assertIn("ValueError: some error", err)
4345
Victor Stinner45df61f2020-11-02 23:17:46 +01004346 def test_logging_at_shutdown_open(self):
4347 # bpo-26789: FileHandler keeps a reference to the builtin open()
4348 # function to be able to open or reopen the file during Python
4349 # finalization.
4350 filename = os_helper.TESTFN
4351 self.addCleanup(os_helper.unlink, filename)
4352
4353 code = textwrap.dedent(f"""
4354 import builtins
4355 import logging
4356
4357 class A:
4358 def __del__(self):
4359 logging.error("log in __del__")
4360
4361 # basicConfig() opens the file, but logging.shutdown() closes
4362 # it at Python exit. When A.__del__() is called,
4363 # FileHandler._open() must be called again to re-open the file.
Inada Naokifb786922021-04-06 11:18:41 +09004364 logging.basicConfig(filename={filename!r}, encoding="utf-8")
Victor Stinner45df61f2020-11-02 23:17:46 +01004365
4366 a = A()
4367
4368 # Simulate the Python finalization which removes the builtin
4369 # open() function.
4370 del builtins.open
4371 """)
4372 assert_python_ok("-c", code)
4373
Inada Naokifb786922021-04-06 11:18:41 +09004374 with open(filename, encoding="utf-8") as fp:
Victor Stinner45df61f2020-11-02 23:17:46 +01004375 self.assertEqual(fp.read().rstrip(), "ERROR:root:log in __del__")
4376
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004377 def test_recursion_error(self):
4378 # Issue 36272
Victor Stinner45df61f2020-11-02 23:17:46 +01004379 code = textwrap.dedent("""
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004380 import logging
4381
4382 def rec():
4383 logging.error("foo")
4384 rec()
4385
Victor Stinner45df61f2020-11-02 23:17:46 +01004386 rec()
4387 """)
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004388 rc, out, err = assert_python_failure("-c", code)
4389 err = err.decode()
4390 self.assertNotIn("Cannot recover from stack overflow.", err)
4391 self.assertEqual(rc, 1)
4392
Antoine Pitrou712cb732013-12-21 15:51:54 +01004393
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004394class LogRecordTest(BaseTest):
4395 def test_str_rep(self):
4396 r = logging.makeLogRecord({})
4397 s = str(r)
4398 self.assertTrue(s.startswith('<LogRecord: '))
4399 self.assertTrue(s.endswith('>'))
4400
4401 def test_dict_arg(self):
4402 h = RecordingHandler()
4403 r = logging.getLogger()
4404 r.addHandler(h)
4405 d = {'less' : 'more' }
4406 logging.warning('less is %(less)s', d)
4407 self.assertIs(h.records[0].args, d)
4408 self.assertEqual(h.records[0].message, 'less is more')
4409 r.removeHandler(h)
4410 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004411
Irit Katriel3fd69992020-09-08 20:40:04 +01004412 @staticmethod # pickled as target of child process in the following test
4413 def _extract_logrecord_process_name(key, logMultiprocessing, conn=None):
4414 prev_logMultiprocessing = logging.logMultiprocessing
4415 logging.logMultiprocessing = logMultiprocessing
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004416 try:
4417 import multiprocessing as mp
Irit Katriel3fd69992020-09-08 20:40:04 +01004418 name = mp.current_process().name
4419
4420 r1 = logging.makeLogRecord({'msg': f'msg1_{key}'})
4421 del sys.modules['multiprocessing']
4422 r2 = logging.makeLogRecord({'msg': f'msg2_{key}'})
4423
4424 results = {'processName' : name,
4425 'r1.processName': r1.processName,
4426 'r2.processName': r2.processName,
4427 }
4428 finally:
4429 logging.logMultiprocessing = prev_logMultiprocessing
4430 if conn:
4431 conn.send(results)
4432 else:
4433 return results
4434
4435 def test_multiprocessing(self):
4436 multiprocessing_imported = 'multiprocessing' in sys.modules
4437 try:
4438 # logMultiprocessing is True by default
4439 self.assertEqual(logging.logMultiprocessing, True)
4440
4441 LOG_MULTI_PROCESSING = True
4442 # When logMultiprocessing == True:
4443 # In the main process processName = 'MainProcess'
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004444 r = logging.makeLogRecord({})
Irit Katriel3fd69992020-09-08 20:40:04 +01004445 self.assertEqual(r.processName, 'MainProcess')
4446
4447 results = self._extract_logrecord_process_name(1, LOG_MULTI_PROCESSING)
4448 self.assertEqual('MainProcess', results['processName'])
4449 self.assertEqual('MainProcess', results['r1.processName'])
4450 self.assertEqual('MainProcess', results['r2.processName'])
4451
4452 # In other processes, processName is correct when multiprocessing in imported,
4453 # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762).
4454 import multiprocessing
4455 parent_conn, child_conn = multiprocessing.Pipe()
4456 p = multiprocessing.Process(
4457 target=self._extract_logrecord_process_name,
4458 args=(2, LOG_MULTI_PROCESSING, child_conn,)
4459 )
4460 p.start()
4461 results = parent_conn.recv()
4462 self.assertNotEqual('MainProcess', results['processName'])
4463 self.assertEqual(results['processName'], results['r1.processName'])
4464 self.assertEqual('MainProcess', results['r2.processName'])
4465 p.join()
4466
4467 finally:
4468 if multiprocessing_imported:
4469 import multiprocessing
4470
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004471
4472 def test_optional(self):
4473 r = logging.makeLogRecord({})
4474 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004475 NOT_NONE(r.thread)
4476 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004477 NOT_NONE(r.process)
4478 NOT_NONE(r.processName)
4479 log_threads = logging.logThreads
4480 log_processes = logging.logProcesses
4481 log_multiprocessing = logging.logMultiprocessing
4482 try:
4483 logging.logThreads = False
4484 logging.logProcesses = False
4485 logging.logMultiprocessing = False
4486 r = logging.makeLogRecord({})
4487 NONE = self.assertIsNone
4488 NONE(r.thread)
4489 NONE(r.threadName)
4490 NONE(r.process)
4491 NONE(r.processName)
4492 finally:
4493 logging.logThreads = log_threads
4494 logging.logProcesses = log_processes
4495 logging.logMultiprocessing = log_multiprocessing
4496
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004497class BasicConfigTest(unittest.TestCase):
4498
Vinay Sajip95bf5042011-04-20 11:50:56 +01004499 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004500
4501 def setUp(self):
4502 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004503 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004504 self.saved_handlers = logging._handlers.copy()
4505 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004506 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004507 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004508 logging.root.handlers = []
4509
4510 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004511 for h in logging.root.handlers[:]:
4512 logging.root.removeHandler(h)
4513 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004514 super(BasicConfigTest, self).tearDown()
4515
Vinay Sajip3def7e02011-04-20 10:58:06 +01004516 def cleanup(self):
4517 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004518 logging._handlers.clear()
4519 logging._handlers.update(self.saved_handlers)
4520 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004521 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004522
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004523 def test_no_kwargs(self):
4524 logging.basicConfig()
4525
4526 # handler defaults to a StreamHandler to sys.stderr
4527 self.assertEqual(len(logging.root.handlers), 1)
4528 handler = logging.root.handlers[0]
4529 self.assertIsInstance(handler, logging.StreamHandler)
4530 self.assertEqual(handler.stream, sys.stderr)
4531
4532 formatter = handler.formatter
4533 # format defaults to logging.BASIC_FORMAT
4534 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4535 # datefmt defaults to None
4536 self.assertIsNone(formatter.datefmt)
4537 # style defaults to %
4538 self.assertIsInstance(formatter._style, logging.PercentStyle)
4539
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004540 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004541 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004542
Vinay Sajip1fd12022014-01-13 21:59:56 +00004543 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004544 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004545 logging.basicConfig(stream=sys.stdout, style="{")
4546 logging.error("Log an error")
4547 sys.stdout.seek(0)
4548 self.assertEqual(output.getvalue().strip(),
4549 "ERROR:root:Log an error")
4550
4551 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004552 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004553 logging.basicConfig(stream=sys.stdout, style="$")
4554 logging.error("Log an error")
4555 sys.stdout.seek(0)
4556 self.assertEqual(output.getvalue().strip(),
4557 "ERROR:root:Log an error")
4558
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004559 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004560
4561 def cleanup(h1, h2, fn):
4562 h1.close()
4563 h2.close()
4564 os.remove(fn)
4565
Inada Naokifb786922021-04-06 11:18:41 +09004566 logging.basicConfig(filename='test.log', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004567
4568 self.assertEqual(len(logging.root.handlers), 1)
4569 handler = logging.root.handlers[0]
4570 self.assertIsInstance(handler, logging.FileHandler)
4571
Inada Naokifb786922021-04-06 11:18:41 +09004572 expected = logging.FileHandler('test.log', 'a', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004573 self.assertEqual(handler.stream.mode, expected.stream.mode)
4574 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004575 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004576
4577 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004578
4579 def cleanup(h1, h2, fn):
4580 h1.close()
4581 h2.close()
4582 os.remove(fn)
4583
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004584 logging.basicConfig(filename='test.log', filemode='wb')
4585
4586 handler = logging.root.handlers[0]
4587 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004588 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004589 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004590
4591 def test_stream(self):
4592 stream = io.StringIO()
4593 self.addCleanup(stream.close)
4594 logging.basicConfig(stream=stream)
4595
4596 self.assertEqual(len(logging.root.handlers), 1)
4597 handler = logging.root.handlers[0]
4598 self.assertIsInstance(handler, logging.StreamHandler)
4599 self.assertEqual(handler.stream, stream)
4600
4601 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004602 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004603
4604 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004605 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004606
4607 def test_datefmt(self):
4608 logging.basicConfig(datefmt='bar')
4609
4610 formatter = logging.root.handlers[0].formatter
4611 self.assertEqual(formatter.datefmt, 'bar')
4612
4613 def test_style(self):
4614 logging.basicConfig(style='$')
4615
4616 formatter = logging.root.handlers[0].formatter
4617 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4618
4619 def test_level(self):
4620 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004621 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004622
4623 logging.basicConfig(level=57)
4624 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004625 # Test that second call has no effect
4626 logging.basicConfig(level=58)
4627 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004628
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004629 def test_incompatible(self):
4630 assertRaises = self.assertRaises
4631 handlers = [logging.StreamHandler()]
4632 stream = sys.stderr
4633 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004634 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004635 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004636 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004637 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004638 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004639 # Issue 23207: test for invalid kwargs
4640 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4641 # Should pop both filename and filemode even if filename is None
4642 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004643
4644 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004645 handlers = [
4646 logging.StreamHandler(),
4647 logging.StreamHandler(sys.stdout),
4648 logging.StreamHandler(),
4649 ]
4650 f = logging.Formatter()
4651 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004652 logging.basicConfig(handlers=handlers)
4653 self.assertIs(handlers[0], logging.root.handlers[0])
4654 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004655 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004656 self.assertIsNotNone(handlers[0].formatter)
4657 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004658 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004659 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4660
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004661 def test_force(self):
4662 old_string_io = io.StringIO()
4663 new_string_io = io.StringIO()
4664 old_handlers = [logging.StreamHandler(old_string_io)]
4665 new_handlers = [logging.StreamHandler(new_string_io)]
4666 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4667 logging.warning('warn')
4668 logging.info('info')
4669 logging.debug('debug')
4670 self.assertEqual(len(logging.root.handlers), 1)
4671 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4672 force=True)
4673 logging.warning('warn')
4674 logging.info('info')
4675 logging.debug('debug')
4676 self.assertEqual(len(logging.root.handlers), 1)
4677 self.assertEqual(old_string_io.getvalue().strip(),
4678 'WARNING:root:warn')
4679 self.assertEqual(new_string_io.getvalue().strip(),
4680 'WARNING:root:warn\nINFO:root:info')
4681
Vinay Sajipca7b5042019-06-17 17:40:52 +01004682 def test_encoding(self):
4683 try:
4684 encoding = 'utf-8'
4685 logging.basicConfig(filename='test.log', encoding=encoding,
4686 errors='strict',
4687 format='%(message)s', level=logging.DEBUG)
4688
4689 self.assertEqual(len(logging.root.handlers), 1)
4690 handler = logging.root.handlers[0]
4691 self.assertIsInstance(handler, logging.FileHandler)
4692 self.assertEqual(handler.encoding, encoding)
4693 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4694 finally:
4695 handler.close()
4696 with open('test.log', encoding='utf-8') as f:
4697 data = f.read().strip()
4698 os.remove('test.log')
4699 self.assertEqual(data,
4700 'The Øresund Bridge joins Copenhagen to Malmö')
4701
4702 def test_encoding_errors(self):
4703 try:
4704 encoding = 'ascii'
4705 logging.basicConfig(filename='test.log', encoding=encoding,
4706 errors='ignore',
4707 format='%(message)s', level=logging.DEBUG)
4708
4709 self.assertEqual(len(logging.root.handlers), 1)
4710 handler = logging.root.handlers[0]
4711 self.assertIsInstance(handler, logging.FileHandler)
4712 self.assertEqual(handler.encoding, encoding)
4713 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4714 finally:
4715 handler.close()
4716 with open('test.log', encoding='utf-8') as f:
4717 data = f.read().strip()
4718 os.remove('test.log')
4719 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4720
4721 def test_encoding_errors_default(self):
4722 try:
4723 encoding = 'ascii'
4724 logging.basicConfig(filename='test.log', encoding=encoding,
4725 format='%(message)s', level=logging.DEBUG)
4726
4727 self.assertEqual(len(logging.root.handlers), 1)
4728 handler = logging.root.handlers[0]
4729 self.assertIsInstance(handler, logging.FileHandler)
4730 self.assertEqual(handler.encoding, encoding)
4731 self.assertEqual(handler.errors, 'backslashreplace')
4732 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4733 finally:
4734 handler.close()
4735 with open('test.log', encoding='utf-8') as f:
4736 data = f.read().strip()
4737 os.remove('test.log')
4738 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4739 r'Bridge joins Copenhagen to Malm\xf6')
4740
4741 def test_encoding_errors_none(self):
4742 # Specifying None should behave as 'strict'
4743 try:
4744 encoding = 'ascii'
4745 logging.basicConfig(filename='test.log', encoding=encoding,
4746 errors=None,
4747 format='%(message)s', level=logging.DEBUG)
4748
4749 self.assertEqual(len(logging.root.handlers), 1)
4750 handler = logging.root.handlers[0]
4751 self.assertIsInstance(handler, logging.FileHandler)
4752 self.assertEqual(handler.encoding, encoding)
4753 self.assertIsNone(handler.errors)
4754
4755 message = []
4756
4757 def dummy_handle_error(record):
4758 _, v, _ = sys.exc_info()
4759 message.append(str(v))
4760
4761 handler.handleError = dummy_handle_error
4762 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4763 self.assertTrue(message)
4764 self.assertIn("'ascii' codec can't encode "
4765 "character '\\xd8' in position 4:", message[0])
4766 finally:
4767 handler.close()
4768 with open('test.log', encoding='utf-8') as f:
4769 data = f.read().strip()
4770 os.remove('test.log')
4771 # didn't write anything due to the encoding error
4772 self.assertEqual(data, r'')
4773
4774
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004775 def _test_log(self, method, level=None):
4776 # logging.root has no handlers so basicConfig should be called
4777 called = []
4778
4779 old_basic_config = logging.basicConfig
4780 def my_basic_config(*a, **kw):
4781 old_basic_config()
4782 old_level = logging.root.level
4783 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004784 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004785 called.append((a, kw))
4786
Vinay Sajip1feedb42014-05-31 08:19:12 +01004787 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004788
4789 log_method = getattr(logging, method)
4790 if level is not None:
4791 log_method(level, "test me")
4792 else:
4793 log_method("test me")
4794
4795 # basicConfig was called with no arguments
4796 self.assertEqual(called, [((), {})])
4797
4798 def test_log(self):
4799 self._test_log('log', logging.WARNING)
4800
4801 def test_debug(self):
4802 self._test_log('debug')
4803
4804 def test_info(self):
4805 self._test_log('info')
4806
4807 def test_warning(self):
4808 self._test_log('warning')
4809
4810 def test_error(self):
4811 self._test_log('error')
4812
4813 def test_critical(self):
4814 self._test_log('critical')
4815
4816
4817class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004818 def setUp(self):
4819 super(LoggerAdapterTest, self).setUp()
4820 old_handler_list = logging._handlerList[:]
4821
4822 self.recording = RecordingHandler()
4823 self.logger = logging.root
4824 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004825 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004826 self.addCleanup(self.recording.close)
4827
4828 def cleanup():
4829 logging._handlerList[:] = old_handler_list
4830
4831 self.addCleanup(cleanup)
4832 self.addCleanup(logging.shutdown)
4833 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4834
4835 def test_exception(self):
4836 msg = 'testing exception: %r'
4837 exc = None
4838 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004839 1 / 0
4840 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004841 exc = e
4842 self.adapter.exception(msg, self.recording)
4843
4844 self.assertEqual(len(self.recording.records), 1)
4845 record = self.recording.records[0]
4846 self.assertEqual(record.levelno, logging.ERROR)
4847 self.assertEqual(record.msg, msg)
4848 self.assertEqual(record.args, (self.recording,))
4849 self.assertEqual(record.exc_info,
4850 (exc.__class__, exc, exc.__traceback__))
4851
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004852 def test_exception_excinfo(self):
4853 try:
4854 1 / 0
4855 except ZeroDivisionError as e:
4856 exc = e
4857
4858 self.adapter.exception('exc_info test', exc_info=exc)
4859
4860 self.assertEqual(len(self.recording.records), 1)
4861 record = self.recording.records[0]
4862 self.assertEqual(record.exc_info,
4863 (exc.__class__, exc, exc.__traceback__))
4864
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004865 def test_critical(self):
4866 msg = 'critical test! %r'
4867 self.adapter.critical(msg, self.recording)
4868
4869 self.assertEqual(len(self.recording.records), 1)
4870 record = self.recording.records[0]
4871 self.assertEqual(record.levelno, logging.CRITICAL)
4872 self.assertEqual(record.msg, msg)
4873 self.assertEqual(record.args, (self.recording,))
4874
4875 def test_is_enabled_for(self):
4876 old_disable = self.adapter.logger.manager.disable
4877 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004878 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4879 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004880 self.assertFalse(self.adapter.isEnabledFor(32))
4881
4882 def test_has_handlers(self):
4883 self.assertTrue(self.adapter.hasHandlers())
4884
4885 for handler in self.logger.handlers:
4886 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004887
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004888 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004889 self.assertFalse(self.adapter.hasHandlers())
4890
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004891 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004892 class Adapter(logging.LoggerAdapter):
4893 prefix = 'Adapter'
4894
4895 def process(self, msg, kwargs):
4896 return f"{self.prefix} {msg}", kwargs
4897
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004898 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004899 adapter = Adapter(logger=self.logger, extra=None)
4900 adapter_adapter = Adapter(logger=adapter, extra=None)
4901 adapter_adapter.prefix = 'AdapterAdapter'
4902 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004903 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004904 self.assertEqual(len(self.recording.records), 1)
4905 record = self.recording.records[0]
4906 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004907 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004908 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004909 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004910 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004911 self.assertIs(self.logger.manager, orig_manager)
4912 temp_manager = object()
4913 try:
4914 adapter_adapter.manager = temp_manager
4915 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004916 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004917 self.assertIs(self.logger.manager, temp_manager)
4918 finally:
4919 adapter_adapter.manager = orig_manager
4920 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004921 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004922 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004923
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004924
Eric Larson9fdb76c2020-09-25 14:08:50 -04004925class LoggerTest(BaseTest, AssertErrorMessage):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004926
4927 def setUp(self):
4928 super(LoggerTest, self).setUp()
4929 self.recording = RecordingHandler()
4930 self.logger = logging.Logger(name='blah')
4931 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004932 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004933 self.addCleanup(self.recording.close)
4934 self.addCleanup(logging.shutdown)
4935
4936 def test_set_invalid_level(self):
Eric Larson9fdb76c2020-09-25 14:08:50 -04004937 self.assert_error_message(
4938 TypeError, 'Level not an integer or a valid string: None',
4939 self.logger.setLevel, None)
4940 self.assert_error_message(
4941 TypeError, 'Level not an integer or a valid string: (0, 0)',
4942 self.logger.setLevel, (0, 0))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004943
4944 def test_exception(self):
4945 msg = 'testing exception: %r'
4946 exc = None
4947 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004948 1 / 0
4949 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004950 exc = e
4951 self.logger.exception(msg, self.recording)
4952
4953 self.assertEqual(len(self.recording.records), 1)
4954 record = self.recording.records[0]
4955 self.assertEqual(record.levelno, logging.ERROR)
4956 self.assertEqual(record.msg, msg)
4957 self.assertEqual(record.args, (self.recording,))
4958 self.assertEqual(record.exc_info,
4959 (exc.__class__, exc, exc.__traceback__))
4960
4961 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004962 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004963 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004964
4965 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004966 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004967 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004968
4969 def test_find_caller_with_stack_info(self):
4970 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004971 support.patch(self, logging.traceback, 'print_stack',
4972 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004973
4974 self.logger.findCaller(stack_info=True)
4975
4976 self.assertEqual(len(called), 1)
4977 self.assertEqual('Stack (most recent call last):\n', called[0])
4978
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004979 def test_find_caller_with_stacklevel(self):
4980 the_level = 1
4981
4982 def innermost():
4983 self.logger.warning('test', stacklevel=the_level)
4984
4985 def inner():
4986 innermost()
4987
4988 def outer():
4989 inner()
4990
4991 records = self.recording.records
4992 outer()
4993 self.assertEqual(records[-1].funcName, 'innermost')
4994 lineno = records[-1].lineno
4995 the_level += 1
4996 outer()
4997 self.assertEqual(records[-1].funcName, 'inner')
4998 self.assertGreater(records[-1].lineno, lineno)
4999 lineno = records[-1].lineno
5000 the_level += 1
5001 outer()
5002 self.assertEqual(records[-1].funcName, 'outer')
5003 self.assertGreater(records[-1].lineno, lineno)
5004 lineno = records[-1].lineno
5005 the_level += 1
5006 outer()
5007 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5008 self.assertGreater(records[-1].lineno, lineno)
5009
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005010 def test_make_record_with_extra_overwrite(self):
5011 name = 'my record'
5012 level = 13
5013 fn = lno = msg = args = exc_info = func = sinfo = None
5014 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
5015 exc_info, func, sinfo)
5016
5017 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
5018 extra = {key: 'some value'}
5019 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
5020 fn, lno, msg, args, exc_info,
5021 extra=extra, sinfo=sinfo)
5022
5023 def test_make_record_with_extra_no_overwrite(self):
5024 name = 'my record'
5025 level = 13
5026 fn = lno = msg = args = exc_info = func = sinfo = None
5027 extra = {'valid_key': 'some value'}
5028 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
5029 exc_info, extra=extra, sinfo=sinfo)
5030 self.assertIn('valid_key', result.__dict__)
5031
5032 def test_has_handlers(self):
5033 self.assertTrue(self.logger.hasHandlers())
5034
5035 for handler in self.logger.handlers:
5036 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005037 self.assertFalse(self.logger.hasHandlers())
5038
5039 def test_has_handlers_no_propagate(self):
5040 child_logger = logging.getLogger('blah.child')
5041 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005042 self.assertFalse(child_logger.hasHandlers())
5043
5044 def test_is_enabled_for(self):
5045 old_disable = self.logger.manager.disable
5046 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01005047 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005048 self.assertFalse(self.logger.isEnabledFor(22))
5049
Timo Furrer6e3ca642018-06-01 09:29:46 +02005050 def test_is_enabled_for_disabled_logger(self):
5051 old_disabled = self.logger.disabled
5052 old_disable = self.logger.manager.disable
5053
5054 self.logger.disabled = True
5055 self.logger.manager.disable = 21
5056
5057 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
5058 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
5059
5060 self.assertFalse(self.logger.isEnabledFor(22))
5061
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005062 def test_root_logger_aliases(self):
5063 root = logging.getLogger()
5064 self.assertIs(root, logging.root)
5065 self.assertIs(root, logging.getLogger(None))
5066 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01005067 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005068 self.assertIs(root, logging.getLogger('foo').root)
5069 self.assertIs(root, logging.getLogger('foo.bar').root)
5070 self.assertIs(root, logging.getLogger('foo').parent)
5071
5072 self.assertIsNot(root, logging.getLogger('\0'))
5073 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
5074
5075 def test_invalid_names(self):
5076 self.assertRaises(TypeError, logging.getLogger, any)
5077 self.assertRaises(TypeError, logging.getLogger, b'foo')
5078
Vinay Sajip6260d9f2017-06-06 16:34:29 +01005079 def test_pickling(self):
5080 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
5081 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
5082 logger = logging.getLogger(name)
5083 s = pickle.dumps(logger, proto)
5084 unpickled = pickle.loads(s)
5085 self.assertIs(unpickled, logger)
5086
Avram Lubkin78c18a92017-07-30 05:36:33 -04005087 def test_caching(self):
5088 root = self.root_logger
5089 logger1 = logging.getLogger("abc")
5090 logger2 = logging.getLogger("abc.def")
5091
5092 # Set root logger level and ensure cache is empty
5093 root.setLevel(logging.ERROR)
5094 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
5095 self.assertEqual(logger2._cache, {})
5096
5097 # Ensure cache is populated and calls are consistent
5098 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5099 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
5100 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
5101 self.assertEqual(root._cache, {})
5102 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5103
5104 # Ensure root cache gets populated
5105 self.assertEqual(root._cache, {})
5106 self.assertTrue(root.isEnabledFor(logging.ERROR))
5107 self.assertEqual(root._cache, {logging.ERROR: True})
5108
5109 # Set parent logger level and ensure caches are emptied
5110 logger1.setLevel(logging.CRITICAL)
5111 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5112 self.assertEqual(logger2._cache, {})
5113
5114 # Ensure logger2 uses parent logger's effective level
5115 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5116
5117 # Set level to NOTSET and ensure caches are empty
5118 logger2.setLevel(logging.NOTSET)
5119 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5120 self.assertEqual(logger2._cache, {})
5121 self.assertEqual(logger1._cache, {})
5122 self.assertEqual(root._cache, {})
5123
5124 # Verify logger2 follows parent and not root
5125 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5126 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
5127 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
5128 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
5129 self.assertTrue(root.isEnabledFor(logging.ERROR))
5130
5131 # Disable logging in manager and ensure caches are clear
5132 logging.disable()
5133 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5134 self.assertEqual(logger2._cache, {})
5135 self.assertEqual(logger1._cache, {})
5136 self.assertEqual(root._cache, {})
5137
5138 # Ensure no loggers are enabled
5139 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5140 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5141 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5142
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005143
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005144class BaseFileTest(BaseTest):
5145 "Base class for handler tests that write log files"
5146
5147 def setUp(self):
5148 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005149 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5150 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005151 self.rmfiles = []
5152
5153 def tearDown(self):
5154 for fn in self.rmfiles:
5155 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005156 if os.path.exists(self.fn):
5157 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005158 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005159
5160 def assertLogFile(self, filename):
5161 "Assert a log file is there and register it for deletion"
5162 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005163 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005164 self.rmfiles.append(filename)
5165
5166
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005167class FileHandlerTest(BaseFileTest):
5168 def test_delay(self):
5169 os.unlink(self.fn)
Inada Naokifb786922021-04-06 11:18:41 +09005170 fh = logging.FileHandler(self.fn, encoding='utf-8', delay=True)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005171 self.assertIsNone(fh.stream)
5172 self.assertFalse(os.path.exists(self.fn))
5173 fh.handle(logging.makeLogRecord({}))
5174 self.assertIsNotNone(fh.stream)
5175 self.assertTrue(os.path.exists(self.fn))
5176 fh.close()
5177
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005178class RotatingFileHandlerTest(BaseFileTest):
5179 def next_rec(self):
5180 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5181 self.next_message(), None, None, None)
5182
5183 def test_should_not_rollover(self):
5184 # If maxbytes is zero rollover never occurs
Inada Naokifb786922021-04-06 11:18:41 +09005185 rh = logging.handlers.RotatingFileHandler(
5186 self.fn, encoding="utf-8", maxBytes=0)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005187 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005188 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005189
5190 def test_should_rollover(self):
Inada Naokifb786922021-04-06 11:18:41 +09005191 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8", maxBytes=1)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005192 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005193 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005194
5195 def test_file_created(self):
5196 # checks that the file is created and assumes it was created
5197 # by us
Inada Naokifb786922021-04-06 11:18:41 +09005198 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8")
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005199 rh.emit(self.next_rec())
5200 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005201 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005202
5203 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005204 def namer(name):
5205 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005206 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005207 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005208 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005209 rh.emit(self.next_rec())
5210 self.assertLogFile(self.fn)
5211 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005212 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005213 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005214 self.assertLogFile(namer(self.fn + ".2"))
5215 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5216 rh.close()
5217
l0rb519cb872019-11-06 22:21:40 +01005218 def test_namer_rotator_inheritance(self):
5219 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5220 def namer(self, name):
5221 return name + ".test"
5222
5223 def rotator(self, source, dest):
5224 if os.path.exists(source):
Victor Stinnerfe52eb62021-04-28 15:47:10 +02005225 os.replace(source, dest + ".rotated")
l0rb519cb872019-11-06 22:21:40 +01005226
5227 rh = HandlerWithNamerAndRotator(
Inada Naokifb786922021-04-06 11:18:41 +09005228 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
l0rb519cb872019-11-06 22:21:40 +01005229 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5230 rh.emit(self.next_rec())
5231 self.assertLogFile(self.fn)
5232 rh.emit(self.next_rec())
5233 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5234 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5235 rh.close()
5236
Hai Shia3ec3ad2020-05-19 06:02:57 +08005237 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005238 def test_rotator(self):
5239 def namer(name):
5240 return name + ".gz"
5241
5242 def rotator(source, dest):
5243 with open(source, "rb") as sf:
5244 data = sf.read()
5245 compressed = zlib.compress(data, 9)
5246 with open(dest, "wb") as df:
5247 df.write(compressed)
5248 os.remove(source)
5249
5250 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005251 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005252 rh.rotator = rotator
5253 rh.namer = namer
5254 m1 = self.next_rec()
5255 rh.emit(m1)
5256 self.assertLogFile(self.fn)
5257 m2 = self.next_rec()
5258 rh.emit(m2)
5259 fn = namer(self.fn + ".1")
5260 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005261 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005262 with open(fn, "rb") as f:
5263 compressed = f.read()
5264 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005265 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005266 rh.emit(self.next_rec())
5267 fn = namer(self.fn + ".2")
5268 self.assertLogFile(fn)
5269 with open(fn, "rb") as f:
5270 compressed = f.read()
5271 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005272 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005273 rh.emit(self.next_rec())
5274 fn = namer(self.fn + ".2")
5275 with open(fn, "rb") as f:
5276 compressed = f.read()
5277 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005278 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005279 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005280 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005281
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005282class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005283 # other test methods added below
5284 def test_rollover(self):
Inada Naokifa51c0c2021-04-29 11:34:56 +09005285 fh = logging.handlers.TimedRotatingFileHandler(
5286 self.fn, 'S', encoding="utf-8", backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005287 fmt = logging.Formatter('%(asctime)s %(message)s')
5288 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005289 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005290 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005291 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005292 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005293 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005294 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005295 fh.close()
5296 # At this point, we should have a recent rotated file which we
5297 # can test for the existence of. However, in practice, on some
5298 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005299 # in time to go to look for the log file. So, we go back a fair
5300 # bit, and stop as soon as we see a rotated file. In theory this
5301 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005302 found = False
5303 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005304 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005305 for secs in range(GO_BACK):
5306 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005307 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5308 found = os.path.exists(fn)
5309 if found:
5310 self.rmfiles.append(fn)
5311 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005312 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5313 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005314 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005315 dn, fn = os.path.split(self.fn)
5316 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005317 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5318 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005319 for f in files:
5320 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005321 path = os.path.join(dn, f)
5322 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005323 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005324 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005325
Vinay Sajip0372e102011-05-05 12:59:14 +01005326 def test_invalid(self):
5327 assertRaises = self.assertRaises
5328 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005329 self.fn, 'X', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005330 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005331 self.fn, 'W', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005332 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005333 self.fn, 'W7', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005334
Vinay Sajipa7130792013-04-12 17:04:23 +01005335 def test_compute_rollover_daily_attime(self):
5336 currentTime = 0
5337 atTime = datetime.time(12, 0, 0)
5338 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005339 self.fn, encoding="utf-8", when='MIDNIGHT', interval=1, backupCount=0,
5340 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005341 try:
5342 actual = rh.computeRollover(currentTime)
5343 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005344
Vinay Sajipd86ac962013-04-14 12:20:46 +01005345 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5346 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5347 finally:
5348 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005349
Vinay Sajip10e8c492013-05-18 10:19:54 -07005350 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005351 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005352 currentTime = int(time.time())
5353 today = currentTime - currentTime % 86400
5354
Vinay Sajipa7130792013-04-12 17:04:23 +01005355 atTime = datetime.time(12, 0, 0)
5356
Vinay Sajip10e8c492013-05-18 10:19:54 -07005357 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005358 for day in range(7):
5359 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005360 self.fn, encoding="utf-8", when='W%d' % day, interval=1, backupCount=0,
5361 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005362 try:
5363 if wday > day:
5364 # The rollover day has already passed this week, so we
5365 # go over into next week
5366 expected = (7 - wday + day)
5367 else:
5368 expected = (day - wday)
5369 # At this point expected is in days from now, convert to seconds
5370 expected *= 24 * 60 * 60
5371 # Add in the rollover time
5372 expected += 12 * 60 * 60
5373 # Add in adjustment for today
5374 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005375 actual = rh.computeRollover(today)
5376 if actual != expected:
5377 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005378 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005379 self.assertEqual(actual, expected)
5380 if day == wday:
5381 # goes into following week
5382 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005383 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005384 if actual != expected:
5385 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005386 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005387 self.assertEqual(actual, expected)
5388 finally:
5389 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005390
5391
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005392def secs(**kw):
5393 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5394
5395for when, exp in (('S', 1),
5396 ('M', 60),
5397 ('H', 60 * 60),
5398 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005399 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005400 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005401 ('W0', secs(days=4, hours=24)),
5402 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005403 def test_compute_rollover(self, when=when, exp=exp):
5404 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005405 self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
Vinay Sajiped0473c2011-02-26 15:35:38 +00005406 currentTime = 0.0
5407 actual = rh.computeRollover(currentTime)
5408 if exp != actual:
5409 # Failures occur on some systems for MIDNIGHT and W0.
5410 # Print detailed calculation for MIDNIGHT so we can try to see
5411 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005412 if when == 'MIDNIGHT':
5413 try:
5414 if rh.utc:
5415 t = time.gmtime(currentTime)
5416 else:
5417 t = time.localtime(currentTime)
5418 currentHour = t[3]
5419 currentMinute = t[4]
5420 currentSecond = t[5]
5421 # r is the number of seconds left between now and midnight
5422 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5423 currentMinute) * 60 +
5424 currentSecond)
5425 result = currentTime + r
5426 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5427 print('currentHour: %s' % currentHour, file=sys.stderr)
5428 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5429 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5430 print('r: %s' % r, file=sys.stderr)
5431 print('result: %s' % result, file=sys.stderr)
5432 except Exception:
5433 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5434 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005435 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005436 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5437
Vinay Sajip60ccd822011-05-09 17:32:09 +01005438
Vinay Sajip223349c2015-10-01 20:37:54 +01005439@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005440class NTEventLogHandlerTest(BaseTest):
5441 def test_basic(self):
5442 logtype = 'Application'
5443 elh = win32evtlog.OpenEventLog(None, logtype)
5444 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005445
5446 try:
5447 h = logging.handlers.NTEventLogHandler('test_logging')
5448 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005449 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005450 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005451 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005452
Vinay Sajip60ccd822011-05-09 17:32:09 +01005453 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5454 h.handle(r)
5455 h.close()
5456 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005457 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005458 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5459 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5460 found = False
5461 GO_BACK = 100
5462 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5463 for e in events:
5464 if e.SourceName != 'test_logging':
5465 continue
5466 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5467 if msg != 'Test Log Message\r\n':
5468 continue
5469 found = True
5470 break
5471 msg = 'Record not found in event log, went back %d records' % GO_BACK
5472 self.assertTrue(found, msg=msg)
5473
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005474
5475class MiscTestCase(unittest.TestCase):
5476 def test__all__(self):
Victor Stinnerfbf43f02020-08-17 07:20:40 +02005477 not_exported = {
5478 'logThreads', 'logMultiprocessing', 'logProcesses', 'currentframe',
5479 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5480 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', 'root',
5481 'threading'}
5482 support.check__all__(self, logging, not_exported=not_exported)
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005483
5484
Christian Heimes180510d2008-03-03 19:15:45 +00005485# Set the locale to the platform-dependent default. I have no idea
5486# why the test does this, but in any case we save the current locale
5487# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005488@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005489def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005490 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005491 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5492 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5493 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5494 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5495 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5496 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5497 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5498 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005499 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005500 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005501 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005502 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005503 ]
5504 if hasattr(logging.handlers, 'QueueListener'):
5505 tests.append(QueueListenerTest)
5506 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005507
Christian Heimes180510d2008-03-03 19:15:45 +00005508if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005509 test_main()