blob: 0f1d2745dddbbb10653d96330b52670a3b6042f4 [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 Naokifa51c0c2021-04-29 11:34:56 +09001585 #with self.check_no_resource_warning():
1586 if 1:
Xtreak087570a2018-07-02 14:27:46 +05301587 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1588 os.close(fd)
1589
1590 # Replace single backslash with double backslash in windows
1591 # to avoid unicode error during string formatting
1592 if os.name == "nt":
1593 fn = fn.replace("\\", "\\\\")
1594
1595 config8 = self.config8.format(tempfile=fn)
1596
1597 self.apply_config(config8)
1598 self.apply_config(config8)
1599
1600 handler = logging.root.handlers[0]
1601 self.addCleanup(cleanup, handler, fn)
1602
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001603 def test_logger_disabling(self):
1604 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001605 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001606 self.assertFalse(logger.disabled)
1607 self.apply_config(self.disable_test)
1608 self.assertTrue(logger.disabled)
1609 self.apply_config(self.disable_test, disable_existing_loggers=False)
1610 self.assertFalse(logger.disabled)
1611
Lucas Cimonb15100f2019-10-31 09:06:25 +01001612 def test_config_set_handler_names(self):
1613 test_config = """
1614 [loggers]
1615 keys=root
1616
1617 [handlers]
1618 keys=hand1
1619
1620 [formatters]
1621 keys=form1
1622
1623 [logger_root]
1624 handlers=hand1
1625
1626 [handler_hand1]
1627 class=StreamHandler
1628 formatter=form1
1629
1630 [formatter_form1]
1631 format=%(levelname)s ++ %(message)s
1632 """
1633 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001634 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001635
Łukasz Langa214f18e2018-06-08 04:02:48 -07001636 def test_defaults_do_no_interpolation(self):
1637 """bpo-33802 defaults should not get interpolated"""
1638 ini = textwrap.dedent("""
1639 [formatters]
1640 keys=default
1641
1642 [formatter_default]
1643
1644 [handlers]
1645 keys=console
1646
1647 [handler_console]
1648 class=logging.StreamHandler
1649 args=tuple()
1650
1651 [loggers]
1652 keys=root
1653
1654 [logger_root]
1655 formatter=default
1656 handlers=console
1657 """).strip()
1658 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1659 try:
1660 os.write(fd, ini.encode('ascii'))
1661 os.close(fd)
1662 logging.config.fileConfig(
1663 fn,
Inada Naokifa51c0c2021-04-29 11:34:56 +09001664 encoding="utf-8",
Łukasz Langa214f18e2018-06-08 04:02:48 -07001665 defaults=dict(
1666 version=1,
1667 disable_existing_loggers=False,
1668 formatters={
1669 "generic": {
1670 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1671 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1672 "class": "logging.Formatter"
1673 },
1674 },
1675 )
1676 )
1677 finally:
1678 os.unlink(fn)
1679
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001680
Christian Heimes180510d2008-03-03 19:15:45 +00001681class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001682
Christian Heimes180510d2008-03-03 19:15:45 +00001683 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001684
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001685 server_class = TestTCPServer
1686 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001687
Christian Heimes180510d2008-03-03 19:15:45 +00001688 def setUp(self):
1689 """Set up a TCP server to receive log messages, and a SocketHandler
1690 pointing to that server's address and port."""
1691 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001692 # Issue #29177: deal with errors that happen during setup
1693 self.server = self.sock_hdlr = self.server_exception = None
1694 try:
1695 self.server = server = self.server_class(self.address,
1696 self.handle_socket, 0.01)
1697 server.start()
1698 # Uncomment next line to test error recovery in setUp()
1699 # raise OSError('dummy error raised')
1700 except OSError as e:
1701 self.server_exception = e
1702 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001703 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001704 hcls = logging.handlers.SocketHandler
1705 if isinstance(server.server_address, tuple):
1706 self.sock_hdlr = hcls('localhost', server.port)
1707 else:
1708 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001709 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001710 self.root_logger.removeHandler(self.root_logger.handlers[0])
1711 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001712 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001713
Christian Heimes180510d2008-03-03 19:15:45 +00001714 def tearDown(self):
1715 """Shutdown the TCP server."""
1716 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001717 if self.sock_hdlr:
1718 self.root_logger.removeHandler(self.sock_hdlr)
1719 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001720 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001721 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001722 finally:
1723 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001724
Vinay Sajip7367d082011-05-02 13:17:27 +01001725 def handle_socket(self, request):
1726 conn = request.connection
1727 while True:
1728 chunk = conn.recv(4)
1729 if len(chunk) < 4:
1730 break
1731 slen = struct.unpack(">L", chunk)[0]
1732 chunk = conn.recv(slen)
1733 while len(chunk) < slen:
1734 chunk = chunk + conn.recv(slen - len(chunk))
1735 obj = pickle.loads(chunk)
1736 record = logging.makeLogRecord(obj)
1737 self.log_output += record.msg + '\n'
1738 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001739
Christian Heimes180510d2008-03-03 19:15:45 +00001740 def test_output(self):
1741 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001742 if self.server_exception:
1743 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001744 logger = logging.getLogger("tcp")
1745 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001746 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001747 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001748 self.handled.acquire()
1749 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001750
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001751 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001752 if self.server_exception:
1753 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001754 # Avoid timing-related failures due to SocketHandler's own hard-wired
1755 # one-second timeout on socket.create_connection() (issue #16264).
1756 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001757 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001758 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001759 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001760 try:
1761 raise RuntimeError('Deliberate mistake')
1762 except RuntimeError:
1763 self.root_logger.exception('Never sent')
1764 self.root_logger.error('Never sent, either')
1765 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001766 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001767 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1768 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001769
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001770def _get_temp_domain_socket():
1771 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1772 os.close(fd)
1773 # just need a name - file can't be present, or we'll get an
1774 # 'address already in use' error.
1775 os.remove(fn)
1776 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001777
Victor Stinnerec5a8602014-06-02 14:41:51 +02001778@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001779class UnixSocketHandlerTest(SocketHandlerTest):
1780
1781 """Test for SocketHandler with unix sockets."""
1782
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001783 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001784 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001785
1786 def setUp(self):
1787 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001788 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001789 SocketHandlerTest.setUp(self)
1790
1791 def tearDown(self):
1792 SocketHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001793 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001794
Vinay Sajip7367d082011-05-02 13:17:27 +01001795class DatagramHandlerTest(BaseTest):
1796
1797 """Test for DatagramHandler."""
1798
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001799 server_class = TestUDPServer
1800 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001801
Vinay Sajip7367d082011-05-02 13:17:27 +01001802 def setUp(self):
1803 """Set up a UDP server to receive log messages, and a DatagramHandler
1804 pointing to that server's address and port."""
1805 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001806 # Issue #29177: deal with errors that happen during setup
1807 self.server = self.sock_hdlr = self.server_exception = None
1808 try:
1809 self.server = server = self.server_class(self.address,
1810 self.handle_datagram, 0.01)
1811 server.start()
1812 # Uncomment next line to test error recovery in setUp()
1813 # raise OSError('dummy error raised')
1814 except OSError as e:
1815 self.server_exception = e
1816 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001817 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001818 hcls = logging.handlers.DatagramHandler
1819 if isinstance(server.server_address, tuple):
1820 self.sock_hdlr = hcls('localhost', server.port)
1821 else:
1822 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001823 self.log_output = ''
1824 self.root_logger.removeHandler(self.root_logger.handlers[0])
1825 self.root_logger.addHandler(self.sock_hdlr)
1826 self.handled = threading.Event()
1827
1828 def tearDown(self):
1829 """Shutdown the UDP server."""
1830 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001831 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001832 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001833 if self.sock_hdlr:
1834 self.root_logger.removeHandler(self.sock_hdlr)
1835 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001836 finally:
1837 BaseTest.tearDown(self)
1838
1839 def handle_datagram(self, request):
1840 slen = struct.pack('>L', 0) # length of prefix
1841 packet = request.packet[len(slen):]
1842 obj = pickle.loads(packet)
1843 record = logging.makeLogRecord(obj)
1844 self.log_output += record.msg + '\n'
1845 self.handled.set()
1846
1847 def test_output(self):
1848 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001849 if self.server_exception:
1850 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001851 logger = logging.getLogger("udp")
1852 logger.error("spam")
1853 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001854 self.handled.clear()
1855 logger.error("eggs")
1856 self.handled.wait()
1857 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001858
Victor Stinnerec5a8602014-06-02 14:41:51 +02001859@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001860class UnixDatagramHandlerTest(DatagramHandlerTest):
1861
1862 """Test for DatagramHandler using Unix sockets."""
1863
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001864 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001865 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001866
1867 def setUp(self):
1868 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001869 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001870 DatagramHandlerTest.setUp(self)
1871
1872 def tearDown(self):
1873 DatagramHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001874 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001875
Vinay Sajip7367d082011-05-02 13:17:27 +01001876class SysLogHandlerTest(BaseTest):
1877
1878 """Test for SysLogHandler using UDP."""
1879
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001880 server_class = TestUDPServer
1881 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001882
Vinay Sajip7367d082011-05-02 13:17:27 +01001883 def setUp(self):
1884 """Set up a UDP server to receive log messages, and a SysLogHandler
1885 pointing to that server's address and port."""
1886 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001887 # Issue #29177: deal with errors that happen during setup
1888 self.server = self.sl_hdlr = self.server_exception = None
1889 try:
1890 self.server = server = self.server_class(self.address,
1891 self.handle_datagram, 0.01)
1892 server.start()
1893 # Uncomment next line to test error recovery in setUp()
1894 # raise OSError('dummy error raised')
1895 except OSError as e:
1896 self.server_exception = e
1897 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001898 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001899 hcls = logging.handlers.SysLogHandler
1900 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001901 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001902 else:
1903 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001904 self.log_output = ''
1905 self.root_logger.removeHandler(self.root_logger.handlers[0])
1906 self.root_logger.addHandler(self.sl_hdlr)
1907 self.handled = threading.Event()
1908
1909 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001910 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001911 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001912 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001913 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001914 if self.sl_hdlr:
1915 self.root_logger.removeHandler(self.sl_hdlr)
1916 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001917 finally:
1918 BaseTest.tearDown(self)
1919
1920 def handle_datagram(self, request):
1921 self.log_output = request.packet
1922 self.handled.set()
1923
1924 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001925 if self.server_exception:
1926 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001927 # The log message sent to the SysLogHandler is properly received.
1928 logger = logging.getLogger("slh")
1929 logger.error("sp\xe4m")
1930 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001931 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001932 self.handled.clear()
1933 self.sl_hdlr.append_nul = False
1934 logger.error("sp\xe4m")
1935 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001936 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001937 self.handled.clear()
1938 self.sl_hdlr.ident = "h\xe4m-"
1939 logger.error("sp\xe4m")
1940 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001941 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001942
Victor Stinnerec5a8602014-06-02 14:41:51 +02001943@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001944class UnixSysLogHandlerTest(SysLogHandlerTest):
1945
1946 """Test for SysLogHandler with Unix sockets."""
1947
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001948 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001949 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001950
1951 def setUp(self):
1952 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001953 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001954 SysLogHandlerTest.setUp(self)
1955
1956 def tearDown(self):
1957 SysLogHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001958 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001959
Serhiy Storchaka16994912020-04-25 10:06:29 +03001960@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001961 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001962class IPv6SysLogHandlerTest(SysLogHandlerTest):
1963
1964 """Test for SysLogHandler with IPv6 host."""
1965
1966 server_class = TestUDPServer
1967 address = ('::1', 0)
1968
1969 def setUp(self):
1970 self.server_class.address_family = socket.AF_INET6
1971 super(IPv6SysLogHandlerTest, self).setUp()
1972
1973 def tearDown(self):
1974 self.server_class.address_family = socket.AF_INET
1975 super(IPv6SysLogHandlerTest, self).tearDown()
1976
Vinay Sajip7367d082011-05-02 13:17:27 +01001977class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001978 """Test for HTTPHandler."""
1979
1980 def setUp(self):
1981 """Set up an HTTP server to receive log messages, and a HTTPHandler
1982 pointing to that server's address and port."""
1983 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001984 self.handled = threading.Event()
1985
Vinay Sajip7367d082011-05-02 13:17:27 +01001986 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001987 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001988 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001989 if self.command == 'POST':
1990 try:
1991 rlen = int(request.headers['Content-Length'])
1992 self.post_data = request.rfile.read(rlen)
1993 except:
1994 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001995 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001996 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001997 self.handled.set()
1998
1999 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01002000 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01002001 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01002002 root_logger = self.root_logger
2003 root_logger.removeHandler(self.root_logger.handlers[0])
2004 for secure in (False, True):
2005 addr = ('localhost', 0)
2006 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01002007 try:
2008 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01002009 except ImportError:
2010 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002011 else:
2012 here = os.path.dirname(__file__)
2013 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02002014 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002015 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06002016
2017 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01002018 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01002019 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06002020 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01002021 self.server = server = TestHTTPServer(addr, self.handle_request,
2022 0.01, sslctx=sslctx)
2023 server.start()
2024 server.ready.wait()
2025 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01002026 secure_client = secure and sslctx
2027 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06002028 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01002029 context=context,
2030 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01002031 self.log_data = None
2032 root_logger.addHandler(self.h_hdlr)
2033
2034 for method in ('GET', 'POST'):
2035 self.h_hdlr.method = method
2036 self.handled.clear()
2037 msg = "sp\xe4m"
2038 logger.error(msg)
2039 self.handled.wait()
2040 self.assertEqual(self.log_data.path, '/frob')
2041 self.assertEqual(self.command, method)
2042 if method == 'GET':
2043 d = parse_qs(self.log_data.query)
2044 else:
2045 d = parse_qs(self.post_data.decode('utf-8'))
2046 self.assertEqual(d['name'], ['http'])
2047 self.assertEqual(d['funcName'], ['test_output'])
2048 self.assertEqual(d['msg'], [msg])
2049
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002050 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002051 self.root_logger.removeHandler(self.h_hdlr)
2052 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002053
Christian Heimes180510d2008-03-03 19:15:45 +00002054class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002055
Christian Heimes180510d2008-03-03 19:15:45 +00002056 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002057
Christian Heimes180510d2008-03-03 19:15:45 +00002058 def setUp(self):
2059 """Create a dict to remember potentially destroyed objects."""
2060 BaseTest.setUp(self)
2061 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002062
Christian Heimes180510d2008-03-03 19:15:45 +00002063 def _watch_for_survival(self, *args):
2064 """Watch the given objects for survival, by creating weakrefs to
2065 them."""
2066 for obj in args:
2067 key = id(obj), repr(obj)
2068 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002069
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002070 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002071 """Assert that all objects watched for survival have survived."""
2072 # Trigger cycle breaking.
2073 gc.collect()
2074 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002075 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002076 if ref() is None:
2077 dead.append(repr_)
2078 if dead:
2079 self.fail("%d objects should have survived "
2080 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002081
Christian Heimes180510d2008-03-03 19:15:45 +00002082 def test_persistent_loggers(self):
2083 # Logger objects are persistent and retain their configuration, even
2084 # if visible references are destroyed.
2085 self.root_logger.setLevel(logging.INFO)
2086 foo = logging.getLogger("foo")
2087 self._watch_for_survival(foo)
2088 foo.setLevel(logging.DEBUG)
2089 self.root_logger.debug(self.next_message())
2090 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002091 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002092 ('foo', 'DEBUG', '2'),
2093 ])
2094 del foo
2095 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002096 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002097 # foo has retained its settings.
2098 bar = logging.getLogger("foo")
2099 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002100 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002101 ('foo', 'DEBUG', '2'),
2102 ('foo', 'DEBUG', '3'),
2103 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002104
Benjamin Petersonf91df042009-02-13 02:50:59 +00002105
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002106class EncodingTest(BaseTest):
2107 def test_encoding_plain_file(self):
2108 # In Python 2.x, a plain file object is treated as having no encoding.
2109 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002110 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2111 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002112 # the non-ascii data we write to the log.
2113 data = "foo\x80"
2114 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002115 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002116 log.addHandler(handler)
2117 try:
2118 # write non-ascii data to the log.
2119 log.warning(data)
2120 finally:
2121 log.removeHandler(handler)
2122 handler.close()
2123 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002124 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002125 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002126 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002127 finally:
2128 f.close()
2129 finally:
2130 if os.path.isfile(fn):
2131 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002132
Benjamin Petersonf91df042009-02-13 02:50:59 +00002133 def test_encoding_cyrillic_unicode(self):
2134 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002135 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002136 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002137 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002138 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002139 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002140 stream = io.BytesIO()
2141 writer = writer_class(stream, 'strict')
2142 handler = logging.StreamHandler(writer)
2143 log.addHandler(handler)
2144 try:
2145 log.warning(message)
2146 finally:
2147 log.removeHandler(handler)
2148 handler.close()
2149 # check we wrote exactly those bytes, ignoring trailing \n etc
2150 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002151 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002152 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2153
2154
Georg Brandlf9734072008-12-07 15:30:06 +00002155class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002156
Georg Brandlf9734072008-12-07 15:30:06 +00002157 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002158 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002159 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002160 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002161 warnings.filterwarnings("always", category=UserWarning)
2162 stream = io.StringIO()
2163 h = logging.StreamHandler(stream)
2164 logger = logging.getLogger("py.warnings")
2165 logger.addHandler(h)
2166 warnings.warn("I'm warning you...")
2167 logger.removeHandler(h)
2168 s = stream.getvalue()
2169 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002170 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002171
Mike53f7a7c2017-12-14 14:04:53 +03002172 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002173 a_file = io.StringIO()
2174 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2175 a_file, "Dummy line")
2176 s = a_file.getvalue()
2177 a_file.close()
2178 self.assertEqual(s,
2179 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2180
2181 def test_warnings_no_handlers(self):
2182 with warnings.catch_warnings():
2183 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002184 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002185
2186 # confirm our assumption: no loggers are set
2187 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002188 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002189
2190 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002191 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002192 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002193
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002194
2195def formatFunc(format, datefmt=None):
2196 return logging.Formatter(format, datefmt)
2197
BNMetrics18fb1fb2018-10-15 19:41:36 +01002198class myCustomFormatter:
2199 def __init__(self, fmt, datefmt=None):
2200 pass
2201
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002202def handlerFunc():
2203 return logging.StreamHandler()
2204
2205class CustomHandler(logging.StreamHandler):
2206 pass
2207
2208class ConfigDictTest(BaseTest):
2209
2210 """Reading logging config from a dictionary."""
2211
Hai Shi3ddc6342020-06-30 21:46:06 +08002212 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002213 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002214
2215 # config0 is a standard configuration.
2216 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002217 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002218 'formatters': {
2219 'form1' : {
2220 'format' : '%(levelname)s ++ %(message)s',
2221 },
2222 },
2223 'handlers' : {
2224 'hand1' : {
2225 'class' : 'logging.StreamHandler',
2226 'formatter' : 'form1',
2227 'level' : 'NOTSET',
2228 'stream' : 'ext://sys.stdout',
2229 },
2230 },
2231 'root' : {
2232 'level' : 'WARNING',
2233 'handlers' : ['hand1'],
2234 },
2235 }
2236
2237 # config1 adds a little to the standard configuration.
2238 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002239 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002240 'formatters': {
2241 'form1' : {
2242 'format' : '%(levelname)s ++ %(message)s',
2243 },
2244 },
2245 'handlers' : {
2246 'hand1' : {
2247 'class' : 'logging.StreamHandler',
2248 'formatter' : 'form1',
2249 'level' : 'NOTSET',
2250 'stream' : 'ext://sys.stdout',
2251 },
2252 },
2253 'loggers' : {
2254 'compiler.parser' : {
2255 'level' : 'DEBUG',
2256 'handlers' : ['hand1'],
2257 },
2258 },
2259 'root' : {
2260 'level' : 'WARNING',
2261 },
2262 }
2263
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002264 # config1a moves the handler to the root. Used with config8a
2265 config1a = {
2266 'version': 1,
2267 'formatters': {
2268 'form1' : {
2269 'format' : '%(levelname)s ++ %(message)s',
2270 },
2271 },
2272 'handlers' : {
2273 'hand1' : {
2274 'class' : 'logging.StreamHandler',
2275 'formatter' : 'form1',
2276 'level' : 'NOTSET',
2277 'stream' : 'ext://sys.stdout',
2278 },
2279 },
2280 'loggers' : {
2281 'compiler.parser' : {
2282 'level' : 'DEBUG',
2283 },
2284 },
2285 'root' : {
2286 'level' : 'WARNING',
2287 'handlers' : ['hand1'],
2288 },
2289 }
2290
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002291 # config2 has a subtle configuration error that should be reported
2292 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002293 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002294 'formatters': {
2295 'form1' : {
2296 'format' : '%(levelname)s ++ %(message)s',
2297 },
2298 },
2299 'handlers' : {
2300 'hand1' : {
2301 'class' : 'logging.StreamHandler',
2302 'formatter' : 'form1',
2303 'level' : 'NOTSET',
2304 'stream' : 'ext://sys.stdbout',
2305 },
2306 },
2307 'loggers' : {
2308 'compiler.parser' : {
2309 'level' : 'DEBUG',
2310 'handlers' : ['hand1'],
2311 },
2312 },
2313 'root' : {
2314 'level' : 'WARNING',
2315 },
2316 }
2317
Mike53f7a7c2017-12-14 14:04:53 +03002318 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002319 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002320 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002321 'formatters': {
2322 'form1' : {
2323 'format' : '%(levelname)s ++ %(message)s',
2324 },
2325 },
2326 'handlers' : {
2327 'hand1' : {
2328 'class' : 'logging.StreamHandler',
2329 'formatter' : 'form1',
2330 'level' : 'NTOSET',
2331 'stream' : 'ext://sys.stdout',
2332 },
2333 },
2334 'loggers' : {
2335 'compiler.parser' : {
2336 'level' : 'DEBUG',
2337 'handlers' : ['hand1'],
2338 },
2339 },
2340 'root' : {
2341 'level' : 'WARNING',
2342 },
2343 }
2344
2345
Mike53f7a7c2017-12-14 14:04:53 +03002346 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002347 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002348 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002349 'formatters': {
2350 'form1' : {
2351 'format' : '%(levelname)s ++ %(message)s',
2352 },
2353 },
2354 'handlers' : {
2355 'hand1' : {
2356 'class' : 'logging.StreamHandler',
2357 'formatter' : 'form1',
2358 'level' : 'NOTSET',
2359 'stream' : 'ext://sys.stdout',
2360 },
2361 },
2362 'loggers' : {
2363 'compiler.parser' : {
2364 'level' : 'DEBUG',
2365 'handlers' : ['hand1'],
2366 },
2367 },
2368 'root' : {
2369 'level' : 'WRANING',
2370 },
2371 }
2372
2373 # config3 has a less subtle configuration error
2374 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002375 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002376 'formatters': {
2377 'form1' : {
2378 'format' : '%(levelname)s ++ %(message)s',
2379 },
2380 },
2381 'handlers' : {
2382 'hand1' : {
2383 'class' : 'logging.StreamHandler',
2384 'formatter' : 'misspelled_name',
2385 'level' : 'NOTSET',
2386 'stream' : 'ext://sys.stdout',
2387 },
2388 },
2389 'loggers' : {
2390 'compiler.parser' : {
2391 'level' : 'DEBUG',
2392 'handlers' : ['hand1'],
2393 },
2394 },
2395 'root' : {
2396 'level' : 'WARNING',
2397 },
2398 }
2399
2400 # config4 specifies a custom formatter class to be loaded
2401 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002402 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002403 'formatters': {
2404 'form1' : {
2405 '()' : __name__ + '.ExceptionFormatter',
2406 'format' : '%(levelname)s:%(name)s:%(message)s',
2407 },
2408 },
2409 'handlers' : {
2410 'hand1' : {
2411 'class' : 'logging.StreamHandler',
2412 'formatter' : 'form1',
2413 'level' : 'NOTSET',
2414 'stream' : 'ext://sys.stdout',
2415 },
2416 },
2417 'root' : {
2418 'level' : 'NOTSET',
2419 'handlers' : ['hand1'],
2420 },
2421 }
2422
2423 # As config4 but using an actual callable rather than a string
2424 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002425 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002426 'formatters': {
2427 'form1' : {
2428 '()' : ExceptionFormatter,
2429 'format' : '%(levelname)s:%(name)s:%(message)s',
2430 },
2431 'form2' : {
2432 '()' : __name__ + '.formatFunc',
2433 'format' : '%(levelname)s:%(name)s:%(message)s',
2434 },
2435 'form3' : {
2436 '()' : formatFunc,
2437 'format' : '%(levelname)s:%(name)s:%(message)s',
2438 },
2439 },
2440 'handlers' : {
2441 'hand1' : {
2442 'class' : 'logging.StreamHandler',
2443 'formatter' : 'form1',
2444 'level' : 'NOTSET',
2445 'stream' : 'ext://sys.stdout',
2446 },
2447 'hand2' : {
2448 '()' : handlerFunc,
2449 },
2450 },
2451 'root' : {
2452 'level' : 'NOTSET',
2453 'handlers' : ['hand1'],
2454 },
2455 }
2456
2457 # config5 specifies a custom handler class to be loaded
2458 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002459 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002460 'formatters': {
2461 'form1' : {
2462 'format' : '%(levelname)s ++ %(message)s',
2463 },
2464 },
2465 'handlers' : {
2466 'hand1' : {
2467 'class' : __name__ + '.CustomHandler',
2468 'formatter' : 'form1',
2469 'level' : 'NOTSET',
2470 'stream' : 'ext://sys.stdout',
2471 },
2472 },
2473 'loggers' : {
2474 'compiler.parser' : {
2475 'level' : 'DEBUG',
2476 'handlers' : ['hand1'],
2477 },
2478 },
2479 'root' : {
2480 'level' : 'WARNING',
2481 },
2482 }
2483
2484 # config6 specifies a custom handler class to be loaded
2485 # but has bad arguments
2486 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002487 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002488 'formatters': {
2489 'form1' : {
2490 'format' : '%(levelname)s ++ %(message)s',
2491 },
2492 },
2493 'handlers' : {
2494 'hand1' : {
2495 'class' : __name__ + '.CustomHandler',
2496 'formatter' : 'form1',
2497 'level' : 'NOTSET',
2498 'stream' : 'ext://sys.stdout',
2499 '9' : 'invalid parameter name',
2500 },
2501 },
2502 'loggers' : {
2503 'compiler.parser' : {
2504 'level' : 'DEBUG',
2505 'handlers' : ['hand1'],
2506 },
2507 },
2508 'root' : {
2509 'level' : 'WARNING',
2510 },
2511 }
2512
Mike53f7a7c2017-12-14 14:04:53 +03002513 # config 7 does not define compiler.parser but defines compiler.lexer
2514 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002515 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002516 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002517 'formatters': {
2518 'form1' : {
2519 'format' : '%(levelname)s ++ %(message)s',
2520 },
2521 },
2522 'handlers' : {
2523 'hand1' : {
2524 'class' : 'logging.StreamHandler',
2525 'formatter' : 'form1',
2526 'level' : 'NOTSET',
2527 'stream' : 'ext://sys.stdout',
2528 },
2529 },
2530 'loggers' : {
2531 'compiler.lexer' : {
2532 'level' : 'DEBUG',
2533 'handlers' : ['hand1'],
2534 },
2535 },
2536 'root' : {
2537 'level' : 'WARNING',
2538 },
2539 }
2540
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002541 # config8 defines both compiler and compiler.lexer
2542 # so compiler.parser should not be disabled (since
2543 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002544 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002545 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002546 'disable_existing_loggers' : False,
2547 'formatters': {
2548 'form1' : {
2549 'format' : '%(levelname)s ++ %(message)s',
2550 },
2551 },
2552 'handlers' : {
2553 'hand1' : {
2554 'class' : 'logging.StreamHandler',
2555 'formatter' : 'form1',
2556 'level' : 'NOTSET',
2557 'stream' : 'ext://sys.stdout',
2558 },
2559 },
2560 'loggers' : {
2561 'compiler' : {
2562 'level' : 'DEBUG',
2563 'handlers' : ['hand1'],
2564 },
2565 'compiler.lexer' : {
2566 },
2567 },
2568 'root' : {
2569 'level' : 'WARNING',
2570 },
2571 }
2572
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002573 # config8a disables existing loggers
2574 config8a = {
2575 'version': 1,
2576 'disable_existing_loggers' : True,
2577 'formatters': {
2578 'form1' : {
2579 'format' : '%(levelname)s ++ %(message)s',
2580 },
2581 },
2582 'handlers' : {
2583 'hand1' : {
2584 'class' : 'logging.StreamHandler',
2585 'formatter' : 'form1',
2586 'level' : 'NOTSET',
2587 'stream' : 'ext://sys.stdout',
2588 },
2589 },
2590 'loggers' : {
2591 'compiler' : {
2592 'level' : 'DEBUG',
2593 'handlers' : ['hand1'],
2594 },
2595 'compiler.lexer' : {
2596 },
2597 },
2598 'root' : {
2599 'level' : 'WARNING',
2600 },
2601 }
2602
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002603 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002604 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002605 'formatters': {
2606 'form1' : {
2607 'format' : '%(levelname)s ++ %(message)s',
2608 },
2609 },
2610 'handlers' : {
2611 'hand1' : {
2612 'class' : 'logging.StreamHandler',
2613 'formatter' : 'form1',
2614 'level' : 'WARNING',
2615 'stream' : 'ext://sys.stdout',
2616 },
2617 },
2618 'loggers' : {
2619 'compiler.parser' : {
2620 'level' : 'WARNING',
2621 'handlers' : ['hand1'],
2622 },
2623 },
2624 'root' : {
2625 'level' : 'NOTSET',
2626 },
2627 }
2628
2629 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002630 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002631 'incremental' : True,
2632 'handlers' : {
2633 'hand1' : {
2634 'level' : 'WARNING',
2635 },
2636 },
2637 'loggers' : {
2638 'compiler.parser' : {
2639 'level' : 'INFO',
2640 },
2641 },
2642 }
2643
2644 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002645 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002646 'incremental' : True,
2647 'handlers' : {
2648 'hand1' : {
2649 'level' : 'INFO',
2650 },
2651 },
2652 'loggers' : {
2653 'compiler.parser' : {
2654 'level' : 'INFO',
2655 },
2656 },
2657 }
2658
Mike53f7a7c2017-12-14 14:04:53 +03002659 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002660 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002661 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002662 'formatters': {
2663 'form1' : {
2664 'format' : '%(levelname)s ++ %(message)s',
2665 },
2666 },
2667 'filters' : {
2668 'filt1' : {
2669 'name' : 'compiler.parser',
2670 },
2671 },
2672 'handlers' : {
2673 'hand1' : {
2674 'class' : 'logging.StreamHandler',
2675 'formatter' : 'form1',
2676 'level' : 'NOTSET',
2677 'stream' : 'ext://sys.stdout',
2678 'filters' : ['filt1'],
2679 },
2680 },
2681 'loggers' : {
2682 'compiler.parser' : {
2683 'level' : 'DEBUG',
2684 'filters' : ['filt1'],
2685 },
2686 },
2687 'root' : {
2688 'level' : 'WARNING',
2689 'handlers' : ['hand1'],
2690 },
2691 }
2692
Mike53f7a7c2017-12-14 14:04:53 +03002693 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002694 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002695 'version': 1,
2696 'true_formatters': {
2697 'form1' : {
2698 'format' : '%(levelname)s ++ %(message)s',
2699 },
2700 },
2701 'handler_configs': {
2702 'hand1' : {
2703 'class' : 'logging.StreamHandler',
2704 'formatter' : 'form1',
2705 'level' : 'NOTSET',
2706 'stream' : 'ext://sys.stdout',
2707 },
2708 },
2709 'formatters' : 'cfg://true_formatters',
2710 'handlers' : {
2711 'hand1' : 'cfg://handler_configs[hand1]',
2712 },
2713 'loggers' : {
2714 'compiler.parser' : {
2715 'level' : 'DEBUG',
2716 'handlers' : ['hand1'],
2717 },
2718 },
2719 'root' : {
2720 'level' : 'WARNING',
2721 },
2722 }
2723
Mike53f7a7c2017-12-14 14:04:53 +03002724 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002725 config12 = {
2726 'true_formatters': {
2727 'form1' : {
2728 'format' : '%(levelname)s ++ %(message)s',
2729 },
2730 },
2731 'handler_configs': {
2732 'hand1' : {
2733 'class' : 'logging.StreamHandler',
2734 'formatter' : 'form1',
2735 'level' : 'NOTSET',
2736 'stream' : 'ext://sys.stdout',
2737 },
2738 },
2739 'formatters' : 'cfg://true_formatters',
2740 'handlers' : {
2741 'hand1' : 'cfg://handler_configs[hand1]',
2742 },
2743 'loggers' : {
2744 'compiler.parser' : {
2745 'level' : 'DEBUG',
2746 'handlers' : ['hand1'],
2747 },
2748 },
2749 'root' : {
2750 'level' : 'WARNING',
2751 },
2752 }
2753
Mike53f7a7c2017-12-14 14:04:53 +03002754 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002755 config13 = {
2756 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002757 'true_formatters': {
2758 'form1' : {
2759 'format' : '%(levelname)s ++ %(message)s',
2760 },
2761 },
2762 'handler_configs': {
2763 'hand1' : {
2764 'class' : 'logging.StreamHandler',
2765 'formatter' : 'form1',
2766 'level' : 'NOTSET',
2767 'stream' : 'ext://sys.stdout',
2768 },
2769 },
2770 'formatters' : 'cfg://true_formatters',
2771 'handlers' : {
2772 'hand1' : 'cfg://handler_configs[hand1]',
2773 },
2774 'loggers' : {
2775 'compiler.parser' : {
2776 'level' : 'DEBUG',
2777 'handlers' : ['hand1'],
2778 },
2779 },
2780 'root' : {
2781 'level' : 'WARNING',
2782 },
2783 }
2784
Vinay Sajip8d270232012-11-15 14:20:18 +00002785 # As config0, but with properties
2786 config14 = {
2787 'version': 1,
2788 'formatters': {
2789 'form1' : {
2790 'format' : '%(levelname)s ++ %(message)s',
2791 },
2792 },
2793 'handlers' : {
2794 'hand1' : {
2795 'class' : 'logging.StreamHandler',
2796 'formatter' : 'form1',
2797 'level' : 'NOTSET',
2798 'stream' : 'ext://sys.stdout',
2799 '.': {
2800 'foo': 'bar',
2801 'terminator': '!\n',
2802 }
2803 },
2804 },
2805 'root' : {
2806 'level' : 'WARNING',
2807 'handlers' : ['hand1'],
2808 },
2809 }
2810
Vinay Sajip3f885b52013-03-22 15:19:54 +00002811 out_of_order = {
2812 "version": 1,
2813 "formatters": {
2814 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002815 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2816 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002817 }
2818 },
2819 "handlers": {
2820 "fileGlobal": {
2821 "class": "logging.StreamHandler",
2822 "level": "DEBUG",
2823 "formatter": "mySimpleFormatter"
2824 },
2825 "bufferGlobal": {
2826 "class": "logging.handlers.MemoryHandler",
2827 "capacity": 5,
2828 "formatter": "mySimpleFormatter",
2829 "target": "fileGlobal",
2830 "level": "DEBUG"
2831 }
2832 },
2833 "loggers": {
2834 "mymodule": {
2835 "level": "DEBUG",
2836 "handlers": ["bufferGlobal"],
2837 "propagate": "true"
2838 }
2839 }
2840 }
2841
BNMetrics18fb1fb2018-10-15 19:41:36 +01002842 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2843 custom_formatter_class_validate = {
2844 'version': 1,
2845 'formatters': {
2846 'form1': {
2847 '()': __name__ + '.ExceptionFormatter',
2848 'format': '%(levelname)s:%(name)s:%(message)s',
2849 'validate': False,
2850 },
2851 },
2852 'handlers' : {
2853 'hand1' : {
2854 'class': 'logging.StreamHandler',
2855 'formatter': 'form1',
2856 'level': 'NOTSET',
2857 'stream': 'ext://sys.stdout',
2858 },
2859 },
2860 "loggers": {
2861 "my_test_logger_custom_formatter": {
2862 "level": "DEBUG",
2863 "handlers": ["hand1"],
2864 "propagate": "true"
2865 }
2866 }
2867 }
2868
2869 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2870 custom_formatter_class_validate2 = {
2871 'version': 1,
2872 'formatters': {
2873 'form1': {
2874 'class': __name__ + '.ExceptionFormatter',
2875 'format': '%(levelname)s:%(name)s:%(message)s',
2876 'validate': False,
2877 },
2878 },
2879 'handlers' : {
2880 'hand1' : {
2881 'class': 'logging.StreamHandler',
2882 'formatter': 'form1',
2883 'level': 'NOTSET',
2884 'stream': 'ext://sys.stdout',
2885 },
2886 },
2887 "loggers": {
2888 "my_test_logger_custom_formatter": {
2889 "level": "DEBUG",
2890 "handlers": ["hand1"],
2891 "propagate": "true"
2892 }
2893 }
2894 }
2895
2896 # Configuration with custom class that is not inherited from logging.Formatter
2897 custom_formatter_class_validate3 = {
2898 'version': 1,
2899 'formatters': {
2900 'form1': {
2901 'class': __name__ + '.myCustomFormatter',
2902 'format': '%(levelname)s:%(name)s:%(message)s',
2903 'validate': False,
2904 },
2905 },
2906 'handlers' : {
2907 'hand1' : {
2908 'class': 'logging.StreamHandler',
2909 'formatter': 'form1',
2910 'level': 'NOTSET',
2911 'stream': 'ext://sys.stdout',
2912 },
2913 },
2914 "loggers": {
2915 "my_test_logger_custom_formatter": {
2916 "level": "DEBUG",
2917 "handlers": ["hand1"],
2918 "propagate": "true"
2919 }
2920 }
2921 }
2922
2923 # Configuration with custom function and 'validate' set to False
2924 custom_formatter_with_function = {
2925 'version': 1,
2926 'formatters': {
2927 'form1': {
2928 '()': formatFunc,
2929 'format': '%(levelname)s:%(name)s:%(message)s',
2930 'validate': False,
2931 },
2932 },
2933 'handlers' : {
2934 'hand1' : {
2935 'class': 'logging.StreamHandler',
2936 'formatter': 'form1',
2937 'level': 'NOTSET',
2938 'stream': 'ext://sys.stdout',
2939 },
2940 },
2941 "loggers": {
2942 "my_test_logger_custom_formatter": {
2943 "level": "DEBUG",
2944 "handlers": ["hand1"],
2945 "propagate": "true"
2946 }
2947 }
2948 }
2949
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002950 def apply_config(self, conf):
2951 logging.config.dictConfig(conf)
2952
2953 def test_config0_ok(self):
2954 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002955 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002956 self.apply_config(self.config0)
2957 logger = logging.getLogger()
2958 # Won't output anything
2959 logger.info(self.next_message())
2960 # Outputs a message
2961 logger.error(self.next_message())
2962 self.assert_log_lines([
2963 ('ERROR', '2'),
2964 ], stream=output)
2965 # Original logger output is empty.
2966 self.assert_log_lines([])
2967
2968 def test_config1_ok(self, config=config1):
2969 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002970 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002971 self.apply_config(config)
2972 logger = logging.getLogger("compiler.parser")
2973 # Both will output a message
2974 logger.info(self.next_message())
2975 logger.error(self.next_message())
2976 self.assert_log_lines([
2977 ('INFO', '1'),
2978 ('ERROR', '2'),
2979 ], stream=output)
2980 # Original logger output is empty.
2981 self.assert_log_lines([])
2982
2983 def test_config2_failure(self):
2984 # A simple config which overrides the default settings.
2985 self.assertRaises(Exception, self.apply_config, self.config2)
2986
2987 def test_config2a_failure(self):
2988 # A simple config which overrides the default settings.
2989 self.assertRaises(Exception, self.apply_config, self.config2a)
2990
2991 def test_config2b_failure(self):
2992 # A simple config which overrides the default settings.
2993 self.assertRaises(Exception, self.apply_config, self.config2b)
2994
2995 def test_config3_failure(self):
2996 # A simple config which overrides the default settings.
2997 self.assertRaises(Exception, self.apply_config, self.config3)
2998
2999 def test_config4_ok(self):
3000 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003001 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003002 self.apply_config(self.config4)
3003 #logger = logging.getLogger()
3004 try:
3005 raise RuntimeError()
3006 except RuntimeError:
3007 logging.exception("just testing")
3008 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003009 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003010 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3011 # Original logger output is empty
3012 self.assert_log_lines([])
3013
3014 def test_config4a_ok(self):
3015 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003016 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003017 self.apply_config(self.config4a)
3018 #logger = logging.getLogger()
3019 try:
3020 raise RuntimeError()
3021 except RuntimeError:
3022 logging.exception("just testing")
3023 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003024 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003025 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3026 # Original logger output is empty
3027 self.assert_log_lines([])
3028
3029 def test_config5_ok(self):
3030 self.test_config1_ok(config=self.config5)
3031
3032 def test_config6_failure(self):
3033 self.assertRaises(Exception, self.apply_config, self.config6)
3034
3035 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003036 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003037 self.apply_config(self.config1)
3038 logger = logging.getLogger("compiler.parser")
3039 # Both will output a message
3040 logger.info(self.next_message())
3041 logger.error(self.next_message())
3042 self.assert_log_lines([
3043 ('INFO', '1'),
3044 ('ERROR', '2'),
3045 ], stream=output)
3046 # Original logger output is empty.
3047 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003048 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003049 self.apply_config(self.config7)
3050 logger = logging.getLogger("compiler.parser")
3051 self.assertTrue(logger.disabled)
3052 logger = logging.getLogger("compiler.lexer")
3053 # Both will output a message
3054 logger.info(self.next_message())
3055 logger.error(self.next_message())
3056 self.assert_log_lines([
3057 ('INFO', '3'),
3058 ('ERROR', '4'),
3059 ], stream=output)
3060 # Original logger output is empty.
3061 self.assert_log_lines([])
3062
Mike53f7a7c2017-12-14 14:04:53 +03003063 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003064 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003065 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003066 self.apply_config(self.config1)
3067 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003068 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003069 logger.info(self.next_message())
3070 logger.error(self.next_message())
3071 self.assert_log_lines([
3072 ('INFO', '1'),
3073 ('ERROR', '2'),
3074 ], stream=output)
3075 # Original logger output is empty.
3076 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003077 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003078 self.apply_config(self.config8)
3079 logger = logging.getLogger("compiler.parser")
3080 self.assertFalse(logger.disabled)
3081 # Both will output a message
3082 logger.info(self.next_message())
3083 logger.error(self.next_message())
3084 logger = logging.getLogger("compiler.lexer")
3085 # Both will output a message
3086 logger.info(self.next_message())
3087 logger.error(self.next_message())
3088 self.assert_log_lines([
3089 ('INFO', '3'),
3090 ('ERROR', '4'),
3091 ('INFO', '5'),
3092 ('ERROR', '6'),
3093 ], stream=output)
3094 # Original logger output is empty.
3095 self.assert_log_lines([])
3096
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003097 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003098 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003099 self.apply_config(self.config1a)
3100 logger = logging.getLogger("compiler.parser")
3101 # See issue #11424. compiler-hyphenated sorts
3102 # between compiler and compiler.xyz and this
3103 # was preventing compiler.xyz from being included
3104 # in the child loggers of compiler because of an
3105 # overzealous loop termination condition.
3106 hyphenated = logging.getLogger('compiler-hyphenated')
3107 # All will output a message
3108 logger.info(self.next_message())
3109 logger.error(self.next_message())
3110 hyphenated.critical(self.next_message())
3111 self.assert_log_lines([
3112 ('INFO', '1'),
3113 ('ERROR', '2'),
3114 ('CRITICAL', '3'),
3115 ], stream=output)
3116 # Original logger output is empty.
3117 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003118 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003119 self.apply_config(self.config8a)
3120 logger = logging.getLogger("compiler.parser")
3121 self.assertFalse(logger.disabled)
3122 # Both will output a message
3123 logger.info(self.next_message())
3124 logger.error(self.next_message())
3125 logger = logging.getLogger("compiler.lexer")
3126 # Both will output a message
3127 logger.info(self.next_message())
3128 logger.error(self.next_message())
3129 # Will not appear
3130 hyphenated.critical(self.next_message())
3131 self.assert_log_lines([
3132 ('INFO', '4'),
3133 ('ERROR', '5'),
3134 ('INFO', '6'),
3135 ('ERROR', '7'),
3136 ], stream=output)
3137 # Original logger output is empty.
3138 self.assert_log_lines([])
3139
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003140 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003141 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003142 self.apply_config(self.config9)
3143 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003144 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003145 logger.info(self.next_message())
3146 self.assert_log_lines([], stream=output)
3147 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003148 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003149 logger.info(self.next_message())
3150 self.assert_log_lines([], stream=output)
3151 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003152 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003153 logger.info(self.next_message())
3154 self.assert_log_lines([
3155 ('INFO', '3'),
3156 ], stream=output)
3157
3158 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003159 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003160 self.apply_config(self.config10)
3161 logger = logging.getLogger("compiler.parser")
3162 logger.warning(self.next_message())
3163 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003164 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003165 logger.warning(self.next_message())
3166 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003167 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003168 logger.warning(self.next_message())
3169 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003170 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003171 logger.error(self.next_message())
3172 self.assert_log_lines([
3173 ('WARNING', '1'),
3174 ('ERROR', '4'),
3175 ], stream=output)
3176
3177 def test_config11_ok(self):
3178 self.test_config1_ok(self.config11)
3179
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003180 def test_config12_failure(self):
3181 self.assertRaises(Exception, self.apply_config, self.config12)
3182
3183 def test_config13_failure(self):
3184 self.assertRaises(Exception, self.apply_config, self.config13)
3185
Vinay Sajip8d270232012-11-15 14:20:18 +00003186 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003187 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003188 self.apply_config(self.config14)
3189 h = logging._handlers['hand1']
3190 self.assertEqual(h.foo, 'bar')
3191 self.assertEqual(h.terminator, '!\n')
3192 logging.warning('Exclamation')
3193 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3194
Xtreak087570a2018-07-02 14:27:46 +05303195 def test_config15_ok(self):
3196
3197 def cleanup(h1, fn):
3198 h1.close()
3199 os.remove(fn)
3200
3201 with self.check_no_resource_warning():
3202 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3203 os.close(fd)
3204
3205 config = {
3206 "version": 1,
3207 "handlers": {
3208 "file": {
3209 "class": "logging.FileHandler",
Inada Naokifa51c0c2021-04-29 11:34:56 +09003210 "filename": fn,
3211 "encoding": "utf-8",
Xtreak087570a2018-07-02 14:27:46 +05303212 }
3213 },
3214 "root": {
3215 "handlers": ["file"]
3216 }
3217 }
3218
3219 self.apply_config(config)
3220 self.apply_config(config)
3221
3222 handler = logging.root.handlers[0]
3223 self.addCleanup(cleanup, handler, fn)
3224
Vinay Sajip4ded5512012-10-02 15:56:16 +01003225 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003226 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003227 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003228 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003229 t.start()
3230 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003231 # Now get the port allocated
3232 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003233 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003234 try:
3235 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3236 sock.settimeout(2.0)
3237 sock.connect(('localhost', port))
3238
3239 slen = struct.pack('>L', len(text))
3240 s = slen + text
3241 sentsofar = 0
3242 left = len(s)
3243 while left > 0:
3244 sent = sock.send(s[sentsofar:])
3245 sentsofar += sent
3246 left -= sent
3247 sock.close()
3248 finally:
3249 t.ready.wait(2.0)
3250 logging.config.stopListening()
Hai Shie80697d2020-05-28 06:10:27 +08003251 threading_helper.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003252
3253 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003254 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003255 self.setup_via_listener(json.dumps(self.config10))
3256 logger = logging.getLogger("compiler.parser")
3257 logger.warning(self.next_message())
3258 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003259 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003260 logger.warning(self.next_message())
3261 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003262 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003263 logger.warning(self.next_message())
3264 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003265 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003266 logger.error(self.next_message())
3267 self.assert_log_lines([
3268 ('WARNING', '1'),
3269 ('ERROR', '4'),
3270 ], stream=output)
3271
3272 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003273 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003274 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3275 logger = logging.getLogger("compiler.parser")
3276 # Both will output a message
3277 logger.info(self.next_message())
3278 logger.error(self.next_message())
3279 self.assert_log_lines([
3280 ('INFO', '1'),
3281 ('ERROR', '2'),
3282 ], stream=output)
3283 # Original logger output is empty.
3284 self.assert_log_lines([])
3285
Vinay Sajip4ded5512012-10-02 15:56:16 +01003286 def test_listen_verify(self):
3287
3288 def verify_fail(stuff):
3289 return None
3290
3291 def verify_reverse(stuff):
3292 return stuff[::-1]
3293
3294 logger = logging.getLogger("compiler.parser")
3295 to_send = textwrap.dedent(ConfigFileTest.config1)
3296 # First, specify a verification function that will fail.
3297 # We expect to see no output, since our configuration
3298 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003299 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003300 self.setup_via_listener(to_send, verify_fail)
3301 # Both will output a message
3302 logger.info(self.next_message())
3303 logger.error(self.next_message())
3304 self.assert_log_lines([], stream=output)
3305 # Original logger output has the stuff we logged.
3306 self.assert_log_lines([
3307 ('INFO', '1'),
3308 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003309 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003310
3311 # Now, perform no verification. Our configuration
3312 # should take effect.
3313
Vinay Sajip1feedb42014-05-31 08:19:12 +01003314 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003315 self.setup_via_listener(to_send) # no verify callable specified
3316 logger = logging.getLogger("compiler.parser")
3317 # Both will output a message
3318 logger.info(self.next_message())
3319 logger.error(self.next_message())
3320 self.assert_log_lines([
3321 ('INFO', '3'),
3322 ('ERROR', '4'),
3323 ], stream=output)
3324 # Original logger output still has the stuff we logged before.
3325 self.assert_log_lines([
3326 ('INFO', '1'),
3327 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003328 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003329
3330 # Now, perform verification which transforms the bytes.
3331
Vinay Sajip1feedb42014-05-31 08:19:12 +01003332 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003333 self.setup_via_listener(to_send[::-1], verify_reverse)
3334 logger = logging.getLogger("compiler.parser")
3335 # Both will output a message
3336 logger.info(self.next_message())
3337 logger.error(self.next_message())
3338 self.assert_log_lines([
3339 ('INFO', '5'),
3340 ('ERROR', '6'),
3341 ], stream=output)
3342 # Original logger output still has the stuff we logged before.
3343 self.assert_log_lines([
3344 ('INFO', '1'),
3345 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003346 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003347
Vinay Sajip3f885b52013-03-22 15:19:54 +00003348 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003349 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3350
3351 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003352 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003353 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3354
3355 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003356 handler = logging.getLogger('mymodule').handlers[0]
3357 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003358 self.assertIsInstance(handler.formatter._style,
3359 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003360
BNMetrics18fb1fb2018-10-15 19:41:36 +01003361 def test_custom_formatter_class_with_validate(self):
3362 self.apply_config(self.custom_formatter_class_validate)
3363 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3364 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3365
3366 def test_custom_formatter_class_with_validate2(self):
3367 self.apply_config(self.custom_formatter_class_validate2)
3368 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3369 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3370
3371 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3372 config = self.custom_formatter_class_validate.copy()
3373 config['formatters']['form1']['style'] = "$"
3374
3375 # Exception should not be raise as we have configured 'validate' to False
3376 self.apply_config(config)
3377 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3378 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3379
3380 def test_custom_formatter_class_with_validate3(self):
3381 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3382
3383 def test_custom_formatter_function_with_validate(self):
3384 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3385
Vinay Sajip373baef2011-04-26 20:05:24 +01003386 def test_baseconfig(self):
3387 d = {
3388 'atuple': (1, 2, 3),
3389 'alist': ['a', 'b', 'c'],
3390 'adict': {'d': 'e', 'f': 3 },
3391 'nest1': ('g', ('h', 'i'), 'j'),
3392 'nest2': ['k', ['l', 'm'], 'n'],
3393 'nest3': ['o', 'cfg://alist', 'p'],
3394 }
3395 bc = logging.config.BaseConfigurator(d)
3396 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3397 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3398 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3399 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3400 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3401 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3402 v = bc.convert('cfg://nest3')
3403 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3404 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3405 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3406 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003407
Vinay Sajip46abfc12020-01-01 19:32:11 +00003408 def test_namedtuple(self):
3409 # see bpo-39142
3410 from collections import namedtuple
3411
3412 class MyHandler(logging.StreamHandler):
3413 def __init__(self, resource, *args, **kwargs):
3414 super().__init__(*args, **kwargs)
3415 self.resource: namedtuple = resource
3416
3417 def emit(self, record):
3418 record.msg += f' {self.resource.type}'
3419 return super().emit(record)
3420
3421 Resource = namedtuple('Resource', ['type', 'labels'])
3422 resource = Resource(type='my_type', labels=['a'])
3423
3424 config = {
3425 'version': 1,
3426 'handlers': {
3427 'myhandler': {
3428 '()': MyHandler,
3429 'resource': resource
3430 }
3431 },
3432 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3433 }
3434 with support.captured_stderr() as stderr:
3435 self.apply_config(config)
3436 logging.info('some log')
3437 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3438
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003439class ManagerTest(BaseTest):
3440 def test_manager_loggerclass(self):
3441 logged = []
3442
3443 class MyLogger(logging.Logger):
3444 def _log(self, level, msg, args, exc_info=None, extra=None):
3445 logged.append(msg)
3446
3447 man = logging.Manager(None)
3448 self.assertRaises(TypeError, man.setLoggerClass, int)
3449 man.setLoggerClass(MyLogger)
3450 logger = man.getLogger('test')
3451 logger.warning('should appear in logged')
3452 logging.warning('should not appear in logged')
3453
3454 self.assertEqual(logged, ['should appear in logged'])
3455
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003456 def test_set_log_record_factory(self):
3457 man = logging.Manager(None)
3458 expected = object()
3459 man.setLogRecordFactory(expected)
3460 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003461
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003462class ChildLoggerTest(BaseTest):
3463 def test_child_loggers(self):
3464 r = logging.getLogger()
3465 l1 = logging.getLogger('abc')
3466 l2 = logging.getLogger('def.ghi')
3467 c1 = r.getChild('xyz')
3468 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003469 self.assertIs(c1, logging.getLogger('xyz'))
3470 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003471 c1 = l1.getChild('def')
3472 c2 = c1.getChild('ghi')
3473 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003474 self.assertIs(c1, logging.getLogger('abc.def'))
3475 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3476 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003477
3478
Vinay Sajip6fac8172010-10-19 20:44:14 +00003479class DerivedLogRecord(logging.LogRecord):
3480 pass
3481
Vinay Sajip61561522010-12-03 11:50:38 +00003482class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003483
3484 def setUp(self):
3485 class CheckingFilter(logging.Filter):
3486 def __init__(self, cls):
3487 self.cls = cls
3488
3489 def filter(self, record):
3490 t = type(record)
3491 if t is not self.cls:
3492 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3493 self.cls)
3494 raise TypeError(msg)
3495 return True
3496
3497 BaseTest.setUp(self)
3498 self.filter = CheckingFilter(DerivedLogRecord)
3499 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003500 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003501
3502 def tearDown(self):
3503 self.root_logger.removeFilter(self.filter)
3504 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003505 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003506
3507 def test_logrecord_class(self):
3508 self.assertRaises(TypeError, self.root_logger.warning,
3509 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003510 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003511 self.root_logger.error(self.next_message())
3512 self.assert_log_lines([
3513 ('root', 'ERROR', '2'),
3514 ])
3515
3516
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003517class QueueHandlerTest(BaseTest):
3518 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003519 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003520
3521 def setUp(self):
3522 BaseTest.setUp(self)
3523 self.queue = queue.Queue(-1)
3524 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003525 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003526 self.que_logger = logging.getLogger('que')
3527 self.que_logger.propagate = False
3528 self.que_logger.setLevel(logging.WARNING)
3529 self.que_logger.addHandler(self.que_hdlr)
3530
3531 def tearDown(self):
3532 self.que_hdlr.close()
3533 BaseTest.tearDown(self)
3534
3535 def test_queue_handler(self):
3536 self.que_logger.debug(self.next_message())
3537 self.assertRaises(queue.Empty, self.queue.get_nowait)
3538 self.que_logger.info(self.next_message())
3539 self.assertRaises(queue.Empty, self.queue.get_nowait)
3540 msg = self.next_message()
3541 self.que_logger.warning(msg)
3542 data = self.queue.get_nowait()
3543 self.assertTrue(isinstance(data, logging.LogRecord))
3544 self.assertEqual(data.name, self.que_logger.name)
3545 self.assertEqual((data.msg, data.args), (msg, None))
3546
favlladfe3442017-08-01 20:12:26 +02003547 def test_formatting(self):
3548 msg = self.next_message()
3549 levelname = logging.getLevelName(logging.WARNING)
3550 log_format_str = '{name} -> {levelname}: {message}'
3551 formatted_msg = log_format_str.format(name=self.name,
3552 levelname=levelname, message=msg)
3553 formatter = logging.Formatter(self.log_format)
3554 self.que_hdlr.setFormatter(formatter)
3555 self.que_logger.warning(msg)
3556 log_record = self.queue.get_nowait()
3557 self.assertEqual(formatted_msg, log_record.msg)
3558 self.assertEqual(formatted_msg, log_record.message)
3559
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003560 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3561 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003562 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003563 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003564 listener = logging.handlers.QueueListener(self.queue, handler)
3565 listener.start()
3566 try:
3567 self.que_logger.warning(self.next_message())
3568 self.que_logger.error(self.next_message())
3569 self.que_logger.critical(self.next_message())
3570 finally:
3571 listener.stop()
3572 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3573 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3574 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003575 handler.close()
3576
3577 # Now test with respect_handler_level set
3578
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003579 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003580 handler.setLevel(logging.CRITICAL)
3581 listener = logging.handlers.QueueListener(self.queue, handler,
3582 respect_handler_level=True)
3583 listener.start()
3584 try:
3585 self.que_logger.warning(self.next_message())
3586 self.que_logger.error(self.next_message())
3587 self.que_logger.critical(self.next_message())
3588 finally:
3589 listener.stop()
3590 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3591 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3592 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003593 handler.close()
3594
3595 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3596 'logging.handlers.QueueListener required for this test')
3597 def test_queue_listener_with_StreamHandler(self):
3598 # Test that traceback only appends once (bpo-34334).
3599 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3600 listener.start()
3601 try:
3602 1 / 0
3603 except ZeroDivisionError as e:
3604 exc = e
3605 self.que_logger.exception(self.next_message(), exc_info=exc)
3606 listener.stop()
3607 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003608
Xtreak2dad9602019-04-07 13:21:27 +05303609 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3610 'logging.handlers.QueueListener required for this test')
3611 def test_queue_listener_with_multiple_handlers(self):
3612 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3613 self.que_hdlr.setFormatter(self.root_formatter)
3614 self.que_logger.addHandler(self.root_hdlr)
3615
3616 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3617 listener.start()
3618 self.que_logger.error("error")
3619 listener.stop()
3620 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3621
Vinay Sajipd61910c2016-09-08 01:13:39 +01003622if hasattr(logging.handlers, 'QueueListener'):
3623 import multiprocessing
3624 from unittest.mock import patch
3625
3626 class QueueListenerTest(BaseTest):
3627 """
3628 Tests based on patch submitted for issue #27930. Ensure that
3629 QueueListener handles all log messages.
3630 """
3631
3632 repeat = 20
3633
3634 @staticmethod
3635 def setup_and_log(log_queue, ident):
3636 """
3637 Creates a logger with a QueueHandler that logs to a queue read by a
3638 QueueListener. Starts the listener, logs five messages, and stops
3639 the listener.
3640 """
3641 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3642 logger.setLevel(logging.DEBUG)
3643 handler = logging.handlers.QueueHandler(log_queue)
3644 logger.addHandler(handler)
3645 listener = logging.handlers.QueueListener(log_queue)
3646 listener.start()
3647
3648 logger.info('one')
3649 logger.info('two')
3650 logger.info('three')
3651 logger.info('four')
3652 logger.info('five')
3653
3654 listener.stop()
3655 logger.removeHandler(handler)
3656 handler.close()
3657
3658 @patch.object(logging.handlers.QueueListener, 'handle')
3659 def test_handle_called_with_queue_queue(self, mock_handle):
3660 for i in range(self.repeat):
3661 log_queue = queue.Queue()
3662 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3663 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3664 'correct number of handled log messages')
3665
3666 @patch.object(logging.handlers.QueueListener, 'handle')
3667 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003668 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003669 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003670 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003671 for i in range(self.repeat):
3672 log_queue = multiprocessing.Queue()
3673 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003674 log_queue.close()
3675 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003676 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3677 'correct number of handled log messages')
3678
3679 @staticmethod
3680 def get_all_from_queue(log_queue):
3681 try:
3682 while True:
3683 yield log_queue.get_nowait()
3684 except queue.Empty:
3685 return []
3686
3687 def test_no_messages_in_queue_after_stop(self):
3688 """
3689 Five messages are logged then the QueueListener is stopped. This
3690 test then gets everything off the queue. Failure of this test
3691 indicates that messages were not registered on the queue until
3692 _after_ the QueueListener stopped.
3693 """
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003694 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003695 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003696 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003697 for i in range(self.repeat):
3698 queue = multiprocessing.Queue()
3699 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3700 # time.sleep(1)
3701 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003702 queue.close()
3703 queue.join_thread()
3704
Vinay Sajipd61910c2016-09-08 01:13:39 +01003705 expected = [[], [logging.handlers.QueueListener._sentinel]]
3706 self.assertIn(items, expected,
3707 'Found unexpected messages in queue: %s' % (
3708 [m.msg if isinstance(m, logging.LogRecord)
3709 else m for m in items]))
3710
Bar Harel6b282e12019-06-01 12:19:09 +03003711 def test_calls_task_done_after_stop(self):
3712 # Issue 36813: Make sure queue.join does not deadlock.
3713 log_queue = queue.Queue()
3714 listener = logging.handlers.QueueListener(log_queue)
3715 listener.start()
3716 listener.stop()
3717 with self.assertRaises(ValueError):
3718 # Make sure all tasks are done and .join won't block.
3719 log_queue.task_done()
3720
Vinay Sajipe723e962011-04-15 22:27:17 +01003721
Vinay Sajip37eb3382011-04-26 20:26:41 +01003722ZERO = datetime.timedelta(0)
3723
3724class UTC(datetime.tzinfo):
3725 def utcoffset(self, dt):
3726 return ZERO
3727
3728 dst = utcoffset
3729
3730 def tzname(self, dt):
3731 return 'UTC'
3732
3733utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003734
Eric Larson9fdb76c2020-09-25 14:08:50 -04003735class AssertErrorMessage:
3736
3737 def assert_error_message(self, exception, message, *args, **kwargs):
3738 try:
3739 self.assertRaises((), *args, **kwargs)
3740 except exception as e:
3741 self.assertEqual(message, str(e))
3742
3743class FormatterTest(unittest.TestCase, AssertErrorMessage):
Vinay Sajipa39c5712010-10-25 13:57:39 +00003744 def setUp(self):
3745 self.common = {
3746 'name': 'formatter.test',
3747 'level': logging.DEBUG,
3748 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3749 'lineno': 42,
3750 'exc_info': None,
3751 'func': None,
3752 'msg': 'Message with %d %s',
3753 'args': (2, 'placeholders'),
3754 }
3755 self.variants = {
Bar Harel8f192d12020-06-18 17:18:58 +03003756 'custom': {
3757 'custom': 1234
3758 }
Vinay Sajipa39c5712010-10-25 13:57:39 +00003759 }
3760
3761 def get_record(self, name=None):
3762 result = dict(self.common)
3763 if name is not None:
3764 result.update(self.variants[name])
3765 return logging.makeLogRecord(result)
3766
3767 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003768 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003769 r = self.get_record()
3770 f = logging.Formatter('${%(message)s}')
3771 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3772 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003773 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003774 self.assertFalse(f.usesTime())
3775 f = logging.Formatter('%(asctime)s')
3776 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003777 f = logging.Formatter('%(asctime)-15s')
3778 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003779 f = logging.Formatter('%(asctime)#15s')
3780 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003781
3782 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003783 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003784 r = self.get_record()
3785 f = logging.Formatter('$%{message}%$', style='{')
3786 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3787 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003788 self.assertRaises(ValueError, f.format, r)
3789 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003790 self.assertFalse(f.usesTime())
3791 f = logging.Formatter('{asctime}', style='{')
3792 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003793 f = logging.Formatter('{asctime!s:15}', style='{')
3794 self.assertTrue(f.usesTime())
3795 f = logging.Formatter('{asctime:15}', style='{')
3796 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003797
3798 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003799 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003800 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003801 f = logging.Formatter('${message}', style='$')
3802 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003803 f = logging.Formatter('$message', style='$')
3804 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3805 f = logging.Formatter('$$%${message}%$$', style='$')
3806 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3807 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003808 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003809 self.assertFalse(f.usesTime())
3810 f = logging.Formatter('${asctime}', style='$')
3811 self.assertTrue(f.usesTime())
3812 f = logging.Formatter('$asctime', style='$')
3813 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003814 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003815 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003816 f = logging.Formatter('${asctime}--', style='$')
3817 self.assertTrue(f.usesTime())
3818
3819 def test_format_validate(self):
3820 # Check correct formatting
3821 # Percentage style
3822 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3823 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3824 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3825 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3826 f = logging.Formatter("%(process)#+027.23X")
3827 self.assertEqual(f._fmt, "%(process)#+027.23X")
3828 f = logging.Formatter("%(foo)#.*g")
3829 self.assertEqual(f._fmt, "%(foo)#.*g")
3830
3831 # StrFormat Style
3832 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3833 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3834 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3835 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3836 f = logging.Formatter("{customfield!s:#<30}", style="{")
3837 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3838 f = logging.Formatter("{message!r}", style="{")
3839 self.assertEqual(f._fmt, "{message!r}")
3840 f = logging.Formatter("{message!s}", style="{")
3841 self.assertEqual(f._fmt, "{message!s}")
3842 f = logging.Formatter("{message!a}", style="{")
3843 self.assertEqual(f._fmt, "{message!a}")
3844 f = logging.Formatter("{process!r:4.2}", style="{")
3845 self.assertEqual(f._fmt, "{process!r:4.2}")
3846 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3847 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3848 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3849 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3850 f = logging.Formatter("{foo:12.{p}}", style="{")
3851 self.assertEqual(f._fmt, "{foo:12.{p}}")
3852 f = logging.Formatter("{foo:{w}.6}", style="{")
3853 self.assertEqual(f._fmt, "{foo:{w}.6}")
3854 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3855 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3856 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3857 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3858 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3859 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3860
3861 # Dollar style
3862 f = logging.Formatter("${asctime} - $message", style="$")
3863 self.assertEqual(f._fmt, "${asctime} - $message")
3864 f = logging.Formatter("$bar $$", style="$")
3865 self.assertEqual(f._fmt, "$bar $$")
3866 f = logging.Formatter("$bar $$$$", style="$")
3867 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3868
3869 # Testing when ValueError being raised from incorrect format
3870 # Percentage Style
3871 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3872 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3873 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3874 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3875 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3876 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3877 self.assertRaises(ValueError, logging.Formatter, '${message}')
3878 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3879 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3880
3881 # StrFormat Style
3882 # Testing failure for '-' in field name
3883 self.assert_error_message(
3884 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003885 "invalid format: invalid field name/expression: 'name-thing'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003886 logging.Formatter, "{name-thing}", style="{"
3887 )
3888 # Testing failure for style mismatch
3889 self.assert_error_message(
3890 ValueError,
3891 "invalid format: no fields",
3892 logging.Formatter, '%(asctime)s', style='{'
3893 )
3894 # Testing failure for invalid conversion
3895 self.assert_error_message(
3896 ValueError,
3897 "invalid conversion: 'Z'"
3898 )
3899 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3900 self.assert_error_message(
3901 ValueError,
3902 "invalid format: expected ':' after conversion specifier",
3903 logging.Formatter, '{asctime!aa:15}', style='{'
3904 )
3905 # Testing failure for invalid spec
3906 self.assert_error_message(
3907 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003908 "invalid format: bad specifier: '.2ff'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003909 logging.Formatter, '{process:.2ff}', style='{'
3910 )
3911 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3912 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3913 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3914 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3915 # Testing failure for mismatch braces
3916 self.assert_error_message(
3917 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003918 "invalid format: expected '}' before end of string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003919 logging.Formatter, '{process', style='{'
3920 )
3921 self.assert_error_message(
3922 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003923 "invalid format: Single '}' encountered in format string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003924 logging.Formatter, 'process}', style='{'
3925 )
3926 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3927 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3928 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3929 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3930 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3931 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3932 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3933 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3934 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3935
3936 # Dollar style
3937 # Testing failure for mismatch bare $
3938 self.assert_error_message(
3939 ValueError,
3940 "invalid format: bare \'$\' not allowed",
3941 logging.Formatter, '$bar $$$', style='$'
3942 )
3943 self.assert_error_message(
3944 ValueError,
3945 "invalid format: bare \'$\' not allowed",
3946 logging.Formatter, 'bar $', style='$'
3947 )
3948 self.assert_error_message(
3949 ValueError,
3950 "invalid format: bare \'$\' not allowed",
3951 logging.Formatter, 'foo $.', style='$'
3952 )
3953 # Testing failure for mismatch style
3954 self.assert_error_message(
3955 ValueError,
3956 "invalid format: no fields",
3957 logging.Formatter, '{asctime}', style='$'
3958 )
3959 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3960
3961 # Testing failure for incorrect fields
3962 self.assert_error_message(
3963 ValueError,
3964 "invalid format: no fields",
3965 logging.Formatter, 'foo', style='$'
3966 )
3967 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003968
Bar Harel8f192d12020-06-18 17:18:58 +03003969 def test_defaults_parameter(self):
3970 fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message']
3971 styles = ['%', '{', '$']
3972 for fmt, style in zip(fmts, styles):
3973 f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'})
3974 r = self.get_record()
3975 self.assertEqual(f.format(r), 'Default Message with 2 placeholders')
3976 r = self.get_record("custom")
3977 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3978
3979 # Without default
3980 f = logging.Formatter(fmt, style=style)
3981 r = self.get_record()
3982 self.assertRaises(ValueError, f.format, r)
3983
3984 # Non-existing default is ignored
3985 f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'})
3986 r = self.get_record("custom")
3987 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3988
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003989 def test_invalid_style(self):
3990 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3991
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003992 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003993 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003994 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3995 # We use None to indicate we want the local timezone
3996 # We're essentially converting a UTC time to local time
3997 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003998 r.msecs = 123
3999 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01004000 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004001 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
4002 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01004003 f.format(r)
4004 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
4005
Mariusz Felisiak06a35542020-04-17 18:02:47 +02004006 def test_default_msec_format_none(self):
4007 class NoMsecFormatter(logging.Formatter):
4008 default_msec_format = None
4009 default_time_format = '%d/%m/%Y %H:%M:%S'
4010
4011 r = self.get_record()
4012 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
4013 r.created = time.mktime(dt.astimezone(None).timetuple())
4014 f = NoMsecFormatter()
4015 f.converter = time.gmtime
4016 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
4017
4018
Vinay Sajip985ef872011-04-26 19:34:04 +01004019class TestBufferingFormatter(logging.BufferingFormatter):
4020 def formatHeader(self, records):
4021 return '[(%d)' % len(records)
4022
4023 def formatFooter(self, records):
4024 return '(%d)]' % len(records)
4025
4026class BufferingFormatterTest(unittest.TestCase):
4027 def setUp(self):
4028 self.records = [
4029 logging.makeLogRecord({'msg': 'one'}),
4030 logging.makeLogRecord({'msg': 'two'}),
4031 ]
4032
4033 def test_default(self):
4034 f = logging.BufferingFormatter()
4035 self.assertEqual('', f.format([]))
4036 self.assertEqual('onetwo', f.format(self.records))
4037
4038 def test_custom(self):
4039 f = TestBufferingFormatter()
4040 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
4041 lf = logging.Formatter('<%(message)s>')
4042 f = TestBufferingFormatter(lf)
4043 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004044
4045class ExceptionTest(BaseTest):
4046 def test_formatting(self):
4047 r = self.root_logger
4048 h = RecordingHandler()
4049 r.addHandler(h)
4050 try:
4051 raise RuntimeError('deliberate mistake')
4052 except:
4053 logging.exception('failed', stack_info=True)
4054 r.removeHandler(h)
4055 h.close()
4056 r = h.records[0]
4057 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
4058 'call last):\n'))
4059 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4060 'deliberate mistake'))
4061 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4062 'call last):\n'))
4063 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4064 'stack_info=True)'))
4065
4066
Vinay Sajip5a27d402010-12-10 11:42:57 +00004067class LastResortTest(BaseTest):
4068 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004069 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004070 root = self.root_logger
4071 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004072 old_lastresort = logging.lastResort
4073 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004074
Vinay Sajip5a27d402010-12-10 11:42:57 +00004075 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004076 with support.captured_stderr() as stderr:
4077 root.debug('This should not appear')
4078 self.assertEqual(stderr.getvalue(), '')
4079 root.warning('Final chance!')
4080 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4081
4082 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004083 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004084 with support.captured_stderr() as stderr:
4085 root.warning('Final chance!')
4086 msg = 'No handlers could be found for logger "root"\n'
4087 self.assertEqual(stderr.getvalue(), msg)
4088
Vinay Sajip5a27d402010-12-10 11:42:57 +00004089 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004090 with support.captured_stderr() as stderr:
4091 root.warning('Final chance!')
4092 self.assertEqual(stderr.getvalue(), '')
4093
4094 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004095 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004096 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004097 with support.captured_stderr() as stderr:
4098 root.warning('Final chance!')
4099 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004100 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004101 root.addHandler(self.root_hdlr)
4102 logging.lastResort = old_lastresort
4103 logging.raiseExceptions = old_raise_exceptions
4104
4105
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004106class FakeHandler:
4107
4108 def __init__(self, identifier, called):
4109 for method in ('acquire', 'flush', 'close', 'release'):
4110 setattr(self, method, self.record_call(identifier, method, called))
4111
4112 def record_call(self, identifier, method_name, called):
4113 def inner():
4114 called.append('{} - {}'.format(identifier, method_name))
4115 return inner
4116
4117
4118class RecordingHandler(logging.NullHandler):
4119
4120 def __init__(self, *args, **kwargs):
4121 super(RecordingHandler, self).__init__(*args, **kwargs)
4122 self.records = []
4123
4124 def handle(self, record):
4125 """Keep track of all the emitted records."""
4126 self.records.append(record)
4127
4128
4129class ShutdownTest(BaseTest):
4130
Vinay Sajip5e66b162011-04-20 15:41:14 +01004131 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004132
4133 def setUp(self):
4134 super(ShutdownTest, self).setUp()
4135 self.called = []
4136
4137 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004138 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004139
4140 def raise_error(self, error):
4141 def inner():
4142 raise error()
4143 return inner
4144
4145 def test_no_failure(self):
4146 # create some fake handlers
4147 handler0 = FakeHandler(0, self.called)
4148 handler1 = FakeHandler(1, self.called)
4149 handler2 = FakeHandler(2, self.called)
4150
4151 # create live weakref to those handlers
4152 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4153
4154 logging.shutdown(handlerList=list(handlers))
4155
4156 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4157 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4158 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4159 self.assertEqual(expected, self.called)
4160
4161 def _test_with_failure_in_method(self, method, error):
4162 handler = FakeHandler(0, self.called)
4163 setattr(handler, method, self.raise_error(error))
4164 handlers = [logging.weakref.ref(handler)]
4165
4166 logging.shutdown(handlerList=list(handlers))
4167
4168 self.assertEqual('0 - release', self.called[-1])
4169
4170 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004171 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004172
4173 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004174 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004175
4176 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004177 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004178
4179 def test_with_valueerror_in_acquire(self):
4180 self._test_with_failure_in_method('acquire', ValueError)
4181
4182 def test_with_valueerror_in_flush(self):
4183 self._test_with_failure_in_method('flush', ValueError)
4184
4185 def test_with_valueerror_in_close(self):
4186 self._test_with_failure_in_method('close', ValueError)
4187
4188 def test_with_other_error_in_acquire_without_raise(self):
4189 logging.raiseExceptions = False
4190 self._test_with_failure_in_method('acquire', IndexError)
4191
4192 def test_with_other_error_in_flush_without_raise(self):
4193 logging.raiseExceptions = False
4194 self._test_with_failure_in_method('flush', IndexError)
4195
4196 def test_with_other_error_in_close_without_raise(self):
4197 logging.raiseExceptions = False
4198 self._test_with_failure_in_method('close', IndexError)
4199
4200 def test_with_other_error_in_acquire_with_raise(self):
4201 logging.raiseExceptions = True
4202 self.assertRaises(IndexError, self._test_with_failure_in_method,
4203 'acquire', IndexError)
4204
4205 def test_with_other_error_in_flush_with_raise(self):
4206 logging.raiseExceptions = True
4207 self.assertRaises(IndexError, self._test_with_failure_in_method,
4208 'flush', IndexError)
4209
4210 def test_with_other_error_in_close_with_raise(self):
4211 logging.raiseExceptions = True
4212 self.assertRaises(IndexError, self._test_with_failure_in_method,
4213 'close', IndexError)
4214
4215
4216class ModuleLevelMiscTest(BaseTest):
4217
Vinay Sajip5e66b162011-04-20 15:41:14 +01004218 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004219
4220 def test_disable(self):
4221 old_disable = logging.root.manager.disable
4222 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004223 self.assertEqual(old_disable, 0)
4224 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004225
4226 logging.disable(83)
4227 self.assertEqual(logging.root.manager.disable, 83)
4228
Matthias Bussonnierb32d8b42020-12-16 01:43:39 -08004229 self.assertRaises(ValueError, logging.disable, "doesnotexists")
4230
4231 class _NotAnIntOrString:
4232 pass
4233
4234 self.assertRaises(TypeError, logging.disable, _NotAnIntOrString())
4235
4236 logging.disable("WARN")
4237
Vinay Sajipd489ac92016-12-31 11:40:11 +00004238 # test the default value introduced in 3.7
4239 # (Issue #28524)
4240 logging.disable()
4241 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4242
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004243 def _test_log(self, method, level=None):
4244 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004245 support.patch(self, logging, 'basicConfig',
4246 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004247
4248 recording = RecordingHandler()
4249 logging.root.addHandler(recording)
4250
4251 log_method = getattr(logging, method)
4252 if level is not None:
4253 log_method(level, "test me: %r", recording)
4254 else:
4255 log_method("test me: %r", recording)
4256
4257 self.assertEqual(len(recording.records), 1)
4258 record = recording.records[0]
4259 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4260
4261 expected_level = level if level is not None else getattr(logging, method.upper())
4262 self.assertEqual(record.levelno, expected_level)
4263
4264 # basicConfig was not called!
4265 self.assertEqual(called, [])
4266
4267 def test_log(self):
4268 self._test_log('log', logging.ERROR)
4269
4270 def test_debug(self):
4271 self._test_log('debug')
4272
4273 def test_info(self):
4274 self._test_log('info')
4275
4276 def test_warning(self):
4277 self._test_log('warning')
4278
4279 def test_error(self):
4280 self._test_log('error')
4281
4282 def test_critical(self):
4283 self._test_log('critical')
4284
4285 def test_set_logger_class(self):
4286 self.assertRaises(TypeError, logging.setLoggerClass, object)
4287
4288 class MyLogger(logging.Logger):
4289 pass
4290
4291 logging.setLoggerClass(MyLogger)
4292 self.assertEqual(logging.getLoggerClass(), MyLogger)
4293
4294 logging.setLoggerClass(logging.Logger)
4295 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4296
Vinay Sajip01500012019-06-19 11:46:53 +01004297 def test_subclass_logger_cache(self):
4298 # bpo-37258
4299 message = []
4300
4301 class MyLogger(logging.getLoggerClass()):
4302 def __init__(self, name='MyLogger', level=logging.NOTSET):
4303 super().__init__(name, level)
4304 message.append('initialized')
4305
4306 logging.setLoggerClass(MyLogger)
4307 logger = logging.getLogger('just_some_logger')
4308 self.assertEqual(message, ['initialized'])
4309 stream = io.StringIO()
4310 h = logging.StreamHandler(stream)
4311 logger.addHandler(h)
4312 try:
4313 logger.setLevel(logging.DEBUG)
4314 logger.debug("hello")
4315 self.assertEqual(stream.getvalue().strip(), "hello")
4316
4317 stream.truncate(0)
4318 stream.seek(0)
4319
4320 logger.setLevel(logging.INFO)
4321 logger.debug("hello")
4322 self.assertEqual(stream.getvalue(), "")
4323 finally:
4324 logger.removeHandler(h)
4325 h.close()
4326 logging.setLoggerClass(logging.Logger)
4327
Antoine Pitrou712cb732013-12-21 15:51:54 +01004328 def test_logging_at_shutdown(self):
Victor Stinner45df61f2020-11-02 23:17:46 +01004329 # bpo-20037: Doing text I/O late at interpreter shutdown must not crash
4330 code = textwrap.dedent("""
Antoine Pitrou712cb732013-12-21 15:51:54 +01004331 import logging
4332
4333 class A:
4334 def __del__(self):
4335 try:
4336 raise ValueError("some error")
4337 except Exception:
4338 logging.exception("exception in __del__")
4339
Victor Stinner45df61f2020-11-02 23:17:46 +01004340 a = A()
4341 """)
Antoine Pitrou712cb732013-12-21 15:51:54 +01004342 rc, out, err = assert_python_ok("-c", code)
4343 err = err.decode()
4344 self.assertIn("exception in __del__", err)
4345 self.assertIn("ValueError: some error", err)
4346
Victor Stinner45df61f2020-11-02 23:17:46 +01004347 def test_logging_at_shutdown_open(self):
4348 # bpo-26789: FileHandler keeps a reference to the builtin open()
4349 # function to be able to open or reopen the file during Python
4350 # finalization.
4351 filename = os_helper.TESTFN
4352 self.addCleanup(os_helper.unlink, filename)
4353
4354 code = textwrap.dedent(f"""
4355 import builtins
4356 import logging
4357
4358 class A:
4359 def __del__(self):
4360 logging.error("log in __del__")
4361
4362 # basicConfig() opens the file, but logging.shutdown() closes
4363 # it at Python exit. When A.__del__() is called,
4364 # FileHandler._open() must be called again to re-open the file.
Inada Naokifb786922021-04-06 11:18:41 +09004365 logging.basicConfig(filename={filename!r}, encoding="utf-8")
Victor Stinner45df61f2020-11-02 23:17:46 +01004366
4367 a = A()
4368
4369 # Simulate the Python finalization which removes the builtin
4370 # open() function.
4371 del builtins.open
4372 """)
4373 assert_python_ok("-c", code)
4374
Inada Naokifb786922021-04-06 11:18:41 +09004375 with open(filename, encoding="utf-8") as fp:
Victor Stinner45df61f2020-11-02 23:17:46 +01004376 self.assertEqual(fp.read().rstrip(), "ERROR:root:log in __del__")
4377
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004378 def test_recursion_error(self):
4379 # Issue 36272
Victor Stinner45df61f2020-11-02 23:17:46 +01004380 code = textwrap.dedent("""
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004381 import logging
4382
4383 def rec():
4384 logging.error("foo")
4385 rec()
4386
Victor Stinner45df61f2020-11-02 23:17:46 +01004387 rec()
4388 """)
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004389 rc, out, err = assert_python_failure("-c", code)
4390 err = err.decode()
4391 self.assertNotIn("Cannot recover from stack overflow.", err)
4392 self.assertEqual(rc, 1)
4393
Antoine Pitrou712cb732013-12-21 15:51:54 +01004394
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004395class LogRecordTest(BaseTest):
4396 def test_str_rep(self):
4397 r = logging.makeLogRecord({})
4398 s = str(r)
4399 self.assertTrue(s.startswith('<LogRecord: '))
4400 self.assertTrue(s.endswith('>'))
4401
4402 def test_dict_arg(self):
4403 h = RecordingHandler()
4404 r = logging.getLogger()
4405 r.addHandler(h)
4406 d = {'less' : 'more' }
4407 logging.warning('less is %(less)s', d)
4408 self.assertIs(h.records[0].args, d)
4409 self.assertEqual(h.records[0].message, 'less is more')
4410 r.removeHandler(h)
4411 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004412
Irit Katriel3fd69992020-09-08 20:40:04 +01004413 @staticmethod # pickled as target of child process in the following test
4414 def _extract_logrecord_process_name(key, logMultiprocessing, conn=None):
4415 prev_logMultiprocessing = logging.logMultiprocessing
4416 logging.logMultiprocessing = logMultiprocessing
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004417 try:
4418 import multiprocessing as mp
Irit Katriel3fd69992020-09-08 20:40:04 +01004419 name = mp.current_process().name
4420
4421 r1 = logging.makeLogRecord({'msg': f'msg1_{key}'})
4422 del sys.modules['multiprocessing']
4423 r2 = logging.makeLogRecord({'msg': f'msg2_{key}'})
4424
4425 results = {'processName' : name,
4426 'r1.processName': r1.processName,
4427 'r2.processName': r2.processName,
4428 }
4429 finally:
4430 logging.logMultiprocessing = prev_logMultiprocessing
4431 if conn:
4432 conn.send(results)
4433 else:
4434 return results
4435
4436 def test_multiprocessing(self):
4437 multiprocessing_imported = 'multiprocessing' in sys.modules
4438 try:
4439 # logMultiprocessing is True by default
4440 self.assertEqual(logging.logMultiprocessing, True)
4441
4442 LOG_MULTI_PROCESSING = True
4443 # When logMultiprocessing == True:
4444 # In the main process processName = 'MainProcess'
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004445 r = logging.makeLogRecord({})
Irit Katriel3fd69992020-09-08 20:40:04 +01004446 self.assertEqual(r.processName, 'MainProcess')
4447
4448 results = self._extract_logrecord_process_name(1, LOG_MULTI_PROCESSING)
4449 self.assertEqual('MainProcess', results['processName'])
4450 self.assertEqual('MainProcess', results['r1.processName'])
4451 self.assertEqual('MainProcess', results['r2.processName'])
4452
4453 # In other processes, processName is correct when multiprocessing in imported,
4454 # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762).
4455 import multiprocessing
4456 parent_conn, child_conn = multiprocessing.Pipe()
4457 p = multiprocessing.Process(
4458 target=self._extract_logrecord_process_name,
4459 args=(2, LOG_MULTI_PROCESSING, child_conn,)
4460 )
4461 p.start()
4462 results = parent_conn.recv()
4463 self.assertNotEqual('MainProcess', results['processName'])
4464 self.assertEqual(results['processName'], results['r1.processName'])
4465 self.assertEqual('MainProcess', results['r2.processName'])
4466 p.join()
4467
4468 finally:
4469 if multiprocessing_imported:
4470 import multiprocessing
4471
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004472
4473 def test_optional(self):
4474 r = logging.makeLogRecord({})
4475 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004476 NOT_NONE(r.thread)
4477 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004478 NOT_NONE(r.process)
4479 NOT_NONE(r.processName)
4480 log_threads = logging.logThreads
4481 log_processes = logging.logProcesses
4482 log_multiprocessing = logging.logMultiprocessing
4483 try:
4484 logging.logThreads = False
4485 logging.logProcesses = False
4486 logging.logMultiprocessing = False
4487 r = logging.makeLogRecord({})
4488 NONE = self.assertIsNone
4489 NONE(r.thread)
4490 NONE(r.threadName)
4491 NONE(r.process)
4492 NONE(r.processName)
4493 finally:
4494 logging.logThreads = log_threads
4495 logging.logProcesses = log_processes
4496 logging.logMultiprocessing = log_multiprocessing
4497
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004498class BasicConfigTest(unittest.TestCase):
4499
Vinay Sajip95bf5042011-04-20 11:50:56 +01004500 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004501
4502 def setUp(self):
4503 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004504 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004505 self.saved_handlers = logging._handlers.copy()
4506 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004507 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004508 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004509 logging.root.handlers = []
4510
4511 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004512 for h in logging.root.handlers[:]:
4513 logging.root.removeHandler(h)
4514 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004515 super(BasicConfigTest, self).tearDown()
4516
Vinay Sajip3def7e02011-04-20 10:58:06 +01004517 def cleanup(self):
4518 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004519 logging._handlers.clear()
4520 logging._handlers.update(self.saved_handlers)
4521 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004522 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004523
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004524 def test_no_kwargs(self):
4525 logging.basicConfig()
4526
4527 # handler defaults to a StreamHandler to sys.stderr
4528 self.assertEqual(len(logging.root.handlers), 1)
4529 handler = logging.root.handlers[0]
4530 self.assertIsInstance(handler, logging.StreamHandler)
4531 self.assertEqual(handler.stream, sys.stderr)
4532
4533 formatter = handler.formatter
4534 # format defaults to logging.BASIC_FORMAT
4535 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4536 # datefmt defaults to None
4537 self.assertIsNone(formatter.datefmt)
4538 # style defaults to %
4539 self.assertIsInstance(formatter._style, logging.PercentStyle)
4540
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004541 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004542 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004543
Vinay Sajip1fd12022014-01-13 21:59:56 +00004544 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004545 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004546 logging.basicConfig(stream=sys.stdout, style="{")
4547 logging.error("Log an error")
4548 sys.stdout.seek(0)
4549 self.assertEqual(output.getvalue().strip(),
4550 "ERROR:root:Log an error")
4551
4552 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004553 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004554 logging.basicConfig(stream=sys.stdout, style="$")
4555 logging.error("Log an error")
4556 sys.stdout.seek(0)
4557 self.assertEqual(output.getvalue().strip(),
4558 "ERROR:root:Log an error")
4559
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004560 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004561
4562 def cleanup(h1, h2, fn):
4563 h1.close()
4564 h2.close()
4565 os.remove(fn)
4566
Inada Naokifb786922021-04-06 11:18:41 +09004567 logging.basicConfig(filename='test.log', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004568
4569 self.assertEqual(len(logging.root.handlers), 1)
4570 handler = logging.root.handlers[0]
4571 self.assertIsInstance(handler, logging.FileHandler)
4572
Inada Naokifb786922021-04-06 11:18:41 +09004573 expected = logging.FileHandler('test.log', 'a', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004574 self.assertEqual(handler.stream.mode, expected.stream.mode)
4575 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004576 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004577
4578 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004579
4580 def cleanup(h1, h2, fn):
4581 h1.close()
4582 h2.close()
4583 os.remove(fn)
4584
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004585 logging.basicConfig(filename='test.log', filemode='wb')
4586
4587 handler = logging.root.handlers[0]
4588 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004589 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004590 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004591
4592 def test_stream(self):
4593 stream = io.StringIO()
4594 self.addCleanup(stream.close)
4595 logging.basicConfig(stream=stream)
4596
4597 self.assertEqual(len(logging.root.handlers), 1)
4598 handler = logging.root.handlers[0]
4599 self.assertIsInstance(handler, logging.StreamHandler)
4600 self.assertEqual(handler.stream, stream)
4601
4602 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004603 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004604
4605 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004606 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004607
4608 def test_datefmt(self):
4609 logging.basicConfig(datefmt='bar')
4610
4611 formatter = logging.root.handlers[0].formatter
4612 self.assertEqual(formatter.datefmt, 'bar')
4613
4614 def test_style(self):
4615 logging.basicConfig(style='$')
4616
4617 formatter = logging.root.handlers[0].formatter
4618 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4619
4620 def test_level(self):
4621 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004622 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004623
4624 logging.basicConfig(level=57)
4625 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004626 # Test that second call has no effect
4627 logging.basicConfig(level=58)
4628 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004629
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004630 def test_incompatible(self):
4631 assertRaises = self.assertRaises
4632 handlers = [logging.StreamHandler()]
4633 stream = sys.stderr
4634 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004635 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004636 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004637 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004638 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004639 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004640 # Issue 23207: test for invalid kwargs
4641 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4642 # Should pop both filename and filemode even if filename is None
4643 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004644
4645 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004646 handlers = [
4647 logging.StreamHandler(),
4648 logging.StreamHandler(sys.stdout),
4649 logging.StreamHandler(),
4650 ]
4651 f = logging.Formatter()
4652 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004653 logging.basicConfig(handlers=handlers)
4654 self.assertIs(handlers[0], logging.root.handlers[0])
4655 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004656 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004657 self.assertIsNotNone(handlers[0].formatter)
4658 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004659 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004660 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4661
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004662 def test_force(self):
4663 old_string_io = io.StringIO()
4664 new_string_io = io.StringIO()
4665 old_handlers = [logging.StreamHandler(old_string_io)]
4666 new_handlers = [logging.StreamHandler(new_string_io)]
4667 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4668 logging.warning('warn')
4669 logging.info('info')
4670 logging.debug('debug')
4671 self.assertEqual(len(logging.root.handlers), 1)
4672 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4673 force=True)
4674 logging.warning('warn')
4675 logging.info('info')
4676 logging.debug('debug')
4677 self.assertEqual(len(logging.root.handlers), 1)
4678 self.assertEqual(old_string_io.getvalue().strip(),
4679 'WARNING:root:warn')
4680 self.assertEqual(new_string_io.getvalue().strip(),
4681 'WARNING:root:warn\nINFO:root:info')
4682
Vinay Sajipca7b5042019-06-17 17:40:52 +01004683 def test_encoding(self):
4684 try:
4685 encoding = 'utf-8'
4686 logging.basicConfig(filename='test.log', encoding=encoding,
4687 errors='strict',
4688 format='%(message)s', level=logging.DEBUG)
4689
4690 self.assertEqual(len(logging.root.handlers), 1)
4691 handler = logging.root.handlers[0]
4692 self.assertIsInstance(handler, logging.FileHandler)
4693 self.assertEqual(handler.encoding, encoding)
4694 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4695 finally:
4696 handler.close()
4697 with open('test.log', encoding='utf-8') as f:
4698 data = f.read().strip()
4699 os.remove('test.log')
4700 self.assertEqual(data,
4701 'The Øresund Bridge joins Copenhagen to Malmö')
4702
4703 def test_encoding_errors(self):
4704 try:
4705 encoding = 'ascii'
4706 logging.basicConfig(filename='test.log', encoding=encoding,
4707 errors='ignore',
4708 format='%(message)s', level=logging.DEBUG)
4709
4710 self.assertEqual(len(logging.root.handlers), 1)
4711 handler = logging.root.handlers[0]
4712 self.assertIsInstance(handler, logging.FileHandler)
4713 self.assertEqual(handler.encoding, encoding)
4714 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4715 finally:
4716 handler.close()
4717 with open('test.log', encoding='utf-8') as f:
4718 data = f.read().strip()
4719 os.remove('test.log')
4720 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4721
4722 def test_encoding_errors_default(self):
4723 try:
4724 encoding = 'ascii'
4725 logging.basicConfig(filename='test.log', encoding=encoding,
4726 format='%(message)s', level=logging.DEBUG)
4727
4728 self.assertEqual(len(logging.root.handlers), 1)
4729 handler = logging.root.handlers[0]
4730 self.assertIsInstance(handler, logging.FileHandler)
4731 self.assertEqual(handler.encoding, encoding)
4732 self.assertEqual(handler.errors, 'backslashreplace')
4733 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4734 finally:
4735 handler.close()
4736 with open('test.log', encoding='utf-8') as f:
4737 data = f.read().strip()
4738 os.remove('test.log')
4739 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4740 r'Bridge joins Copenhagen to Malm\xf6')
4741
4742 def test_encoding_errors_none(self):
4743 # Specifying None should behave as 'strict'
4744 try:
4745 encoding = 'ascii'
4746 logging.basicConfig(filename='test.log', encoding=encoding,
4747 errors=None,
4748 format='%(message)s', level=logging.DEBUG)
4749
4750 self.assertEqual(len(logging.root.handlers), 1)
4751 handler = logging.root.handlers[0]
4752 self.assertIsInstance(handler, logging.FileHandler)
4753 self.assertEqual(handler.encoding, encoding)
4754 self.assertIsNone(handler.errors)
4755
4756 message = []
4757
4758 def dummy_handle_error(record):
4759 _, v, _ = sys.exc_info()
4760 message.append(str(v))
4761
4762 handler.handleError = dummy_handle_error
4763 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4764 self.assertTrue(message)
4765 self.assertIn("'ascii' codec can't encode "
4766 "character '\\xd8' in position 4:", message[0])
4767 finally:
4768 handler.close()
4769 with open('test.log', encoding='utf-8') as f:
4770 data = f.read().strip()
4771 os.remove('test.log')
4772 # didn't write anything due to the encoding error
4773 self.assertEqual(data, r'')
4774
4775
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004776 def _test_log(self, method, level=None):
4777 # logging.root has no handlers so basicConfig should be called
4778 called = []
4779
4780 old_basic_config = logging.basicConfig
4781 def my_basic_config(*a, **kw):
4782 old_basic_config()
4783 old_level = logging.root.level
4784 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004785 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004786 called.append((a, kw))
4787
Vinay Sajip1feedb42014-05-31 08:19:12 +01004788 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004789
4790 log_method = getattr(logging, method)
4791 if level is not None:
4792 log_method(level, "test me")
4793 else:
4794 log_method("test me")
4795
4796 # basicConfig was called with no arguments
4797 self.assertEqual(called, [((), {})])
4798
4799 def test_log(self):
4800 self._test_log('log', logging.WARNING)
4801
4802 def test_debug(self):
4803 self._test_log('debug')
4804
4805 def test_info(self):
4806 self._test_log('info')
4807
4808 def test_warning(self):
4809 self._test_log('warning')
4810
4811 def test_error(self):
4812 self._test_log('error')
4813
4814 def test_critical(self):
4815 self._test_log('critical')
4816
4817
4818class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004819 def setUp(self):
4820 super(LoggerAdapterTest, self).setUp()
4821 old_handler_list = logging._handlerList[:]
4822
4823 self.recording = RecordingHandler()
4824 self.logger = logging.root
4825 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004826 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004827 self.addCleanup(self.recording.close)
4828
4829 def cleanup():
4830 logging._handlerList[:] = old_handler_list
4831
4832 self.addCleanup(cleanup)
4833 self.addCleanup(logging.shutdown)
4834 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4835
4836 def test_exception(self):
4837 msg = 'testing exception: %r'
4838 exc = None
4839 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004840 1 / 0
4841 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004842 exc = e
4843 self.adapter.exception(msg, self.recording)
4844
4845 self.assertEqual(len(self.recording.records), 1)
4846 record = self.recording.records[0]
4847 self.assertEqual(record.levelno, logging.ERROR)
4848 self.assertEqual(record.msg, msg)
4849 self.assertEqual(record.args, (self.recording,))
4850 self.assertEqual(record.exc_info,
4851 (exc.__class__, exc, exc.__traceback__))
4852
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004853 def test_exception_excinfo(self):
4854 try:
4855 1 / 0
4856 except ZeroDivisionError as e:
4857 exc = e
4858
4859 self.adapter.exception('exc_info test', exc_info=exc)
4860
4861 self.assertEqual(len(self.recording.records), 1)
4862 record = self.recording.records[0]
4863 self.assertEqual(record.exc_info,
4864 (exc.__class__, exc, exc.__traceback__))
4865
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004866 def test_critical(self):
4867 msg = 'critical test! %r'
4868 self.adapter.critical(msg, self.recording)
4869
4870 self.assertEqual(len(self.recording.records), 1)
4871 record = self.recording.records[0]
4872 self.assertEqual(record.levelno, logging.CRITICAL)
4873 self.assertEqual(record.msg, msg)
4874 self.assertEqual(record.args, (self.recording,))
4875
4876 def test_is_enabled_for(self):
4877 old_disable = self.adapter.logger.manager.disable
4878 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004879 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4880 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004881 self.assertFalse(self.adapter.isEnabledFor(32))
4882
4883 def test_has_handlers(self):
4884 self.assertTrue(self.adapter.hasHandlers())
4885
4886 for handler in self.logger.handlers:
4887 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004888
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004889 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004890 self.assertFalse(self.adapter.hasHandlers())
4891
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004892 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004893 class Adapter(logging.LoggerAdapter):
4894 prefix = 'Adapter'
4895
4896 def process(self, msg, kwargs):
4897 return f"{self.prefix} {msg}", kwargs
4898
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004899 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004900 adapter = Adapter(logger=self.logger, extra=None)
4901 adapter_adapter = Adapter(logger=adapter, extra=None)
4902 adapter_adapter.prefix = 'AdapterAdapter'
4903 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004904 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004905 self.assertEqual(len(self.recording.records), 1)
4906 record = self.recording.records[0]
4907 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004908 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004909 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004910 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004911 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004912 self.assertIs(self.logger.manager, orig_manager)
4913 temp_manager = object()
4914 try:
4915 adapter_adapter.manager = temp_manager
4916 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004917 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004918 self.assertIs(self.logger.manager, temp_manager)
4919 finally:
4920 adapter_adapter.manager = orig_manager
4921 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004922 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004923 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004924
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004925
Eric Larson9fdb76c2020-09-25 14:08:50 -04004926class LoggerTest(BaseTest, AssertErrorMessage):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004927
4928 def setUp(self):
4929 super(LoggerTest, self).setUp()
4930 self.recording = RecordingHandler()
4931 self.logger = logging.Logger(name='blah')
4932 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004933 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004934 self.addCleanup(self.recording.close)
4935 self.addCleanup(logging.shutdown)
4936
4937 def test_set_invalid_level(self):
Eric Larson9fdb76c2020-09-25 14:08:50 -04004938 self.assert_error_message(
4939 TypeError, 'Level not an integer or a valid string: None',
4940 self.logger.setLevel, None)
4941 self.assert_error_message(
4942 TypeError, 'Level not an integer or a valid string: (0, 0)',
4943 self.logger.setLevel, (0, 0))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004944
4945 def test_exception(self):
4946 msg = 'testing exception: %r'
4947 exc = None
4948 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004949 1 / 0
4950 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004951 exc = e
4952 self.logger.exception(msg, self.recording)
4953
4954 self.assertEqual(len(self.recording.records), 1)
4955 record = self.recording.records[0]
4956 self.assertEqual(record.levelno, logging.ERROR)
4957 self.assertEqual(record.msg, msg)
4958 self.assertEqual(record.args, (self.recording,))
4959 self.assertEqual(record.exc_info,
4960 (exc.__class__, exc, exc.__traceback__))
4961
4962 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004963 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004964 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004965
4966 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004967 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004968 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004969
4970 def test_find_caller_with_stack_info(self):
4971 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004972 support.patch(self, logging.traceback, 'print_stack',
4973 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004974
4975 self.logger.findCaller(stack_info=True)
4976
4977 self.assertEqual(len(called), 1)
4978 self.assertEqual('Stack (most recent call last):\n', called[0])
4979
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004980 def test_find_caller_with_stacklevel(self):
4981 the_level = 1
4982
4983 def innermost():
4984 self.logger.warning('test', stacklevel=the_level)
4985
4986 def inner():
4987 innermost()
4988
4989 def outer():
4990 inner()
4991
4992 records = self.recording.records
4993 outer()
4994 self.assertEqual(records[-1].funcName, 'innermost')
4995 lineno = records[-1].lineno
4996 the_level += 1
4997 outer()
4998 self.assertEqual(records[-1].funcName, 'inner')
4999 self.assertGreater(records[-1].lineno, lineno)
5000 lineno = records[-1].lineno
5001 the_level += 1
5002 outer()
5003 self.assertEqual(records[-1].funcName, 'outer')
5004 self.assertGreater(records[-1].lineno, lineno)
5005 lineno = records[-1].lineno
5006 the_level += 1
5007 outer()
5008 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5009 self.assertGreater(records[-1].lineno, lineno)
5010
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005011 def test_make_record_with_extra_overwrite(self):
5012 name = 'my record'
5013 level = 13
5014 fn = lno = msg = args = exc_info = func = sinfo = None
5015 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
5016 exc_info, func, sinfo)
5017
5018 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
5019 extra = {key: 'some value'}
5020 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
5021 fn, lno, msg, args, exc_info,
5022 extra=extra, sinfo=sinfo)
5023
5024 def test_make_record_with_extra_no_overwrite(self):
5025 name = 'my record'
5026 level = 13
5027 fn = lno = msg = args = exc_info = func = sinfo = None
5028 extra = {'valid_key': 'some value'}
5029 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
5030 exc_info, extra=extra, sinfo=sinfo)
5031 self.assertIn('valid_key', result.__dict__)
5032
5033 def test_has_handlers(self):
5034 self.assertTrue(self.logger.hasHandlers())
5035
5036 for handler in self.logger.handlers:
5037 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005038 self.assertFalse(self.logger.hasHandlers())
5039
5040 def test_has_handlers_no_propagate(self):
5041 child_logger = logging.getLogger('blah.child')
5042 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005043 self.assertFalse(child_logger.hasHandlers())
5044
5045 def test_is_enabled_for(self):
5046 old_disable = self.logger.manager.disable
5047 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01005048 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005049 self.assertFalse(self.logger.isEnabledFor(22))
5050
Timo Furrer6e3ca642018-06-01 09:29:46 +02005051 def test_is_enabled_for_disabled_logger(self):
5052 old_disabled = self.logger.disabled
5053 old_disable = self.logger.manager.disable
5054
5055 self.logger.disabled = True
5056 self.logger.manager.disable = 21
5057
5058 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
5059 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
5060
5061 self.assertFalse(self.logger.isEnabledFor(22))
5062
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005063 def test_root_logger_aliases(self):
5064 root = logging.getLogger()
5065 self.assertIs(root, logging.root)
5066 self.assertIs(root, logging.getLogger(None))
5067 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01005068 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005069 self.assertIs(root, logging.getLogger('foo').root)
5070 self.assertIs(root, logging.getLogger('foo.bar').root)
5071 self.assertIs(root, logging.getLogger('foo').parent)
5072
5073 self.assertIsNot(root, logging.getLogger('\0'))
5074 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
5075
5076 def test_invalid_names(self):
5077 self.assertRaises(TypeError, logging.getLogger, any)
5078 self.assertRaises(TypeError, logging.getLogger, b'foo')
5079
Vinay Sajip6260d9f2017-06-06 16:34:29 +01005080 def test_pickling(self):
5081 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
5082 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
5083 logger = logging.getLogger(name)
5084 s = pickle.dumps(logger, proto)
5085 unpickled = pickle.loads(s)
5086 self.assertIs(unpickled, logger)
5087
Avram Lubkin78c18a92017-07-30 05:36:33 -04005088 def test_caching(self):
5089 root = self.root_logger
5090 logger1 = logging.getLogger("abc")
5091 logger2 = logging.getLogger("abc.def")
5092
5093 # Set root logger level and ensure cache is empty
5094 root.setLevel(logging.ERROR)
5095 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
5096 self.assertEqual(logger2._cache, {})
5097
5098 # Ensure cache is populated and calls are consistent
5099 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5100 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
5101 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
5102 self.assertEqual(root._cache, {})
5103 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5104
5105 # Ensure root cache gets populated
5106 self.assertEqual(root._cache, {})
5107 self.assertTrue(root.isEnabledFor(logging.ERROR))
5108 self.assertEqual(root._cache, {logging.ERROR: True})
5109
5110 # Set parent logger level and ensure caches are emptied
5111 logger1.setLevel(logging.CRITICAL)
5112 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5113 self.assertEqual(logger2._cache, {})
5114
5115 # Ensure logger2 uses parent logger's effective level
5116 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5117
5118 # Set level to NOTSET and ensure caches are empty
5119 logger2.setLevel(logging.NOTSET)
5120 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5121 self.assertEqual(logger2._cache, {})
5122 self.assertEqual(logger1._cache, {})
5123 self.assertEqual(root._cache, {})
5124
5125 # Verify logger2 follows parent and not root
5126 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5127 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
5128 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
5129 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
5130 self.assertTrue(root.isEnabledFor(logging.ERROR))
5131
5132 # Disable logging in manager and ensure caches are clear
5133 logging.disable()
5134 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5135 self.assertEqual(logger2._cache, {})
5136 self.assertEqual(logger1._cache, {})
5137 self.assertEqual(root._cache, {})
5138
5139 # Ensure no loggers are enabled
5140 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5141 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5142 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5143
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005144
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005145class BaseFileTest(BaseTest):
5146 "Base class for handler tests that write log files"
5147
5148 def setUp(self):
5149 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005150 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5151 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005152 self.rmfiles = []
5153
5154 def tearDown(self):
5155 for fn in self.rmfiles:
5156 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005157 if os.path.exists(self.fn):
5158 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005159 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005160
5161 def assertLogFile(self, filename):
5162 "Assert a log file is there and register it for deletion"
5163 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005164 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005165 self.rmfiles.append(filename)
5166
5167
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005168class FileHandlerTest(BaseFileTest):
5169 def test_delay(self):
5170 os.unlink(self.fn)
Inada Naokifb786922021-04-06 11:18:41 +09005171 fh = logging.FileHandler(self.fn, encoding='utf-8', delay=True)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005172 self.assertIsNone(fh.stream)
5173 self.assertFalse(os.path.exists(self.fn))
5174 fh.handle(logging.makeLogRecord({}))
5175 self.assertIsNotNone(fh.stream)
5176 self.assertTrue(os.path.exists(self.fn))
5177 fh.close()
5178
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005179class RotatingFileHandlerTest(BaseFileTest):
5180 def next_rec(self):
5181 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5182 self.next_message(), None, None, None)
5183
5184 def test_should_not_rollover(self):
5185 # If maxbytes is zero rollover never occurs
Inada Naokifb786922021-04-06 11:18:41 +09005186 rh = logging.handlers.RotatingFileHandler(
5187 self.fn, encoding="utf-8", maxBytes=0)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005188 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005189 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005190
5191 def test_should_rollover(self):
Inada Naokifb786922021-04-06 11:18:41 +09005192 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8", maxBytes=1)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005193 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005194 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005195
5196 def test_file_created(self):
5197 # checks that the file is created and assumes it was created
5198 # by us
Inada Naokifb786922021-04-06 11:18:41 +09005199 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8")
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005200 rh.emit(self.next_rec())
5201 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005202 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005203
5204 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005205 def namer(name):
5206 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005207 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005208 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005209 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005210 rh.emit(self.next_rec())
5211 self.assertLogFile(self.fn)
5212 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005213 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005214 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005215 self.assertLogFile(namer(self.fn + ".2"))
5216 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5217 rh.close()
5218
l0rb519cb872019-11-06 22:21:40 +01005219 def test_namer_rotator_inheritance(self):
5220 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5221 def namer(self, name):
5222 return name + ".test"
5223
5224 def rotator(self, source, dest):
5225 if os.path.exists(source):
Victor Stinnerfe52eb62021-04-28 15:47:10 +02005226 os.replace(source, dest + ".rotated")
l0rb519cb872019-11-06 22:21:40 +01005227
5228 rh = HandlerWithNamerAndRotator(
Inada Naokifb786922021-04-06 11:18:41 +09005229 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
l0rb519cb872019-11-06 22:21:40 +01005230 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5231 rh.emit(self.next_rec())
5232 self.assertLogFile(self.fn)
5233 rh.emit(self.next_rec())
5234 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5235 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5236 rh.close()
5237
Hai Shia3ec3ad2020-05-19 06:02:57 +08005238 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005239 def test_rotator(self):
5240 def namer(name):
5241 return name + ".gz"
5242
5243 def rotator(source, dest):
5244 with open(source, "rb") as sf:
5245 data = sf.read()
5246 compressed = zlib.compress(data, 9)
5247 with open(dest, "wb") as df:
5248 df.write(compressed)
5249 os.remove(source)
5250
5251 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005252 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005253 rh.rotator = rotator
5254 rh.namer = namer
5255 m1 = self.next_rec()
5256 rh.emit(m1)
5257 self.assertLogFile(self.fn)
5258 m2 = self.next_rec()
5259 rh.emit(m2)
5260 fn = namer(self.fn + ".1")
5261 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005262 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005263 with open(fn, "rb") as f:
5264 compressed = f.read()
5265 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005266 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005267 rh.emit(self.next_rec())
5268 fn = namer(self.fn + ".2")
5269 self.assertLogFile(fn)
5270 with open(fn, "rb") as f:
5271 compressed = f.read()
5272 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005273 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005274 rh.emit(self.next_rec())
5275 fn = namer(self.fn + ".2")
5276 with open(fn, "rb") as f:
5277 compressed = f.read()
5278 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005279 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005280 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005281 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005282
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005283class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005284 # other test methods added below
5285 def test_rollover(self):
Inada Naokifa51c0c2021-04-29 11:34:56 +09005286 fh = logging.handlers.TimedRotatingFileHandler(
5287 self.fn, 'S', encoding="utf-8", backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005288 fmt = logging.Formatter('%(asctime)s %(message)s')
5289 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005290 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005291 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005292 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005293 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005294 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005295 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005296 fh.close()
5297 # At this point, we should have a recent rotated file which we
5298 # can test for the existence of. However, in practice, on some
5299 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005300 # in time to go to look for the log file. So, we go back a fair
5301 # bit, and stop as soon as we see a rotated file. In theory this
5302 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005303 found = False
5304 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005305 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005306 for secs in range(GO_BACK):
5307 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005308 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5309 found = os.path.exists(fn)
5310 if found:
5311 self.rmfiles.append(fn)
5312 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005313 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5314 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005315 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005316 dn, fn = os.path.split(self.fn)
5317 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005318 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5319 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005320 for f in files:
5321 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005322 path = os.path.join(dn, f)
5323 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005324 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005325 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005326
Vinay Sajip0372e102011-05-05 12:59:14 +01005327 def test_invalid(self):
5328 assertRaises = self.assertRaises
5329 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005330 self.fn, 'X', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005331 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005332 self.fn, 'W', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005333 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005334 self.fn, 'W7', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005335
Vinay Sajipa7130792013-04-12 17:04:23 +01005336 def test_compute_rollover_daily_attime(self):
5337 currentTime = 0
5338 atTime = datetime.time(12, 0, 0)
5339 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005340 self.fn, encoding="utf-8", when='MIDNIGHT', interval=1, backupCount=0,
5341 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005342 try:
5343 actual = rh.computeRollover(currentTime)
5344 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005345
Vinay Sajipd86ac962013-04-14 12:20:46 +01005346 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5347 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5348 finally:
5349 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005350
Vinay Sajip10e8c492013-05-18 10:19:54 -07005351 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005352 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005353 currentTime = int(time.time())
5354 today = currentTime - currentTime % 86400
5355
Vinay Sajipa7130792013-04-12 17:04:23 +01005356 atTime = datetime.time(12, 0, 0)
5357
Vinay Sajip10e8c492013-05-18 10:19:54 -07005358 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005359 for day in range(7):
5360 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005361 self.fn, encoding="utf-8", when='W%d' % day, interval=1, backupCount=0,
5362 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005363 try:
5364 if wday > day:
5365 # The rollover day has already passed this week, so we
5366 # go over into next week
5367 expected = (7 - wday + day)
5368 else:
5369 expected = (day - wday)
5370 # At this point expected is in days from now, convert to seconds
5371 expected *= 24 * 60 * 60
5372 # Add in the rollover time
5373 expected += 12 * 60 * 60
5374 # Add in adjustment for today
5375 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005376 actual = rh.computeRollover(today)
5377 if actual != expected:
5378 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005379 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005380 self.assertEqual(actual, expected)
5381 if day == wday:
5382 # goes into following week
5383 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005384 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005385 if actual != expected:
5386 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005387 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005388 self.assertEqual(actual, expected)
5389 finally:
5390 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005391
5392
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005393def secs(**kw):
5394 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5395
5396for when, exp in (('S', 1),
5397 ('M', 60),
5398 ('H', 60 * 60),
5399 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005400 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005401 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005402 ('W0', secs(days=4, hours=24)),
5403 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005404 def test_compute_rollover(self, when=when, exp=exp):
5405 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005406 self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
Vinay Sajiped0473c2011-02-26 15:35:38 +00005407 currentTime = 0.0
5408 actual = rh.computeRollover(currentTime)
5409 if exp != actual:
5410 # Failures occur on some systems for MIDNIGHT and W0.
5411 # Print detailed calculation for MIDNIGHT so we can try to see
5412 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005413 if when == 'MIDNIGHT':
5414 try:
5415 if rh.utc:
5416 t = time.gmtime(currentTime)
5417 else:
5418 t = time.localtime(currentTime)
5419 currentHour = t[3]
5420 currentMinute = t[4]
5421 currentSecond = t[5]
5422 # r is the number of seconds left between now and midnight
5423 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5424 currentMinute) * 60 +
5425 currentSecond)
5426 result = currentTime + r
5427 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5428 print('currentHour: %s' % currentHour, file=sys.stderr)
5429 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5430 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5431 print('r: %s' % r, file=sys.stderr)
5432 print('result: %s' % result, file=sys.stderr)
5433 except Exception:
5434 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5435 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005436 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005437 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5438
Vinay Sajip60ccd822011-05-09 17:32:09 +01005439
Vinay Sajip223349c2015-10-01 20:37:54 +01005440@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005441class NTEventLogHandlerTest(BaseTest):
5442 def test_basic(self):
5443 logtype = 'Application'
5444 elh = win32evtlog.OpenEventLog(None, logtype)
5445 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005446
5447 try:
5448 h = logging.handlers.NTEventLogHandler('test_logging')
5449 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005450 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005451 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005452 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005453
Vinay Sajip60ccd822011-05-09 17:32:09 +01005454 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5455 h.handle(r)
5456 h.close()
5457 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005458 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005459 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5460 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5461 found = False
5462 GO_BACK = 100
5463 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5464 for e in events:
5465 if e.SourceName != 'test_logging':
5466 continue
5467 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5468 if msg != 'Test Log Message\r\n':
5469 continue
5470 found = True
5471 break
5472 msg = 'Record not found in event log, went back %d records' % GO_BACK
5473 self.assertTrue(found, msg=msg)
5474
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005475
5476class MiscTestCase(unittest.TestCase):
5477 def test__all__(self):
Victor Stinnerfbf43f02020-08-17 07:20:40 +02005478 not_exported = {
5479 'logThreads', 'logMultiprocessing', 'logProcesses', 'currentframe',
5480 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5481 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', 'root',
5482 'threading'}
5483 support.check__all__(self, logging, not_exported=not_exported)
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005484
5485
Christian Heimes180510d2008-03-03 19:15:45 +00005486# Set the locale to the platform-dependent default. I have no idea
5487# why the test does this, but in any case we save the current locale
5488# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005489@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005490def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005491 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005492 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5493 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5494 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5495 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5496 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5497 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5498 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5499 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005500 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005501 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005502 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005503 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005504 ]
5505 if hasattr(logging.handlers, 'QueueListener'):
5506 tests.append(QueueListenerTest)
5507 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005508
Christian Heimes180510d2008-03-03 19:15:45 +00005509if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005510 test_main()