blob: 5794b75ba3f6f1bf89f51d16292a654fa6b100e6 [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
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020057from http.server import HTTPServer, BaseHTTPRequestHandler
Antoine Pitroua6a4dc82017-09-07 18:56:24 +020058from urllib.parse import urlparse, parse_qs
59from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
60 ThreadingTCPServer, StreamRequestHandler)
61
Miss Islington (bot)8bec9fb2021-06-24 16:38:01 -070062with warnings.catch_warnings():
63 warnings.simplefilter('ignore', DeprecationWarning)
64 import asyncore
65 import smtpd
66
Vinay Sajip60ccd822011-05-09 17:32:09 +010067try:
Vinay Sajip223349c2015-10-01 20:37:54 +010068 import win32evtlog, win32evtlogutil, pywintypes
Vinay Sajip60ccd822011-05-09 17:32:09 +010069except ImportError:
Vinay Sajip223349c2015-10-01 20:37:54 +010070 win32evtlog = win32evtlogutil = pywintypes = None
71
Eric V. Smith851cad72012-03-11 22:46:04 -070072try:
73 import zlib
74except ImportError:
75 pass
Christian Heimes18c66892008-02-17 13:31:39 +000076
Christian Heimes180510d2008-03-03 19:15:45 +000077class BaseTest(unittest.TestCase):
78
79 """Base class for logging tests."""
80
81 log_format = "%(name)s -> %(levelname)s: %(message)s"
Ezio Melotti3ffd29b2012-10-02 19:45:00 +030082 expected_log_pat = r"^([\w.]+) -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +000083 message_num = 0
84
85 def setUp(self):
86 """Setup the default logging stream to an internal StringIO instance,
87 so that we can examine log output as we want."""
Hai Shie80697d2020-05-28 06:10:27 +080088 self._threading_key = threading_helper.threading_setup()
Victor Stinner69669602017-08-18 23:47:54 +020089
Christian Heimes180510d2008-03-03 19:15:45 +000090 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000091 logging._acquireLock()
92 try:
Christian Heimes180510d2008-03-03 19:15:45 +000093 self.saved_handlers = logging._handlers.copy()
94 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000095 self.saved_loggers = saved_loggers = logger_dict.copy()
Vinay Sajip3b84eae2013-05-25 03:20:34 -070096 self.saved_name_to_level = logging._nameToLevel.copy()
97 self.saved_level_to_name = logging._levelToName.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000098 self.logger_states = logger_states = {}
99 for name in saved_loggers:
100 logger_states[name] = getattr(saved_loggers[name],
101 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +0000102 finally:
103 logging._releaseLock()
104
Florent Xicluna5252f9f2011-11-07 19:43:05 +0100105 # Set two unused loggers
Vinay Sajipb4a08092010-09-20 09:55:00 +0000106 self.logger1 = logging.getLogger("\xab\xd7\xbb")
107 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000108
Christian Heimes180510d2008-03-03 19:15:45 +0000109 self.root_logger = logging.getLogger("")
110 self.original_logging_level = self.root_logger.getEffectiveLevel()
111
112 self.stream = io.StringIO()
113 self.root_logger.setLevel(logging.DEBUG)
114 self.root_hdlr = logging.StreamHandler(self.stream)
115 self.root_formatter = logging.Formatter(self.log_format)
116 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000117 if self.logger1.hasHandlers():
118 hlist = self.logger1.handlers + self.root_logger.handlers
119 raise AssertionError('Unexpected handlers: %s' % hlist)
120 if self.logger2.hasHandlers():
121 hlist = self.logger2.handlers + self.root_logger.handlers
122 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000123 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000124 self.assertTrue(self.logger1.hasHandlers())
125 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000126
127 def tearDown(self):
128 """Remove our logging stream, and restore the original logging
129 level."""
130 self.stream.close()
131 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000132 while self.root_logger.handlers:
133 h = self.root_logger.handlers[0]
134 self.root_logger.removeHandler(h)
135 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000136 self.root_logger.setLevel(self.original_logging_level)
137 logging._acquireLock()
138 try:
Vinay Sajip3b84eae2013-05-25 03:20:34 -0700139 logging._levelToName.clear()
140 logging._levelToName.update(self.saved_level_to_name)
141 logging._nameToLevel.clear()
142 logging._nameToLevel.update(self.saved_name_to_level)
Christian Heimes180510d2008-03-03 19:15:45 +0000143 logging._handlers.clear()
144 logging._handlers.update(self.saved_handlers)
145 logging._handlerList[:] = self.saved_handler_list
Avram Lubkin78c18a92017-07-30 05:36:33 -0400146 manager = logging.getLogger().manager
147 manager.disable = 0
148 loggerDict = manager.loggerDict
Christian Heimes180510d2008-03-03 19:15:45 +0000149 loggerDict.clear()
150 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000151 logger_states = self.logger_states
152 for name in self.logger_states:
153 if logger_states[name] is not None:
154 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000155 finally:
156 logging._releaseLock()
157
Victor Stinner69669602017-08-18 23:47:54 +0200158 self.doCleanups()
Hai Shie80697d2020-05-28 06:10:27 +0800159 threading_helper.threading_cleanup(*self._threading_key)
Victor Stinner69669602017-08-18 23:47:54 +0200160
Vinay Sajip4ded5512012-10-02 15:56:16 +0100161 def assert_log_lines(self, expected_values, stream=None, pat=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000162 """Match the collected log lines against the regular expression
163 self.expected_log_pat, and compare the extracted group values to
164 the expected_values list of tuples."""
165 stream = stream or self.stream
Vinay Sajip4ded5512012-10-02 15:56:16 +0100166 pat = re.compile(pat or self.expected_log_pat)
Serhiy Storchaka50254c52013-08-29 11:35:43 +0300167 actual_lines = stream.getvalue().splitlines()
Vinay Sajip5056c8c2011-06-05 09:31:34 +0100168 self.assertEqual(len(actual_lines), len(expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000169 for actual, expected in zip(actual_lines, expected_values):
170 match = pat.search(actual)
171 if not match:
172 self.fail("Log line does not match expected pattern:\n" +
173 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000174 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000175 s = stream.read()
176 if s:
177 self.fail("Remaining output at end of log stream:\n" + s)
178
179 def next_message(self):
180 """Generate a message consisting solely of an auto-incrementing
181 integer."""
182 self.message_num += 1
183 return "%d" % self.message_num
184
185
186class BuiltinLevelsTest(BaseTest):
187 """Test builtin levels and their inheritance."""
188
189 def test_flat(self):
Mike53f7a7c2017-12-14 14:04:53 +0300190 # Logging levels in a flat logger namespace.
Christian Heimes180510d2008-03-03 19:15:45 +0000191 m = self.next_message
192
193 ERR = logging.getLogger("ERR")
194 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000195 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000196 INF.setLevel(logging.INFO)
197 DEB = logging.getLogger("DEB")
198 DEB.setLevel(logging.DEBUG)
199
200 # These should log.
201 ERR.log(logging.CRITICAL, m())
202 ERR.error(m())
203
204 INF.log(logging.CRITICAL, m())
205 INF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100206 INF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000207 INF.info(m())
208
209 DEB.log(logging.CRITICAL, m())
210 DEB.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100211 DEB.warning(m())
212 DEB.info(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000213 DEB.debug(m())
214
215 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100216 ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000217 ERR.info(m())
218 ERR.debug(m())
219
220 INF.debug(m())
221
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000222 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000223 ('ERR', 'CRITICAL', '1'),
224 ('ERR', 'ERROR', '2'),
225 ('INF', 'CRITICAL', '3'),
226 ('INF', 'ERROR', '4'),
227 ('INF', 'WARNING', '5'),
228 ('INF', 'INFO', '6'),
229 ('DEB', 'CRITICAL', '7'),
230 ('DEB', 'ERROR', '8'),
231 ('DEB', 'WARNING', '9'),
232 ('DEB', 'INFO', '10'),
233 ('DEB', 'DEBUG', '11'),
234 ])
235
236 def test_nested_explicit(self):
237 # Logging levels in a nested namespace, all explicitly set.
238 m = self.next_message
239
240 INF = logging.getLogger("INF")
241 INF.setLevel(logging.INFO)
242 INF_ERR = logging.getLogger("INF.ERR")
243 INF_ERR.setLevel(logging.ERROR)
244
245 # These should log.
246 INF_ERR.log(logging.CRITICAL, m())
247 INF_ERR.error(m())
248
249 # These should not log.
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100250 INF_ERR.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000251 INF_ERR.info(m())
252 INF_ERR.debug(m())
253
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000254 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000255 ('INF.ERR', 'CRITICAL', '1'),
256 ('INF.ERR', 'ERROR', '2'),
257 ])
258
259 def test_nested_inherited(self):
Mike53f7a7c2017-12-14 14:04:53 +0300260 # Logging levels in a nested namespace, inherited from parent loggers.
Christian Heimes180510d2008-03-03 19:15:45 +0000261 m = self.next_message
262
263 INF = logging.getLogger("INF")
264 INF.setLevel(logging.INFO)
265 INF_ERR = logging.getLogger("INF.ERR")
266 INF_ERR.setLevel(logging.ERROR)
267 INF_UNDEF = logging.getLogger("INF.UNDEF")
268 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
269 UNDEF = logging.getLogger("UNDEF")
270
271 # These should log.
272 INF_UNDEF.log(logging.CRITICAL, m())
273 INF_UNDEF.error(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100274 INF_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000275 INF_UNDEF.info(m())
276 INF_ERR_UNDEF.log(logging.CRITICAL, m())
277 INF_ERR_UNDEF.error(m())
278
279 # These should not log.
280 INF_UNDEF.debug(m())
Vinay Sajipb9e46e92011-10-26 13:17:20 +0100281 INF_ERR_UNDEF.warning(m())
Christian Heimes180510d2008-03-03 19:15:45 +0000282 INF_ERR_UNDEF.info(m())
283 INF_ERR_UNDEF.debug(m())
284
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000285 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000286 ('INF.UNDEF', 'CRITICAL', '1'),
287 ('INF.UNDEF', 'ERROR', '2'),
288 ('INF.UNDEF', 'WARNING', '3'),
289 ('INF.UNDEF', 'INFO', '4'),
290 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
291 ('INF.ERR.UNDEF', 'ERROR', '6'),
292 ])
293
294 def test_nested_with_virtual_parent(self):
295 # Logging levels when some parent does not exist yet.
296 m = self.next_message
297
298 INF = logging.getLogger("INF")
299 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
300 CHILD = logging.getLogger("INF.BADPARENT")
301 INF.setLevel(logging.INFO)
302
303 # These should log.
304 GRANDCHILD.log(logging.FATAL, m())
305 GRANDCHILD.info(m())
306 CHILD.log(logging.FATAL, m())
307 CHILD.info(m())
308
309 # These should not log.
310 GRANDCHILD.debug(m())
311 CHILD.debug(m())
312
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000313 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000314 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
315 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
316 ('INF.BADPARENT', 'CRITICAL', '3'),
317 ('INF.BADPARENT', 'INFO', '4'),
318 ])
319
Vinay Sajipd1d4fbf2014-09-11 23:06:09 +0100320 def test_regression_22386(self):
321 """See issue #22386 for more information."""
322 self.assertEqual(logging.getLevelName('INFO'), logging.INFO)
323 self.assertEqual(logging.getLevelName(logging.INFO), 'INFO')
Christian Heimes180510d2008-03-03 19:15:45 +0000324
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100325 def test_issue27935(self):
326 fatal = logging.getLevelName('FATAL')
327 self.assertEqual(fatal, logging.FATAL)
328
Vinay Sajip924aaae2017-01-11 17:35:36 +0000329 def test_regression_29220(self):
330 """See issue #29220 for more information."""
331 logging.addLevelName(logging.INFO, '')
332 self.addCleanup(logging.addLevelName, logging.INFO, 'INFO')
333 self.assertEqual(logging.getLevelName(logging.INFO), '')
334 self.assertEqual(logging.getLevelName(logging.NOTSET), 'NOTSET')
335 self.assertEqual(logging.getLevelName('NOTSET'), logging.NOTSET)
336
Christian Heimes180510d2008-03-03 19:15:45 +0000337class BasicFilterTest(BaseTest):
338
339 """Test the bundled Filter class."""
340
341 def test_filter(self):
342 # Only messages satisfying the specified criteria pass through the
343 # filter.
344 filter_ = logging.Filter("spam.eggs")
345 handler = self.root_logger.handlers[0]
346 try:
347 handler.addFilter(filter_)
348 spam = logging.getLogger("spam")
349 spam_eggs = logging.getLogger("spam.eggs")
350 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
351 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
352
353 spam.info(self.next_message())
354 spam_eggs.info(self.next_message()) # Good.
355 spam_eggs_fish.info(self.next_message()) # Good.
356 spam_bakedbeans.info(self.next_message())
357
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000358 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000359 ('spam.eggs', 'INFO', '2'),
360 ('spam.eggs.fish', 'INFO', '3'),
361 ])
362 finally:
363 handler.removeFilter(filter_)
364
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000365 def test_callable_filter(self):
366 # Only messages satisfying the specified criteria pass through the
367 # filter.
368
369 def filterfunc(record):
370 parts = record.name.split('.')
371 prefix = '.'.join(parts[:2])
372 return prefix == 'spam.eggs'
373
374 handler = self.root_logger.handlers[0]
375 try:
376 handler.addFilter(filterfunc)
377 spam = logging.getLogger("spam")
378 spam_eggs = logging.getLogger("spam.eggs")
379 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
380 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
381
382 spam.info(self.next_message())
383 spam_eggs.info(self.next_message()) # Good.
384 spam_eggs_fish.info(self.next_message()) # Good.
385 spam_bakedbeans.info(self.next_message())
386
387 self.assert_log_lines([
388 ('spam.eggs', 'INFO', '2'),
389 ('spam.eggs.fish', 'INFO', '3'),
390 ])
391 finally:
392 handler.removeFilter(filterfunc)
393
Vinay Sajip985ef872011-04-26 19:34:04 +0100394 def test_empty_filter(self):
395 f = logging.Filter()
396 r = logging.makeLogRecord({'name': 'spam.eggs'})
397 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000398
399#
400# First, we define our levels. There can be as many as you want - the only
401# limitations are that they should be integers, the lowest should be > 0 and
402# larger values mean less information being logged. If you need specific
403# level values which do not fit into these limitations, you can use a
404# mapping dictionary to convert between your application levels and the
405# logging system.
406#
407SILENT = 120
408TACITURN = 119
409TERSE = 118
410EFFUSIVE = 117
411SOCIABLE = 116
412VERBOSE = 115
413TALKATIVE = 114
414GARRULOUS = 113
415CHATTERBOX = 112
416BORING = 111
417
418LEVEL_RANGE = range(BORING, SILENT + 1)
419
420#
421# Next, we define names for our levels. You don't need to do this - in which
422# case the system will use "Level n" to denote the text for the level.
423#
424my_logging_levels = {
425 SILENT : 'Silent',
426 TACITURN : 'Taciturn',
427 TERSE : 'Terse',
428 EFFUSIVE : 'Effusive',
429 SOCIABLE : 'Sociable',
430 VERBOSE : 'Verbose',
431 TALKATIVE : 'Talkative',
432 GARRULOUS : 'Garrulous',
433 CHATTERBOX : 'Chatterbox',
434 BORING : 'Boring',
435}
436
437class GarrulousFilter(logging.Filter):
438
439 """A filter which blocks garrulous messages."""
440
441 def filter(self, record):
442 return record.levelno != GARRULOUS
443
444class VerySpecificFilter(logging.Filter):
445
446 """A filter which blocks sociable and taciturn messages."""
447
448 def filter(self, record):
449 return record.levelno not in [SOCIABLE, TACITURN]
450
451
452class CustomLevelsAndFiltersTest(BaseTest):
453
454 """Test various filtering possibilities with custom logging levels."""
455
456 # Skip the logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +0300457 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +0000458
459 def setUp(self):
460 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000461 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000462 logging.addLevelName(k, v)
463
464 def log_at_all_levels(self, logger):
465 for lvl in LEVEL_RANGE:
466 logger.log(lvl, self.next_message())
467
468 def test_logger_filter(self):
469 # Filter at logger level.
470 self.root_logger.setLevel(VERBOSE)
471 # Levels >= 'Verbose' are good.
472 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000473 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000474 ('Verbose', '5'),
475 ('Sociable', '6'),
476 ('Effusive', '7'),
477 ('Terse', '8'),
478 ('Taciturn', '9'),
479 ('Silent', '10'),
480 ])
481
482 def test_handler_filter(self):
483 # Filter at handler level.
484 self.root_logger.handlers[0].setLevel(SOCIABLE)
485 try:
486 # Levels >= 'Sociable' are good.
487 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000488 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000489 ('Sociable', '6'),
490 ('Effusive', '7'),
491 ('Terse', '8'),
492 ('Taciturn', '9'),
493 ('Silent', '10'),
494 ])
495 finally:
496 self.root_logger.handlers[0].setLevel(logging.NOTSET)
497
498 def test_specific_filters(self):
499 # Set a specific filter object on the handler, and then add another
500 # filter object on the logger itself.
501 handler = self.root_logger.handlers[0]
502 specific_filter = None
503 garr = GarrulousFilter()
504 handler.addFilter(garr)
505 try:
506 self.log_at_all_levels(self.root_logger)
507 first_lines = [
508 # Notice how 'Garrulous' is missing
509 ('Boring', '1'),
510 ('Chatterbox', '2'),
511 ('Talkative', '4'),
512 ('Verbose', '5'),
513 ('Sociable', '6'),
514 ('Effusive', '7'),
515 ('Terse', '8'),
516 ('Taciturn', '9'),
517 ('Silent', '10'),
518 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000519 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000520
521 specific_filter = VerySpecificFilter()
522 self.root_logger.addFilter(specific_filter)
523 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000524 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000525 # Not only 'Garrulous' is still missing, but also 'Sociable'
526 # and 'Taciturn'
527 ('Boring', '11'),
528 ('Chatterbox', '12'),
529 ('Talkative', '14'),
530 ('Verbose', '15'),
531 ('Effusive', '17'),
532 ('Terse', '18'),
533 ('Silent', '20'),
534 ])
535 finally:
536 if specific_filter:
537 self.root_logger.removeFilter(specific_filter)
538 handler.removeFilter(garr)
539
540
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100541class HandlerTest(BaseTest):
542 def test_name(self):
543 h = logging.Handler()
544 h.name = 'generic'
545 self.assertEqual(h.name, 'generic')
546 h.name = 'anothergeneric'
547 self.assertEqual(h.name, 'anothergeneric')
548 self.assertRaises(NotImplementedError, h.emit, None)
549
Vinay Sajip5a35b062011-04-27 11:31:14 +0100550 def test_builtin_handlers(self):
551 # We can't actually *use* too many handlers in the tests,
552 # but we can try instantiating them with various options
Victor Stinnere6747472011-08-21 00:39:18 +0200553 if sys.platform in ('linux', 'darwin'):
Vinay Sajip5a35b062011-04-27 11:31:14 +0100554 for existing in (True, False):
555 fd, fn = tempfile.mkstemp()
556 os.close(fd)
557 if not existing:
558 os.unlink(fn)
Inada Naokifb786922021-04-06 11:18:41 +0900559 h = logging.handlers.WatchedFileHandler(fn, encoding='utf-8', delay=True)
Vinay Sajip5a35b062011-04-27 11:31:14 +0100560 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100561 dev, ino = h.dev, h.ino
Vinay Sajipa5798de2012-04-24 23:33:33 +0100562 self.assertEqual(dev, -1)
563 self.assertEqual(ino, -1)
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100564 r = logging.makeLogRecord({'msg': 'Test'})
565 h.handle(r)
566 # Now remove the file.
567 os.unlink(fn)
568 self.assertFalse(os.path.exists(fn))
569 # The next call should recreate the file.
570 h.handle(r)
571 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100572 else:
573 self.assertEqual(h.dev, -1)
574 self.assertEqual(h.ino, -1)
575 h.close()
576 if existing:
577 os.unlink(fn)
578 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100579 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100580 else:
581 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100582 try:
583 h = logging.handlers.SysLogHandler(sockname)
584 self.assertEqual(h.facility, h.LOG_USER)
585 self.assertTrue(h.unixsocket)
586 h.close()
Andrew Svetlov0832af62012-12-18 23:10:48 +0200587 except OSError: # syslogd might not be available
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100588 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100589 for method in ('GET', 'POST', 'PUT'):
590 if method == 'PUT':
591 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
592 'localhost', '/log', method)
593 else:
594 h = logging.handlers.HTTPHandler('localhost', '/log', method)
595 h.close()
596 h = logging.handlers.BufferingHandler(0)
597 r = logging.makeLogRecord({})
598 self.assertTrue(h.shouldFlush(r))
599 h.close()
600 h = logging.handlers.BufferingHandler(1)
601 self.assertFalse(h.shouldFlush(r))
602 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100603
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100604 def test_path_objects(self):
605 """
606 Test that Path objects are accepted as filename arguments to handlers.
607
608 See Issue #27493.
609 """
610 fd, fn = tempfile.mkstemp()
611 os.close(fd)
612 os.unlink(fn)
613 pfn = pathlib.Path(fn)
614 cases = (
615 (logging.FileHandler, (pfn, 'w')),
616 (logging.handlers.RotatingFileHandler, (pfn, 'a')),
617 (logging.handlers.TimedRotatingFileHandler, (pfn, 'h')),
618 )
619 if sys.platform in ('linux', 'darwin'):
620 cases += ((logging.handlers.WatchedFileHandler, (pfn, 'w')),)
621 for cls, args in cases:
Inada Naokifb786922021-04-06 11:18:41 +0900622 h = cls(*args, encoding="utf-8")
Vinay Sajipdb8f4632016-09-08 01:37:03 +0100623 self.assertTrue(os.path.exists(fn))
624 h.close()
625 os.unlink(fn)
626
Vinay Sajip5e86eed2012-05-29 22:48:10 +0100627 @unittest.skipIf(os.name == 'nt', 'WatchedFileHandler not appropriate for Windows.')
Vinay Sajipa5798de2012-04-24 23:33:33 +0100628 def test_race(self):
629 # Issue #14632 refers.
630 def remove_loop(fname, tries):
631 for _ in range(tries):
632 try:
633 os.unlink(fname)
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000634 self.deletion_time = time.time()
Vinay Sajipa5798de2012-04-24 23:33:33 +0100635 except OSError:
636 pass
637 time.sleep(0.004 * random.randint(0, 4))
638
Vinay Sajipc94871a2012-04-25 10:51:35 +0100639 del_count = 500
640 log_count = 500
Vinay Sajipa5798de2012-04-24 23:33:33 +0100641
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000642 self.handle_time = None
643 self.deletion_time = None
644
Vinay Sajipa5798de2012-04-24 23:33:33 +0100645 for delay in (False, True):
Vinay Sajipc94871a2012-04-25 10:51:35 +0100646 fd, fn = tempfile.mkstemp('.log', 'test_logging-3-')
647 os.close(fd)
648 remover = threading.Thread(target=remove_loop, args=(fn, del_count))
649 remover.daemon = True
650 remover.start()
Inada Naokifb786922021-04-06 11:18:41 +0900651 h = logging.handlers.WatchedFileHandler(fn, encoding='utf-8', delay=delay)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100652 f = logging.Formatter('%(asctime)s: %(levelname)s: %(message)s')
653 h.setFormatter(f)
Vinay Sajipc94871a2012-04-25 10:51:35 +0100654 try:
655 for _ in range(log_count):
656 time.sleep(0.005)
657 r = logging.makeLogRecord({'msg': 'testing' })
Vinay Sajipdc96ca22013-12-03 12:31:23 +0000658 try:
659 self.handle_time = time.time()
660 h.handle(r)
661 except Exception:
662 print('Deleted at %s, '
663 'opened at %s' % (self.deletion_time,
664 self.handle_time))
665 raise
Vinay Sajipc94871a2012-04-25 10:51:35 +0100666 finally:
Vinay Sajipc94871a2012-04-25 10:51:35 +0100667 remover.join()
Vinay Sajipac20f462012-05-03 12:09:38 +0100668 h.close()
Vinay Sajipc94871a2012-04-25 10:51:35 +0100669 if os.path.exists(fn):
670 os.unlink(fn)
Vinay Sajipa5798de2012-04-24 23:33:33 +0100671
Gregory P. Smith19003842018-09-13 22:08:31 -0700672 # The implementation relies on os.register_at_fork existing, but we test
673 # based on os.fork existing because that is what users and this test use.
674 # This helps ensure that when fork exists (the important concept) that the
675 # register_at_fork mechanism is also present and used.
676 @unittest.skipIf(not hasattr(os, 'fork'), 'Test requires os.fork().')
677 def test_post_fork_child_no_deadlock(self):
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400678 """Ensure child logging locks are not held; bpo-6721 & bpo-36533."""
679 class _OurHandler(logging.Handler):
680 def __init__(self):
681 super().__init__()
682 self.sub_handler = logging.StreamHandler(
Inada Naokifb786922021-04-06 11:18:41 +0900683 stream=open('/dev/null', 'wt', encoding='utf-8'))
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400684
685 def emit(self, record):
686 self.sub_handler.acquire()
687 try:
688 self.sub_handler.emit(record)
689 finally:
690 self.sub_handler.release()
691
692 self.assertEqual(len(logging._handlers), 0)
693 refed_h = _OurHandler()
Xtreak2c105382019-05-13 20:18:52 +0530694 self.addCleanup(refed_h.sub_handler.stream.close)
Gregory P. Smith19003842018-09-13 22:08:31 -0700695 refed_h.name = 'because we need at least one for this test'
696 self.assertGreater(len(logging._handlers), 0)
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400697 self.assertGreater(len(logging._at_fork_reinit_lock_weakset), 1)
698 test_logger = logging.getLogger('test_post_fork_child_no_deadlock')
699 test_logger.addHandler(refed_h)
700 test_logger.setLevel(logging.DEBUG)
Gregory P. Smith19003842018-09-13 22:08:31 -0700701
702 locks_held__ready_to_fork = threading.Event()
703 fork_happened__release_locks_and_end_thread = threading.Event()
704
705 def lock_holder_thread_fn():
706 logging._acquireLock()
707 try:
708 refed_h.acquire()
709 try:
710 # Tell the main thread to do the fork.
711 locks_held__ready_to_fork.set()
712
713 # If the deadlock bug exists, the fork will happen
714 # without dealing with the locks we hold, deadlocking
715 # the child.
716
717 # Wait for a successful fork or an unreasonable amount of
718 # time before releasing our locks. To avoid a timing based
719 # test we'd need communication from os.fork() as to when it
720 # has actually happened. Given this is a regression test
721 # for a fixed issue, potentially less reliably detecting
722 # regression via timing is acceptable for simplicity.
723 # The test will always take at least this long. :(
724 fork_happened__release_locks_and_end_thread.wait(0.5)
725 finally:
726 refed_h.release()
727 finally:
728 logging._releaseLock()
729
730 lock_holder_thread = threading.Thread(
731 target=lock_holder_thread_fn,
732 name='test_post_fork_child_no_deadlock lock holder')
733 lock_holder_thread.start()
734
735 locks_held__ready_to_fork.wait()
736 pid = os.fork()
Victor Stinner278c1e12020-03-31 20:08:12 +0200737 if pid == 0:
738 # Child process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400739 try:
740 test_logger.info(r'Child process did not deadlock. \o/')
741 finally:
742 os._exit(0)
Victor Stinner278c1e12020-03-31 20:08:12 +0200743 else:
744 # Parent process
Gregory P. Smith64aa6d22019-05-07 12:18:20 -0400745 test_logger.info(r'Parent process returned from fork. \o/')
Gregory P. Smith19003842018-09-13 22:08:31 -0700746 fork_happened__release_locks_and_end_thread.set()
747 lock_holder_thread.join()
Victor Stinner278c1e12020-03-31 20:08:12 +0200748
749 support.wait_process(pid, exitcode=0)
Gregory P. Smith19003842018-09-13 22:08:31 -0700750
Vinay Sajipa5798de2012-04-24 23:33:33 +0100751
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100752class BadStream(object):
753 def write(self, data):
754 raise RuntimeError('deliberate mistake')
755
756class TestStreamHandler(logging.StreamHandler):
757 def handleError(self, record):
758 self.error_record = record
759
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200760class StreamWithIntName(object):
761 level = logging.NOTSET
762 name = 2
763
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100764class StreamHandlerTest(BaseTest):
765 def test_error_handling(self):
766 h = TestStreamHandler(BadStream())
767 r = logging.makeLogRecord({})
768 old_raise = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +0100769
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100770 try:
771 h.handle(r)
772 self.assertIs(h.error_record, r)
Vinay Sajip1feedb42014-05-31 08:19:12 +0100773
Vinay Sajip985ef872011-04-26 19:34:04 +0100774 h = logging.StreamHandler(BadStream())
Vinay Sajip1feedb42014-05-31 08:19:12 +0100775 with support.captured_stderr() as stderr:
776 h.handle(r)
777 msg = '\nRuntimeError: deliberate mistake\n'
778 self.assertIn(msg, stderr.getvalue())
779
Vinay Sajip985ef872011-04-26 19:34:04 +0100780 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +0100781 with support.captured_stderr() as stderr:
782 h.handle(r)
783 self.assertEqual('', stderr.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100784 finally:
785 logging.raiseExceptions = old_raise
786
Vinay Sajip2543f502017-07-30 10:41:45 +0100787 def test_stream_setting(self):
788 """
789 Test setting the handler's stream
790 """
791 h = logging.StreamHandler()
792 stream = io.StringIO()
793 old = h.setStream(stream)
794 self.assertIs(old, sys.stderr)
795 actual = h.setStream(old)
796 self.assertIs(actual, stream)
797 # test that setting to existing value returns None
798 actual = h.setStream(old)
799 self.assertIsNone(actual)
800
Riccardo Magliocchettica87eeb2019-05-07 23:36:39 +0200801 def test_can_represent_stream_with_int_name(self):
802 h = logging.StreamHandler(StreamWithIntName())
803 self.assertEqual(repr(h), '<StreamHandler 2 (NOTSET)>')
804
Vinay Sajip7367d082011-05-02 13:17:27 +0100805# -- The following section could be moved into a server_helper.py module
806# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100807
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200808class TestSMTPServer(smtpd.SMTPServer):
809 """
810 This class implements a test SMTP server.
Vinay Sajipa463d252011-04-30 21:52:48 +0100811
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200812 :param addr: A (host, port) tuple which the server listens on.
813 You can specify a port value of zero: the server's
814 *port* attribute will hold the actual port number
815 used, which can be used in client connections.
816 :param handler: A callable which will be called to process
817 incoming messages. The handler will be passed
818 the client address tuple, who the message is from,
819 a list of recipients and the message data.
820 :param poll_interval: The interval, in seconds, used in the underlying
821 :func:`select` or :func:`poll` call by
822 :func:`asyncore.loop`.
823 :param sockmap: A dictionary which will be used to hold
824 :class:`asyncore.dispatcher` instances used by
825 :func:`asyncore.loop`. This avoids changing the
826 :mod:`asyncore` module's global state.
827 """
828
829 def __init__(self, addr, handler, poll_interval, sockmap):
830 smtpd.SMTPServer.__init__(self, addr, None, map=sockmap,
831 decode_data=True)
832 self.port = self.socket.getsockname()[1]
833 self._handler = handler
834 self._thread = None
Victor Stinner75ec1032021-04-16 14:32:01 +0200835 self._quit = False
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200836 self.poll_interval = poll_interval
837
838 def process_message(self, peer, mailfrom, rcpttos, data):
839 """
840 Delegates to the handler passed in to the server's constructor.
841
842 Typically, this will be a test case method.
843 :param peer: The client (host, port) tuple.
844 :param mailfrom: The address of the sender.
845 :param rcpttos: The addresses of the recipients.
846 :param data: The message.
847 """
848 self._handler(peer, mailfrom, rcpttos, data)
849
850 def start(self):
851 """
852 Start the server running on a separate daemon thread.
853 """
854 self._thread = t = threading.Thread(target=self.serve_forever,
855 args=(self.poll_interval,))
Christian Heimes95bbb332021-04-12 13:12:36 +0200856 t.daemon = True
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200857 t.start()
858
859 def serve_forever(self, poll_interval):
860 """
861 Run the :mod:`asyncore` loop until normal termination
862 conditions arise.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100863 :param poll_interval: The interval, in seconds, used in the underlying
864 :func:`select` or :func:`poll` call by
865 :func:`asyncore.loop`.
866 """
Victor Stinner75ec1032021-04-16 14:32:01 +0200867 while not self._quit:
868 asyncore.loop(poll_interval, map=self._map, count=1)
Vinay Sajipce7c9782011-05-17 07:15:53 +0100869
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100870 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200871 """
872 Stop the thread by closing the server instance.
873 Wait for the server thread to terminate.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200874 """
Victor Stinner75ec1032021-04-16 14:32:01 +0200875 self._quit = True
Hai Shie80697d2020-05-28 06:10:27 +0800876 threading_helper.join_thread(self._thread)
Victor Stinnerb9b69002017-09-14 14:40:56 -0700877 self._thread = None
Victor Stinner75ec1032021-04-16 14:32:01 +0200878 self.close()
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200879 asyncore.close_all(map=self._map, ignore_all=True)
Vinay Sajipa463d252011-04-30 21:52:48 +0100880
Vinay Sajip7367d082011-05-02 13:17:27 +0100881
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200882class ControlMixin(object):
883 """
884 This mixin is used to start a server on a separate thread, and
885 shut it down programmatically. Request handling is simplified - instead
886 of needing to derive a suitable RequestHandler subclass, you just
887 provide a callable which will be passed each received request to be
888 processed.
Vinay Sajip7367d082011-05-02 13:17:27 +0100889
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200890 :param handler: A handler callable which will be called with a
891 single parameter - the request - in order to
892 process the request. This handler is called on the
893 server thread, effectively meaning that requests are
Miss Islington (bot)6fc1efa2021-07-26 15:34:32 -0700894 processed serially. While not quite web scale ;-),
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200895 this should be fine for testing applications.
896 :param poll_interval: The polling interval in seconds.
897 """
898 def __init__(self, handler, poll_interval):
899 self._thread = None
900 self.poll_interval = poll_interval
901 self._handler = handler
902 self.ready = threading.Event()
Vinay Sajip7367d082011-05-02 13:17:27 +0100903
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200904 def start(self):
905 """
906 Create a daemon thread to run the server, and start it.
907 """
908 self._thread = t = threading.Thread(target=self.serve_forever,
909 args=(self.poll_interval,))
Christian Heimes95bbb332021-04-12 13:12:36 +0200910 t.daemon = True
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200911 t.start()
Vinay Sajip7367d082011-05-02 13:17:27 +0100912
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200913 def serve_forever(self, poll_interval):
914 """
915 Run the server. Set the ready flag before entering the
916 service loop.
917 """
918 self.ready.set()
919 super(ControlMixin, self).serve_forever(poll_interval)
920
Victor Stinnerbbc8b792019-12-10 20:41:23 +0100921 def stop(self):
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200922 """
923 Tell the server thread to stop, and wait for it to do so.
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200924 """
925 self.shutdown()
926 if self._thread is not None:
Hai Shie80697d2020-05-28 06:10:27 +0800927 threading_helper.join_thread(self._thread)
Vinay Sajip7367d082011-05-02 13:17:27 +0100928 self._thread = None
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200929 self.server_close()
930 self.ready.clear()
Vinay Sajip7367d082011-05-02 13:17:27 +0100931
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200932class TestHTTPServer(ControlMixin, HTTPServer):
933 """
934 An HTTP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100935
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200936 :param addr: A tuple with the IP address and port to listen on.
937 :param handler: A handler callable which will be called with a
938 single parameter - the request - in order to
939 process the request.
940 :param poll_interval: The polling interval in seconds.
941 :param log: Pass ``True`` to enable log messages.
942 """
943 def __init__(self, addr, handler, poll_interval=0.5,
944 log=False, sslctx=None):
945 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
946 def __getattr__(self, name, default=None):
947 if name.startswith('do_'):
948 return self.process_request
949 raise AttributeError(name)
Vinay Sajip7367d082011-05-02 13:17:27 +0100950
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200951 def process_request(self):
952 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +0100953
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200954 def log_message(self, format, *args):
955 if log:
956 super(DelegatingHTTPRequestHandler,
957 self).log_message(format, *args)
958 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
959 ControlMixin.__init__(self, handler, poll_interval)
960 self.sslctx = sslctx
Vinay Sajip7367d082011-05-02 13:17:27 +0100961
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200962 def get_request(self):
963 try:
964 sock, addr = self.socket.accept()
965 if self.sslctx:
966 sock = self.sslctx.wrap_socket(sock, server_side=True)
967 except OSError as e:
968 # socket errors are silenced by the caller, print them here
969 sys.stderr.write("Got an error:\n%s\n" % e)
970 raise
971 return sock, addr
Vinay Sajip7367d082011-05-02 13:17:27 +0100972
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200973class TestTCPServer(ControlMixin, ThreadingTCPServer):
974 """
975 A TCP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +0100976
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200977 :param addr: A tuple with the IP address and port to listen on.
978 :param handler: A handler callable which will be called with a single
979 parameter - the request - in order to process the request.
980 :param poll_interval: The polling interval in seconds.
981 :bind_and_activate: If True (the default), binds the server and starts it
982 listening. If False, you need to call
983 :meth:`server_bind` and :meth:`server_activate` at
984 some later time before calling :meth:`start`, so that
985 the server will set up the socket and listen on it.
986 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100987
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200988 allow_reuse_address = True
Vinay Sajip7367d082011-05-02 13:17:27 +0100989
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200990 def __init__(self, addr, handler, poll_interval=0.5,
991 bind_and_activate=True):
992 class DelegatingTCPRequestHandler(StreamRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +0100993
Antoine Pitroua6a4dc82017-09-07 18:56:24 +0200994 def handle(self):
995 self.server._handler(self)
996 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
997 bind_and_activate)
998 ControlMixin.__init__(self, handler, poll_interval)
Vinay Sajip32565b62011-05-21 00:34:51 +0100999
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001000 def server_bind(self):
1001 super(TestTCPServer, self).server_bind()
1002 self.port = self.socket.getsockname()[1]
Vinay Sajip7367d082011-05-02 13:17:27 +01001003
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001004class TestUDPServer(ControlMixin, ThreadingUDPServer):
1005 """
1006 A UDP server which is controllable using :class:`ControlMixin`.
Vinay Sajip7367d082011-05-02 13:17:27 +01001007
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001008 :param addr: A tuple with the IP address and port to listen on.
1009 :param handler: A handler callable which will be called with a
1010 single parameter - the request - in order to
1011 process the request.
1012 :param poll_interval: The polling interval for shutdown requests,
1013 in seconds.
1014 :bind_and_activate: If True (the default), binds the server and
1015 starts it listening. If False, you need to
1016 call :meth:`server_bind` and
1017 :meth:`server_activate` at some later time
1018 before calling :meth:`start`, so that the server will
1019 set up the socket and listen on it.
1020 """
1021 def __init__(self, addr, handler, poll_interval=0.5,
1022 bind_and_activate=True):
1023 class DelegatingUDPRequestHandler(DatagramRequestHandler):
Vinay Sajip7367d082011-05-02 13:17:27 +01001024
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001025 def handle(self):
1026 self.server._handler(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001027
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001028 def finish(self):
1029 data = self.wfile.getvalue()
1030 if data:
1031 try:
1032 super(DelegatingUDPRequestHandler, self).finish()
1033 except OSError:
1034 if not self.server._closed:
1035 raise
Vinay Sajipce7c9782011-05-17 07:15:53 +01001036
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001037 ThreadingUDPServer.__init__(self, addr,
1038 DelegatingUDPRequestHandler,
1039 bind_and_activate)
1040 ControlMixin.__init__(self, handler, poll_interval)
1041 self._closed = False
Vinay Sajipce7c9782011-05-17 07:15:53 +01001042
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001043 def server_bind(self):
1044 super(TestUDPServer, self).server_bind()
1045 self.port = self.socket.getsockname()[1]
Vinay Sajipce7c9782011-05-17 07:15:53 +01001046
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001047 def server_close(self):
1048 super(TestUDPServer, self).server_close()
1049 self._closed = True
Vinay Sajipce7c9782011-05-17 07:15:53 +01001050
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001051if hasattr(socket, "AF_UNIX"):
1052 class TestUnixStreamServer(TestTCPServer):
1053 address_family = socket.AF_UNIX
Vinay Sajipce7c9782011-05-17 07:15:53 +01001054
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001055 class TestUnixDatagramServer(TestUDPServer):
1056 address_family = socket.AF_UNIX
Vinay Sajip5421f352013-09-27 18:18:28 +01001057
Vinay Sajip7367d082011-05-02 13:17:27 +01001058# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +01001059
1060class SMTPHandlerTest(BaseTest):
Victor Stinnerc98b0192019-12-10 21:12:26 +01001061 # bpo-14314, bpo-19665, bpo-34092: don't wait forever
1062 TIMEOUT = support.LONG_TIMEOUT
Victor Stinner4bcfa3a2017-04-25 00:41:36 +02001063
Vinay Sajipa463d252011-04-30 21:52:48 +01001064 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +01001065 sockmap = {}
Serhiy Storchaka16994912020-04-25 10:06:29 +03001066 server = TestSMTPServer((socket_helper.HOST, 0), self.process_message, 0.001,
Vinay Sajip7367d082011-05-02 13:17:27 +01001067 sockmap)
1068 server.start()
Serhiy Storchaka16994912020-04-25 10:06:29 +03001069 addr = (socket_helper.HOST, server.port)
Vinay Sajip827f5d32013-12-03 11:28:55 +00001070 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log',
1071 timeout=self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001072 self.assertEqual(h.toaddrs, ['you'])
1073 self.messages = []
Vinay Sajip277640a2015-10-17 16:13:10 +01001074 r = logging.makeLogRecord({'msg': 'Hello \u2713'})
Vinay Sajip7367d082011-05-02 13:17:27 +01001075 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +01001076 h.handle(r)
Victor Stinner31b50b82018-07-11 12:35:38 +02001077 self.handled.wait(self.TIMEOUT)
Vinay Sajipa463d252011-04-30 21:52:48 +01001078 server.stop()
Vinay Sajip17160fd2012-03-15 12:02:08 +00001079 self.assertTrue(self.handled.is_set())
Vinay Sajipa463d252011-04-30 21:52:48 +01001080 self.assertEqual(len(self.messages), 1)
1081 peer, mailfrom, rcpttos, data = self.messages[0]
1082 self.assertEqual(mailfrom, 'me')
1083 self.assertEqual(rcpttos, ['you'])
Vinay Sajip5056c8c2011-06-05 09:31:34 +01001084 self.assertIn('\nSubject: Log\n', data)
Vinay Sajip277640a2015-10-17 16:13:10 +01001085 self.assertTrue(data.endswith('\n\nHello \u2713'))
Vinay Sajipa463d252011-04-30 21:52:48 +01001086 h.close()
1087
1088 def process_message(self, *args):
1089 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +01001090 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +01001091
Christian Heimes180510d2008-03-03 19:15:45 +00001092class MemoryHandlerTest(BaseTest):
1093
1094 """Tests for the MemoryHandler."""
1095
1096 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001097 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001098
1099 def setUp(self):
1100 BaseTest.setUp(self)
1101 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001102 self.root_hdlr)
Christian Heimes180510d2008-03-03 19:15:45 +00001103 self.mem_logger = logging.getLogger('mem')
1104 self.mem_logger.propagate = 0
1105 self.mem_logger.addHandler(self.mem_hdlr)
1106
1107 def tearDown(self):
1108 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +00001109 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +00001110
1111 def test_flush(self):
1112 # The memory handler flushes to its target handler based on specific
1113 # criteria (message count and message level).
1114 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001115 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001116 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001117 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001118 # This will flush because the level is >= logging.WARNING
Vinay Sajipb9e46e92011-10-26 13:17:20 +01001119 self.mem_logger.warning(self.next_message())
Christian Heimes180510d2008-03-03 19:15:45 +00001120 lines = [
1121 ('DEBUG', '1'),
1122 ('INFO', '2'),
1123 ('WARNING', '3'),
1124 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001125 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001126 for n in (4, 14):
1127 for i in range(9):
1128 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001129 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001130 # This will flush because it's the 10th message since the last
1131 # flush.
1132 self.mem_logger.debug(self.next_message())
1133 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001134 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001135
1136 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001137 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +00001138
Vinay Sajipdb8f4632016-09-08 01:37:03 +01001139 def test_flush_on_close(self):
1140 """
1141 Test that the flush-on-close configuration works as expected.
1142 """
1143 self.mem_logger.debug(self.next_message())
1144 self.assert_log_lines([])
1145 self.mem_logger.info(self.next_message())
1146 self.assert_log_lines([])
1147 self.mem_logger.removeHandler(self.mem_hdlr)
1148 # Default behaviour is to flush on close. Check that it happens.
1149 self.mem_hdlr.close()
1150 lines = [
1151 ('DEBUG', '1'),
1152 ('INFO', '2'),
1153 ]
1154 self.assert_log_lines(lines)
1155 # Now configure for flushing not to be done on close.
1156 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
1157 self.root_hdlr,
1158 False)
1159 self.mem_logger.addHandler(self.mem_hdlr)
1160 self.mem_logger.debug(self.next_message())
1161 self.assert_log_lines(lines) # no change
1162 self.mem_logger.info(self.next_message())
1163 self.assert_log_lines(lines) # no change
1164 self.mem_logger.removeHandler(self.mem_hdlr)
1165 self.mem_hdlr.close()
1166 # assert that no new lines have been added
1167 self.assert_log_lines(lines) # no change
1168
Irit Katriel2353d772020-08-16 16:10:13 +01001169 def test_race_between_set_target_and_flush(self):
1170 class MockRaceConditionHandler:
1171 def __init__(self, mem_hdlr):
1172 self.mem_hdlr = mem_hdlr
Victor Stinner13ff3962020-10-12 00:37:20 +02001173 self.threads = []
Irit Katriel2353d772020-08-16 16:10:13 +01001174
1175 def removeTarget(self):
1176 self.mem_hdlr.setTarget(None)
1177
1178 def handle(self, msg):
Victor Stinner13ff3962020-10-12 00:37:20 +02001179 thread = threading.Thread(target=self.removeTarget)
1180 self.threads.append(thread)
1181 thread.start()
Irit Katriel2353d772020-08-16 16:10:13 +01001182
1183 target = MockRaceConditionHandler(self.mem_hdlr)
Victor Stinner13ff3962020-10-12 00:37:20 +02001184 try:
1185 self.mem_hdlr.setTarget(target)
Irit Katriel2353d772020-08-16 16:10:13 +01001186
Victor Stinner13ff3962020-10-12 00:37:20 +02001187 for _ in range(10):
1188 time.sleep(0.005)
1189 self.mem_logger.info("not flushed")
1190 self.mem_logger.warning("flushed")
1191 finally:
1192 for thread in target.threads:
1193 threading_helper.join_thread(thread)
Irit Katriel2353d772020-08-16 16:10:13 +01001194
Christian Heimes180510d2008-03-03 19:15:45 +00001195
1196class ExceptionFormatter(logging.Formatter):
1197 """A special exception formatter."""
1198 def formatException(self, ei):
1199 return "Got a [%s]" % ei[0].__name__
1200
1201
1202class ConfigFileTest(BaseTest):
1203
1204 """Reading logging config from a .ini-style config file."""
1205
Hai Shi3ddc6342020-06-30 21:46:06 +08001206 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03001207 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Christian Heimes180510d2008-03-03 19:15:45 +00001208
1209 # config0 is a standard configuration.
1210 config0 = """
1211 [loggers]
1212 keys=root
1213
1214 [handlers]
1215 keys=hand1
1216
1217 [formatters]
1218 keys=form1
1219
1220 [logger_root]
1221 level=WARNING
1222 handlers=hand1
1223
1224 [handler_hand1]
1225 class=StreamHandler
1226 level=NOTSET
1227 formatter=form1
1228 args=(sys.stdout,)
1229
1230 [formatter_form1]
1231 format=%(levelname)s ++ %(message)s
1232 datefmt=
1233 """
1234
1235 # config1 adds a little to the standard configuration.
1236 config1 = """
1237 [loggers]
1238 keys=root,parser
1239
1240 [handlers]
1241 keys=hand1
1242
1243 [formatters]
1244 keys=form1
1245
1246 [logger_root]
1247 level=WARNING
1248 handlers=
1249
1250 [logger_parser]
1251 level=DEBUG
1252 handlers=hand1
1253 propagate=1
1254 qualname=compiler.parser
1255
1256 [handler_hand1]
1257 class=StreamHandler
1258 level=NOTSET
1259 formatter=form1
1260 args=(sys.stdout,)
1261
1262 [formatter_form1]
1263 format=%(levelname)s ++ %(message)s
1264 datefmt=
1265 """
1266
Vinay Sajip3f84b072011-03-07 17:49:33 +00001267 # config1a moves the handler to the root.
1268 config1a = """
1269 [loggers]
1270 keys=root,parser
1271
1272 [handlers]
1273 keys=hand1
1274
1275 [formatters]
1276 keys=form1
1277
1278 [logger_root]
1279 level=WARNING
1280 handlers=hand1
1281
1282 [logger_parser]
1283 level=DEBUG
1284 handlers=
1285 propagate=1
1286 qualname=compiler.parser
1287
1288 [handler_hand1]
1289 class=StreamHandler
1290 level=NOTSET
1291 formatter=form1
1292 args=(sys.stdout,)
1293
1294 [formatter_form1]
1295 format=%(levelname)s ++ %(message)s
1296 datefmt=
1297 """
1298
Christian Heimes180510d2008-03-03 19:15:45 +00001299 # config2 has a subtle configuration error that should be reported
1300 config2 = config1.replace("sys.stdout", "sys.stbout")
1301
1302 # config3 has a less subtle configuration error
1303 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1304
1305 # config4 specifies a custom formatter class to be loaded
1306 config4 = """
1307 [loggers]
1308 keys=root
1309
1310 [handlers]
1311 keys=hand1
1312
1313 [formatters]
1314 keys=form1
1315
1316 [logger_root]
1317 level=NOTSET
1318 handlers=hand1
1319
1320 [handler_hand1]
1321 class=StreamHandler
1322 level=NOTSET
1323 formatter=form1
1324 args=(sys.stdout,)
1325
1326 [formatter_form1]
1327 class=""" + __name__ + """.ExceptionFormatter
1328 format=%(levelname)s:%(name)s:%(message)s
1329 datefmt=
1330 """
1331
Georg Brandl3dbca812008-07-23 16:10:53 +00001332 # config5 specifies a custom handler class to be loaded
1333 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1334
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001335 # config6 uses ', ' delimiters in the handlers and formatters sections
1336 config6 = """
1337 [loggers]
1338 keys=root,parser
1339
1340 [handlers]
1341 keys=hand1, hand2
1342
1343 [formatters]
1344 keys=form1, form2
1345
1346 [logger_root]
1347 level=WARNING
1348 handlers=
1349
1350 [logger_parser]
1351 level=DEBUG
1352 handlers=hand1
1353 propagate=1
1354 qualname=compiler.parser
1355
1356 [handler_hand1]
1357 class=StreamHandler
1358 level=NOTSET
1359 formatter=form1
1360 args=(sys.stdout,)
1361
1362 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001363 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001364 level=NOTSET
1365 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001366 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001367
1368 [formatter_form1]
1369 format=%(levelname)s ++ %(message)s
1370 datefmt=
1371
1372 [formatter_form2]
1373 format=%(message)s
1374 datefmt=
1375 """
1376
Preston Landers6ea56d22017-08-02 15:44:28 -05001377 # config7 adds a compiler logger, and uses kwargs instead of args.
Vinay Sajip3f84b072011-03-07 17:49:33 +00001378 config7 = """
1379 [loggers]
1380 keys=root,parser,compiler
1381
1382 [handlers]
1383 keys=hand1
1384
1385 [formatters]
1386 keys=form1
1387
1388 [logger_root]
1389 level=WARNING
1390 handlers=hand1
1391
1392 [logger_compiler]
1393 level=DEBUG
1394 handlers=
1395 propagate=1
1396 qualname=compiler
1397
1398 [logger_parser]
1399 level=DEBUG
1400 handlers=
1401 propagate=1
1402 qualname=compiler.parser
1403
1404 [handler_hand1]
1405 class=StreamHandler
1406 level=NOTSET
1407 formatter=form1
Preston Landers6ea56d22017-08-02 15:44:28 -05001408 kwargs={'stream': sys.stdout,}
Vinay Sajip3f84b072011-03-07 17:49:33 +00001409
1410 [formatter_form1]
1411 format=%(levelname)s ++ %(message)s
1412 datefmt=
1413 """
1414
Xtreak087570a2018-07-02 14:27:46 +05301415 # config 8, check for resource warning
1416 config8 = r"""
1417 [loggers]
1418 keys=root
1419
1420 [handlers]
1421 keys=file
1422
1423 [formatters]
1424 keys=
1425
1426 [logger_root]
1427 level=DEBUG
1428 handlers=file
1429
1430 [handler_file]
1431 class=FileHandler
1432 level=DEBUG
1433 args=("{tempfile}",)
Inada Naokifa51c0c2021-04-29 11:34:56 +09001434 kwargs={{"encoding": "utf-8"}}
Xtreak087570a2018-07-02 14:27:46 +05301435 """
1436
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001437 disable_test = """
1438 [loggers]
1439 keys=root
1440
1441 [handlers]
1442 keys=screen
1443
1444 [formatters]
1445 keys=
1446
1447 [logger_root]
1448 level=DEBUG
1449 handlers=screen
1450
1451 [handler_screen]
1452 level=DEBUG
1453 class=StreamHandler
1454 args=(sys.stdout,)
1455 formatter=
1456 """
1457
1458 def apply_config(self, conf, **kwargs):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001459 file = io.StringIO(textwrap.dedent(conf))
Inada Naokifa51c0c2021-04-29 11:34:56 +09001460 logging.config.fileConfig(file, encoding="utf-8", **kwargs)
Christian Heimes180510d2008-03-03 19:15:45 +00001461
1462 def test_config0_ok(self):
1463 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001464 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001465 self.apply_config(self.config0)
1466 logger = logging.getLogger()
1467 # Won't output anything
1468 logger.info(self.next_message())
1469 # Outputs a message
1470 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001471 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001472 ('ERROR', '2'),
1473 ], stream=output)
1474 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001475 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001476
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001477 def test_config0_using_cp_ok(self):
1478 # A simple config file which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001479 with support.captured_stdout() as output:
Vinay Sajipcf9e2f22012-10-09 09:06:03 +01001480 file = io.StringIO(textwrap.dedent(self.config0))
1481 cp = configparser.ConfigParser()
1482 cp.read_file(file)
1483 logging.config.fileConfig(cp)
1484 logger = logging.getLogger()
1485 # Won't output anything
1486 logger.info(self.next_message())
1487 # Outputs a message
1488 logger.error(self.next_message())
1489 self.assert_log_lines([
1490 ('ERROR', '2'),
1491 ], stream=output)
1492 # Original logger output is empty.
1493 self.assert_log_lines([])
1494
Georg Brandl3dbca812008-07-23 16:10:53 +00001495 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001496 # A config file defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001497 with support.captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001498 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001499 logger = logging.getLogger("compiler.parser")
1500 # Both will output a message
1501 logger.info(self.next_message())
1502 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001503 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001504 ('INFO', '1'),
1505 ('ERROR', '2'),
1506 ], stream=output)
1507 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001508 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001509
1510 def test_config2_failure(self):
1511 # A simple config file which overrides the default settings.
1512 self.assertRaises(Exception, self.apply_config, self.config2)
1513
1514 def test_config3_failure(self):
1515 # A simple config file which overrides the default settings.
1516 self.assertRaises(Exception, self.apply_config, self.config3)
1517
1518 def test_config4_ok(self):
1519 # A config file specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01001520 with support.captured_stdout() as output:
Christian Heimes180510d2008-03-03 19:15:45 +00001521 self.apply_config(self.config4)
1522 logger = logging.getLogger()
1523 try:
1524 raise RuntimeError()
1525 except RuntimeError:
1526 logging.exception("just testing")
1527 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001528 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001529 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1530 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001531 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001532
Georg Brandl3dbca812008-07-23 16:10:53 +00001533 def test_config5_ok(self):
1534 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001535
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001536 def test_config6_ok(self):
1537 self.test_config1_ok(config=self.config6)
1538
Vinay Sajip3f84b072011-03-07 17:49:33 +00001539 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01001540 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001541 self.apply_config(self.config1a)
1542 logger = logging.getLogger("compiler.parser")
1543 # See issue #11424. compiler-hyphenated sorts
1544 # between compiler and compiler.xyz and this
1545 # was preventing compiler.xyz from being included
1546 # in the child loggers of compiler because of an
1547 # overzealous loop termination condition.
1548 hyphenated = logging.getLogger('compiler-hyphenated')
1549 # All will output a message
1550 logger.info(self.next_message())
1551 logger.error(self.next_message())
1552 hyphenated.critical(self.next_message())
1553 self.assert_log_lines([
1554 ('INFO', '1'),
1555 ('ERROR', '2'),
1556 ('CRITICAL', '3'),
1557 ], stream=output)
1558 # Original logger output is empty.
1559 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01001560 with support.captured_stdout() as output:
Vinay Sajip3f84b072011-03-07 17:49:33 +00001561 self.apply_config(self.config7)
1562 logger = logging.getLogger("compiler.parser")
1563 self.assertFalse(logger.disabled)
1564 # Both will output a message
1565 logger.info(self.next_message())
1566 logger.error(self.next_message())
1567 logger = logging.getLogger("compiler.lexer")
1568 # Both will output a message
1569 logger.info(self.next_message())
1570 logger.error(self.next_message())
1571 # Will not appear
1572 hyphenated.critical(self.next_message())
1573 self.assert_log_lines([
1574 ('INFO', '4'),
1575 ('ERROR', '5'),
1576 ('INFO', '6'),
1577 ('ERROR', '7'),
1578 ], stream=output)
1579 # Original logger output is empty.
1580 self.assert_log_lines([])
1581
Xtreak087570a2018-07-02 14:27:46 +05301582 def test_config8_ok(self):
1583
1584 def cleanup(h1, fn):
1585 h1.close()
1586 os.remove(fn)
1587
Inada Naoki53dd6c92021-04-29 20:37:32 +09001588 with self.check_no_resource_warning():
Xtreak087570a2018-07-02 14:27:46 +05301589 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
1590 os.close(fd)
1591
1592 # Replace single backslash with double backslash in windows
1593 # to avoid unicode error during string formatting
1594 if os.name == "nt":
1595 fn = fn.replace("\\", "\\\\")
1596
1597 config8 = self.config8.format(tempfile=fn)
1598
1599 self.apply_config(config8)
1600 self.apply_config(config8)
1601
1602 handler = logging.root.handlers[0]
1603 self.addCleanup(cleanup, handler, fn)
1604
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001605 def test_logger_disabling(self):
1606 self.apply_config(self.disable_test)
Antoine Pitrou04d4d6a2013-09-14 21:16:39 +02001607 logger = logging.getLogger('some_pristine_logger')
Vinay Sajip68b4cc82013-03-23 11:18:45 +00001608 self.assertFalse(logger.disabled)
1609 self.apply_config(self.disable_test)
1610 self.assertTrue(logger.disabled)
1611 self.apply_config(self.disable_test, disable_existing_loggers=False)
1612 self.assertFalse(logger.disabled)
1613
Lucas Cimonb15100f2019-10-31 09:06:25 +01001614 def test_config_set_handler_names(self):
1615 test_config = """
1616 [loggers]
1617 keys=root
1618
1619 [handlers]
1620 keys=hand1
1621
1622 [formatters]
1623 keys=form1
1624
1625 [logger_root]
1626 handlers=hand1
1627
1628 [handler_hand1]
1629 class=StreamHandler
1630 formatter=form1
1631
1632 [formatter_form1]
1633 format=%(levelname)s ++ %(message)s
1634 """
1635 self.apply_config(test_config)
l0rb991b02d2019-11-07 11:13:36 +01001636 self.assertEqual(logging.getLogger().handlers[0].name, 'hand1')
Lucas Cimonb15100f2019-10-31 09:06:25 +01001637
Łukasz Langa214f18e2018-06-08 04:02:48 -07001638 def test_defaults_do_no_interpolation(self):
1639 """bpo-33802 defaults should not get interpolated"""
1640 ini = textwrap.dedent("""
1641 [formatters]
1642 keys=default
1643
1644 [formatter_default]
1645
1646 [handlers]
1647 keys=console
1648
1649 [handler_console]
1650 class=logging.StreamHandler
1651 args=tuple()
1652
1653 [loggers]
1654 keys=root
1655
1656 [logger_root]
1657 formatter=default
1658 handlers=console
1659 """).strip()
1660 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.ini')
1661 try:
1662 os.write(fd, ini.encode('ascii'))
1663 os.close(fd)
1664 logging.config.fileConfig(
1665 fn,
Inada Naokifa51c0c2021-04-29 11:34:56 +09001666 encoding="utf-8",
Łukasz Langa214f18e2018-06-08 04:02:48 -07001667 defaults=dict(
1668 version=1,
1669 disable_existing_loggers=False,
1670 formatters={
1671 "generic": {
1672 "format": "%(asctime)s [%(process)d] [%(levelname)s] %(message)s",
1673 "datefmt": "[%Y-%m-%d %H:%M:%S %z]",
1674 "class": "logging.Formatter"
1675 },
1676 },
1677 )
1678 )
1679 finally:
1680 os.unlink(fn)
1681
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001682
Christian Heimes180510d2008-03-03 19:15:45 +00001683class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001684
Christian Heimes180510d2008-03-03 19:15:45 +00001685 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001686
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001687 server_class = TestTCPServer
1688 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001689
Christian Heimes180510d2008-03-03 19:15:45 +00001690 def setUp(self):
1691 """Set up a TCP server to receive log messages, and a SocketHandler
1692 pointing to that server's address and port."""
1693 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001694 # Issue #29177: deal with errors that happen during setup
1695 self.server = self.sock_hdlr = self.server_exception = None
1696 try:
1697 self.server = server = self.server_class(self.address,
1698 self.handle_socket, 0.01)
1699 server.start()
1700 # Uncomment next line to test error recovery in setUp()
1701 # raise OSError('dummy error raised')
1702 except OSError as e:
1703 self.server_exception = e
1704 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001705 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001706 hcls = logging.handlers.SocketHandler
1707 if isinstance(server.server_address, tuple):
1708 self.sock_hdlr = hcls('localhost', server.port)
1709 else:
1710 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001711 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001712 self.root_logger.removeHandler(self.root_logger.handlers[0])
1713 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001714 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001715
Christian Heimes180510d2008-03-03 19:15:45 +00001716 def tearDown(self):
1717 """Shutdown the TCP server."""
1718 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001719 if self.sock_hdlr:
1720 self.root_logger.removeHandler(self.sock_hdlr)
1721 self.sock_hdlr.close()
Victor Stinnerb8f41632017-09-13 01:47:22 -07001722 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001723 self.server.stop()
Christian Heimes180510d2008-03-03 19:15:45 +00001724 finally:
1725 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001726
Vinay Sajip7367d082011-05-02 13:17:27 +01001727 def handle_socket(self, request):
1728 conn = request.connection
1729 while True:
1730 chunk = conn.recv(4)
1731 if len(chunk) < 4:
1732 break
1733 slen = struct.unpack(">L", chunk)[0]
1734 chunk = conn.recv(slen)
1735 while len(chunk) < slen:
1736 chunk = chunk + conn.recv(slen - len(chunk))
1737 obj = pickle.loads(chunk)
1738 record = logging.makeLogRecord(obj)
1739 self.log_output += record.msg + '\n'
1740 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001741
Christian Heimes180510d2008-03-03 19:15:45 +00001742 def test_output(self):
1743 # The log message sent to the SocketHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001744 if self.server_exception:
1745 self.skipTest(self.server_exception)
Christian Heimes180510d2008-03-03 19:15:45 +00001746 logger = logging.getLogger("tcp")
1747 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001748 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001749 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001750 self.handled.acquire()
1751 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001752
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001753 def test_noserver(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001754 if self.server_exception:
1755 self.skipTest(self.server_exception)
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001756 # Avoid timing-related failures due to SocketHandler's own hard-wired
1757 # one-second timeout on socket.create_connection() (issue #16264).
1758 self.sock_hdlr.retryStart = 2.5
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001759 # Kill the server
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001760 self.server.stop()
Antoine Pitrouda201fa2012-10-21 17:18:27 +02001761 # The logging call should try to connect, which should fail
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001762 try:
1763 raise RuntimeError('Deliberate mistake')
1764 except RuntimeError:
1765 self.root_logger.exception('Never sent')
1766 self.root_logger.error('Never sent, either')
1767 now = time.time()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02001768 self.assertGreater(self.sock_hdlr.retryTime, now)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001769 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1770 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001771
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001772def _get_temp_domain_socket():
1773 fd, fn = tempfile.mkstemp(prefix='test_logging_', suffix='.sock')
1774 os.close(fd)
1775 # just need a name - file can't be present, or we'll get an
1776 # 'address already in use' error.
1777 os.remove(fn)
1778 return fn
Vinay Sajip7367d082011-05-02 13:17:27 +01001779
Victor Stinnerec5a8602014-06-02 14:41:51 +02001780@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001781class UnixSocketHandlerTest(SocketHandlerTest):
1782
1783 """Test for SocketHandler with unix sockets."""
1784
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001785 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001786 server_class = TestUnixStreamServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001787
1788 def setUp(self):
1789 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001790 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001791 SocketHandlerTest.setUp(self)
1792
1793 def tearDown(self):
1794 SocketHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001795 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001796
Vinay Sajip7367d082011-05-02 13:17:27 +01001797class DatagramHandlerTest(BaseTest):
1798
1799 """Test for DatagramHandler."""
1800
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001801 server_class = TestUDPServer
1802 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001803
Vinay Sajip7367d082011-05-02 13:17:27 +01001804 def setUp(self):
1805 """Set up a UDP server to receive log messages, and a DatagramHandler
1806 pointing to that server's address and port."""
1807 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001808 # Issue #29177: deal with errors that happen during setup
1809 self.server = self.sock_hdlr = self.server_exception = None
1810 try:
1811 self.server = server = self.server_class(self.address,
1812 self.handle_datagram, 0.01)
1813 server.start()
1814 # Uncomment next line to test error recovery in setUp()
1815 # raise OSError('dummy error raised')
1816 except OSError as e:
1817 self.server_exception = e
1818 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001819 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001820 hcls = logging.handlers.DatagramHandler
1821 if isinstance(server.server_address, tuple):
1822 self.sock_hdlr = hcls('localhost', server.port)
1823 else:
1824 self.sock_hdlr = hcls(server.server_address, None)
Vinay Sajip7367d082011-05-02 13:17:27 +01001825 self.log_output = ''
1826 self.root_logger.removeHandler(self.root_logger.handlers[0])
1827 self.root_logger.addHandler(self.sock_hdlr)
1828 self.handled = threading.Event()
1829
1830 def tearDown(self):
1831 """Shutdown the UDP server."""
1832 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001833 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001834 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001835 if self.sock_hdlr:
1836 self.root_logger.removeHandler(self.sock_hdlr)
1837 self.sock_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001838 finally:
1839 BaseTest.tearDown(self)
1840
1841 def handle_datagram(self, request):
1842 slen = struct.pack('>L', 0) # length of prefix
1843 packet = request.packet[len(slen):]
1844 obj = pickle.loads(packet)
1845 record = logging.makeLogRecord(obj)
1846 self.log_output += record.msg + '\n'
1847 self.handled.set()
1848
1849 def test_output(self):
1850 # The log message sent to the DatagramHandler is properly received.
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001851 if self.server_exception:
1852 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001853 logger = logging.getLogger("udp")
1854 logger.error("spam")
1855 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001856 self.handled.clear()
1857 logger.error("eggs")
1858 self.handled.wait()
1859 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001860
Victor Stinnerec5a8602014-06-02 14:41:51 +02001861@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001862class UnixDatagramHandlerTest(DatagramHandlerTest):
1863
1864 """Test for DatagramHandler using Unix sockets."""
1865
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001866 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001867 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001868
1869 def setUp(self):
1870 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001871 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001872 DatagramHandlerTest.setUp(self)
1873
1874 def tearDown(self):
1875 DatagramHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001876 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001877
Vinay Sajip7367d082011-05-02 13:17:27 +01001878class SysLogHandlerTest(BaseTest):
1879
1880 """Test for SysLogHandler using UDP."""
1881
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001882 server_class = TestUDPServer
1883 address = ('localhost', 0)
Vinay Sajip5421f352013-09-27 18:18:28 +01001884
Vinay Sajip7367d082011-05-02 13:17:27 +01001885 def setUp(self):
1886 """Set up a UDP server to receive log messages, and a SysLogHandler
1887 pointing to that server's address and port."""
1888 BaseTest.setUp(self)
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001889 # Issue #29177: deal with errors that happen during setup
1890 self.server = self.sl_hdlr = self.server_exception = None
1891 try:
1892 self.server = server = self.server_class(self.address,
1893 self.handle_datagram, 0.01)
1894 server.start()
1895 # Uncomment next line to test error recovery in setUp()
1896 # raise OSError('dummy error raised')
1897 except OSError as e:
1898 self.server_exception = e
1899 return
Vinay Sajip7367d082011-05-02 13:17:27 +01001900 server.ready.wait()
Vinay Sajip5421f352013-09-27 18:18:28 +01001901 hcls = logging.handlers.SysLogHandler
1902 if isinstance(server.server_address, tuple):
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001903 self.sl_hdlr = hcls((server.server_address[0], server.port))
Vinay Sajip5421f352013-09-27 18:18:28 +01001904 else:
1905 self.sl_hdlr = hcls(server.server_address)
Vinay Sajip7367d082011-05-02 13:17:27 +01001906 self.log_output = ''
1907 self.root_logger.removeHandler(self.root_logger.handlers[0])
1908 self.root_logger.addHandler(self.sl_hdlr)
1909 self.handled = threading.Event()
1910
1911 def tearDown(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001912 """Shutdown the server."""
Vinay Sajip7367d082011-05-02 13:17:27 +01001913 try:
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001914 if self.server:
Victor Stinnerbbc8b792019-12-10 20:41:23 +01001915 self.server.stop()
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001916 if self.sl_hdlr:
1917 self.root_logger.removeHandler(self.sl_hdlr)
1918 self.sl_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01001919 finally:
1920 BaseTest.tearDown(self)
1921
1922 def handle_datagram(self, request):
1923 self.log_output = request.packet
1924 self.handled.set()
1925
1926 def test_output(self):
Vinay Sajip1e6499c2017-01-09 16:54:12 +00001927 if self.server_exception:
1928 self.skipTest(self.server_exception)
Vinay Sajip7367d082011-05-02 13:17:27 +01001929 # The log message sent to the SysLogHandler is properly received.
1930 logger = logging.getLogger("slh")
1931 logger.error("sp\xe4m")
1932 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001933 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
Vinay Sajip8dd8d582011-06-09 16:55:23 +01001934 self.handled.clear()
1935 self.sl_hdlr.append_nul = False
1936 logger.error("sp\xe4m")
1937 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001938 self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m')
Vinay Sajip2353e352011-06-27 15:40:06 +01001939 self.handled.clear()
1940 self.sl_hdlr.ident = "h\xe4m-"
1941 logger.error("sp\xe4m")
1942 self.handled.wait()
Vinay Sajip7706d9a2012-04-16 16:22:38 +01001943 self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
Vinay Sajip7367d082011-05-02 13:17:27 +01001944
Victor Stinnerec5a8602014-06-02 14:41:51 +02001945@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
Vinay Sajip5421f352013-09-27 18:18:28 +01001946class UnixSysLogHandlerTest(SysLogHandlerTest):
1947
1948 """Test for SysLogHandler with Unix sockets."""
1949
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02001950 if hasattr(socket, "AF_UNIX"):
Vinay Sajipcb2c4fe2013-09-27 19:08:24 +01001951 server_class = TestUnixDatagramServer
Vinay Sajip5421f352013-09-27 18:18:28 +01001952
1953 def setUp(self):
1954 # override the definition in the base class
Vinay Sajipf6cdffe2013-09-27 18:41:12 +01001955 self.address = _get_temp_domain_socket()
Vinay Sajip5421f352013-09-27 18:18:28 +01001956 SysLogHandlerTest.setUp(self)
1957
1958 def tearDown(self):
1959 SysLogHandlerTest.tearDown(self)
Hai Shi3ddc6342020-06-30 21:46:06 +08001960 os_helper.unlink(self.address)
Vinay Sajip5421f352013-09-27 18:18:28 +01001961
Serhiy Storchaka16994912020-04-25 10:06:29 +03001962@unittest.skipUnless(socket_helper.IPV6_ENABLED,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001963 'IPv6 support required for this test.')
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08001964class IPv6SysLogHandlerTest(SysLogHandlerTest):
1965
1966 """Test for SysLogHandler with IPv6 host."""
1967
1968 server_class = TestUDPServer
1969 address = ('::1', 0)
1970
1971 def setUp(self):
1972 self.server_class.address_family = socket.AF_INET6
1973 super(IPv6SysLogHandlerTest, self).setUp()
1974
1975 def tearDown(self):
1976 self.server_class.address_family = socket.AF_INET
1977 super(IPv6SysLogHandlerTest, self).tearDown()
1978
Vinay Sajip7367d082011-05-02 13:17:27 +01001979class HTTPHandlerTest(BaseTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01001980 """Test for HTTPHandler."""
1981
1982 def setUp(self):
1983 """Set up an HTTP server to receive log messages, and a HTTPHandler
1984 pointing to that server's address and port."""
1985 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001986 self.handled = threading.Event()
1987
Vinay Sajip7367d082011-05-02 13:17:27 +01001988 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001989 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001990 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001991 if self.command == 'POST':
1992 try:
1993 rlen = int(request.headers['Content-Length'])
1994 self.post_data = request.rfile.read(rlen)
1995 except:
1996 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001997 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001998 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001999 self.handled.set()
2000
2001 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01002002 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01002003 logger = logging.getLogger("http")
Vinay Sajip9ba87612011-05-21 11:32:15 +01002004 root_logger = self.root_logger
2005 root_logger.removeHandler(self.root_logger.handlers[0])
2006 for secure in (False, True):
2007 addr = ('localhost', 0)
2008 if secure:
Vinay Sajip39e35282011-05-21 16:46:41 +01002009 try:
2010 import ssl
Vinay Sajip39e35282011-05-21 16:46:41 +01002011 except ImportError:
2012 sslctx = None
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002013 else:
2014 here = os.path.dirname(__file__)
2015 localhost_cert = os.path.join(here, "keycert.pem")
Christian Heimesa170fa12017-09-15 20:27:30 +02002016 sslctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
Benjamin Peterson4ffb0752014-11-03 14:29:33 -05002017 sslctx.load_cert_chain(localhost_cert)
Benjamin Peterson43052a12014-11-23 20:36:44 -06002018
2019 context = ssl.create_default_context(cafile=localhost_cert)
Vinay Sajip0372e102011-05-05 12:59:14 +01002020 else:
Vinay Sajip9ba87612011-05-21 11:32:15 +01002021 sslctx = None
Benjamin Peterson43052a12014-11-23 20:36:44 -06002022 context = None
Vinay Sajip9ba87612011-05-21 11:32:15 +01002023 self.server = server = TestHTTPServer(addr, self.handle_request,
2024 0.01, sslctx=sslctx)
2025 server.start()
2026 server.ready.wait()
2027 host = 'localhost:%d' % server.server_port
Vinay Sajip39e35282011-05-21 16:46:41 +01002028 secure_client = secure and sslctx
2029 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob',
Benjamin Peterson43052a12014-11-23 20:36:44 -06002030 secure=secure_client,
Vinay Sajip1bf197e2016-06-07 21:19:55 +01002031 context=context,
2032 credentials=('foo', 'bar'))
Vinay Sajip9ba87612011-05-21 11:32:15 +01002033 self.log_data = None
2034 root_logger.addHandler(self.h_hdlr)
2035
2036 for method in ('GET', 'POST'):
2037 self.h_hdlr.method = method
2038 self.handled.clear()
2039 msg = "sp\xe4m"
2040 logger.error(msg)
2041 self.handled.wait()
2042 self.assertEqual(self.log_data.path, '/frob')
2043 self.assertEqual(self.command, method)
2044 if method == 'GET':
2045 d = parse_qs(self.log_data.query)
2046 else:
2047 d = parse_qs(self.post_data.decode('utf-8'))
2048 self.assertEqual(d['name'], ['http'])
2049 self.assertEqual(d['funcName'], ['test_output'])
2050 self.assertEqual(d['msg'], [msg])
2051
Victor Stinnerbbc8b792019-12-10 20:41:23 +01002052 self.server.stop()
Vinay Sajip9ba87612011-05-21 11:32:15 +01002053 self.root_logger.removeHandler(self.h_hdlr)
2054 self.h_hdlr.close()
Vinay Sajip7367d082011-05-02 13:17:27 +01002055
Christian Heimes180510d2008-03-03 19:15:45 +00002056class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002057
Christian Heimes180510d2008-03-03 19:15:45 +00002058 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002059
Christian Heimes180510d2008-03-03 19:15:45 +00002060 def setUp(self):
2061 """Create a dict to remember potentially destroyed objects."""
2062 BaseTest.setUp(self)
2063 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002064
Christian Heimes180510d2008-03-03 19:15:45 +00002065 def _watch_for_survival(self, *args):
2066 """Watch the given objects for survival, by creating weakrefs to
2067 them."""
2068 for obj in args:
2069 key = id(obj), repr(obj)
2070 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002071
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002072 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00002073 """Assert that all objects watched for survival have survived."""
2074 # Trigger cycle breaking.
2075 gc.collect()
2076 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002077 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00002078 if ref() is None:
2079 dead.append(repr_)
2080 if dead:
2081 self.fail("%d objects should have survived "
2082 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002083
Christian Heimes180510d2008-03-03 19:15:45 +00002084 def test_persistent_loggers(self):
2085 # Logger objects are persistent and retain their configuration, even
2086 # if visible references are destroyed.
2087 self.root_logger.setLevel(logging.INFO)
2088 foo = logging.getLogger("foo")
2089 self._watch_for_survival(foo)
2090 foo.setLevel(logging.DEBUG)
2091 self.root_logger.debug(self.next_message())
2092 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002093 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002094 ('foo', 'DEBUG', '2'),
2095 ])
2096 del foo
2097 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002098 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00002099 # foo has retained its settings.
2100 bar = logging.getLogger("foo")
2101 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00002102 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00002103 ('foo', 'DEBUG', '2'),
2104 ('foo', 'DEBUG', '3'),
2105 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002106
Benjamin Petersonf91df042009-02-13 02:50:59 +00002107
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002108class EncodingTest(BaseTest):
2109 def test_encoding_plain_file(self):
2110 # In Python 2.x, a plain file object is treated as having no encoding.
2111 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00002112 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
2113 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002114 # the non-ascii data we write to the log.
2115 data = "foo\x80"
2116 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002117 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002118 log.addHandler(handler)
2119 try:
2120 # write non-ascii data to the log.
2121 log.warning(data)
2122 finally:
2123 log.removeHandler(handler)
2124 handler.close()
2125 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002126 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002127 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00002128 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00002129 finally:
2130 f.close()
2131 finally:
2132 if os.path.isfile(fn):
2133 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002134
Benjamin Petersonf91df042009-02-13 02:50:59 +00002135 def test_encoding_cyrillic_unicode(self):
2136 log = logging.getLogger("test")
Mike53f7a7c2017-12-14 14:04:53 +03002137 # Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
Benjamin Petersonf91df042009-02-13 02:50:59 +00002138 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
Mike53f7a7c2017-12-14 14:04:53 +03002139 # Ensure it's written in a Cyrillic encoding
Benjamin Petersonf91df042009-02-13 02:50:59 +00002140 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00002141 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00002142 stream = io.BytesIO()
2143 writer = writer_class(stream, 'strict')
2144 handler = logging.StreamHandler(writer)
2145 log.addHandler(handler)
2146 try:
2147 log.warning(message)
2148 finally:
2149 log.removeHandler(handler)
2150 handler.close()
2151 # check we wrote exactly those bytes, ignoring trailing \n etc
2152 s = stream.getvalue()
Mike53f7a7c2017-12-14 14:04:53 +03002153 # Compare against what the data should be when encoded in CP-1251
Benjamin Petersonf91df042009-02-13 02:50:59 +00002154 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
2155
2156
Georg Brandlf9734072008-12-07 15:30:06 +00002157class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00002158
Georg Brandlf9734072008-12-07 15:30:06 +00002159 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00002160 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00002161 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002162 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002163 warnings.filterwarnings("always", category=UserWarning)
2164 stream = io.StringIO()
2165 h = logging.StreamHandler(stream)
2166 logger = logging.getLogger("py.warnings")
2167 logger.addHandler(h)
2168 warnings.warn("I'm warning you...")
2169 logger.removeHandler(h)
2170 s = stream.getvalue()
2171 h.close()
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02002172 self.assertGreater(s.find("UserWarning: I'm warning you...\n"), 0)
Georg Brandlf9734072008-12-07 15:30:06 +00002173
Mike53f7a7c2017-12-14 14:04:53 +03002174 # See if an explicit file uses the original implementation
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002175 a_file = io.StringIO()
2176 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
2177 a_file, "Dummy line")
2178 s = a_file.getvalue()
2179 a_file.close()
2180 self.assertEqual(s,
2181 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
2182
2183 def test_warnings_no_handlers(self):
2184 with warnings.catch_warnings():
2185 logging.captureWarnings(True)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002186 self.addCleanup(logging.captureWarnings, False)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002187
2188 # confirm our assumption: no loggers are set
2189 logger = logging.getLogger("py.warnings")
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002190 self.assertEqual(logger.handlers, [])
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002191
2192 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01002193 self.assertEqual(len(logger.handlers), 1)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002194 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00002195
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002196
2197def formatFunc(format, datefmt=None):
2198 return logging.Formatter(format, datefmt)
2199
BNMetrics18fb1fb2018-10-15 19:41:36 +01002200class myCustomFormatter:
2201 def __init__(self, fmt, datefmt=None):
2202 pass
2203
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002204def handlerFunc():
2205 return logging.StreamHandler()
2206
2207class CustomHandler(logging.StreamHandler):
2208 pass
2209
2210class ConfigDictTest(BaseTest):
2211
2212 """Reading logging config from a dictionary."""
2213
Hai Shi3ddc6342020-06-30 21:46:06 +08002214 check_no_resource_warning = warnings_helper.check_no_resource_warning
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03002215 expected_log_pat = r"^(\w+) \+\+ (\w+)$"
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002216
2217 # config0 is a standard configuration.
2218 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002219 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002220 'formatters': {
2221 'form1' : {
2222 'format' : '%(levelname)s ++ %(message)s',
2223 },
2224 },
2225 'handlers' : {
2226 'hand1' : {
2227 'class' : 'logging.StreamHandler',
2228 'formatter' : 'form1',
2229 'level' : 'NOTSET',
2230 'stream' : 'ext://sys.stdout',
2231 },
2232 },
2233 'root' : {
2234 'level' : 'WARNING',
2235 'handlers' : ['hand1'],
2236 },
2237 }
2238
2239 # config1 adds a little to the standard configuration.
2240 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002241 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002242 'formatters': {
2243 'form1' : {
2244 'format' : '%(levelname)s ++ %(message)s',
2245 },
2246 },
2247 'handlers' : {
2248 'hand1' : {
2249 'class' : 'logging.StreamHandler',
2250 'formatter' : 'form1',
2251 'level' : 'NOTSET',
2252 'stream' : 'ext://sys.stdout',
2253 },
2254 },
2255 'loggers' : {
2256 'compiler.parser' : {
2257 'level' : 'DEBUG',
2258 'handlers' : ['hand1'],
2259 },
2260 },
2261 'root' : {
2262 'level' : 'WARNING',
2263 },
2264 }
2265
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002266 # config1a moves the handler to the root. Used with config8a
2267 config1a = {
2268 'version': 1,
2269 'formatters': {
2270 'form1' : {
2271 'format' : '%(levelname)s ++ %(message)s',
2272 },
2273 },
2274 'handlers' : {
2275 'hand1' : {
2276 'class' : 'logging.StreamHandler',
2277 'formatter' : 'form1',
2278 'level' : 'NOTSET',
2279 'stream' : 'ext://sys.stdout',
2280 },
2281 },
2282 'loggers' : {
2283 'compiler.parser' : {
2284 'level' : 'DEBUG',
2285 },
2286 },
2287 'root' : {
2288 'level' : 'WARNING',
2289 'handlers' : ['hand1'],
2290 },
2291 }
2292
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002293 # config2 has a subtle configuration error that should be reported
2294 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002295 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002296 'formatters': {
2297 'form1' : {
2298 'format' : '%(levelname)s ++ %(message)s',
2299 },
2300 },
2301 'handlers' : {
2302 'hand1' : {
2303 'class' : 'logging.StreamHandler',
2304 'formatter' : 'form1',
2305 'level' : 'NOTSET',
2306 'stream' : 'ext://sys.stdbout',
2307 },
2308 },
2309 'loggers' : {
2310 'compiler.parser' : {
2311 'level' : 'DEBUG',
2312 'handlers' : ['hand1'],
2313 },
2314 },
2315 'root' : {
2316 'level' : 'WARNING',
2317 },
2318 }
2319
Mike53f7a7c2017-12-14 14:04:53 +03002320 # As config1 but with a misspelt level on a handler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002321 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002322 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002323 'formatters': {
2324 'form1' : {
2325 'format' : '%(levelname)s ++ %(message)s',
2326 },
2327 },
2328 'handlers' : {
2329 'hand1' : {
2330 'class' : 'logging.StreamHandler',
2331 'formatter' : 'form1',
2332 'level' : 'NTOSET',
2333 'stream' : 'ext://sys.stdout',
2334 },
2335 },
2336 'loggers' : {
2337 'compiler.parser' : {
2338 'level' : 'DEBUG',
2339 'handlers' : ['hand1'],
2340 },
2341 },
2342 'root' : {
2343 'level' : 'WARNING',
2344 },
2345 }
2346
2347
Mike53f7a7c2017-12-14 14:04:53 +03002348 # As config1 but with a misspelt level on a logger
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002349 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002350 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002351 'formatters': {
2352 'form1' : {
2353 'format' : '%(levelname)s ++ %(message)s',
2354 },
2355 },
2356 'handlers' : {
2357 'hand1' : {
2358 'class' : 'logging.StreamHandler',
2359 'formatter' : 'form1',
2360 'level' : 'NOTSET',
2361 'stream' : 'ext://sys.stdout',
2362 },
2363 },
2364 'loggers' : {
2365 'compiler.parser' : {
2366 'level' : 'DEBUG',
2367 'handlers' : ['hand1'],
2368 },
2369 },
2370 'root' : {
2371 'level' : 'WRANING',
2372 },
2373 }
2374
2375 # config3 has a less subtle configuration error
2376 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002377 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002378 'formatters': {
2379 'form1' : {
2380 'format' : '%(levelname)s ++ %(message)s',
2381 },
2382 },
2383 'handlers' : {
2384 'hand1' : {
2385 'class' : 'logging.StreamHandler',
2386 'formatter' : 'misspelled_name',
2387 'level' : 'NOTSET',
2388 'stream' : 'ext://sys.stdout',
2389 },
2390 },
2391 'loggers' : {
2392 'compiler.parser' : {
2393 'level' : 'DEBUG',
2394 'handlers' : ['hand1'],
2395 },
2396 },
2397 'root' : {
2398 'level' : 'WARNING',
2399 },
2400 }
2401
2402 # config4 specifies a custom formatter class to be loaded
2403 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002404 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002405 'formatters': {
2406 'form1' : {
2407 '()' : __name__ + '.ExceptionFormatter',
2408 'format' : '%(levelname)s:%(name)s:%(message)s',
2409 },
2410 },
2411 'handlers' : {
2412 'hand1' : {
2413 'class' : 'logging.StreamHandler',
2414 'formatter' : 'form1',
2415 'level' : 'NOTSET',
2416 'stream' : 'ext://sys.stdout',
2417 },
2418 },
2419 'root' : {
2420 'level' : 'NOTSET',
2421 'handlers' : ['hand1'],
2422 },
2423 }
2424
2425 # As config4 but using an actual callable rather than a string
2426 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002427 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002428 'formatters': {
2429 'form1' : {
2430 '()' : ExceptionFormatter,
2431 'format' : '%(levelname)s:%(name)s:%(message)s',
2432 },
2433 'form2' : {
2434 '()' : __name__ + '.formatFunc',
2435 'format' : '%(levelname)s:%(name)s:%(message)s',
2436 },
2437 'form3' : {
2438 '()' : formatFunc,
2439 'format' : '%(levelname)s:%(name)s:%(message)s',
2440 },
2441 },
2442 'handlers' : {
2443 'hand1' : {
2444 'class' : 'logging.StreamHandler',
2445 'formatter' : 'form1',
2446 'level' : 'NOTSET',
2447 'stream' : 'ext://sys.stdout',
2448 },
2449 'hand2' : {
2450 '()' : handlerFunc,
2451 },
2452 },
2453 'root' : {
2454 'level' : 'NOTSET',
2455 'handlers' : ['hand1'],
2456 },
2457 }
2458
2459 # config5 specifies a custom handler class to be loaded
2460 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002461 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002462 'formatters': {
2463 'form1' : {
2464 'format' : '%(levelname)s ++ %(message)s',
2465 },
2466 },
2467 'handlers' : {
2468 'hand1' : {
2469 'class' : __name__ + '.CustomHandler',
2470 'formatter' : 'form1',
2471 'level' : 'NOTSET',
2472 'stream' : 'ext://sys.stdout',
2473 },
2474 },
2475 'loggers' : {
2476 'compiler.parser' : {
2477 'level' : 'DEBUG',
2478 'handlers' : ['hand1'],
2479 },
2480 },
2481 'root' : {
2482 'level' : 'WARNING',
2483 },
2484 }
2485
2486 # config6 specifies a custom handler class to be loaded
2487 # but has bad arguments
2488 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002489 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002490 'formatters': {
2491 'form1' : {
2492 'format' : '%(levelname)s ++ %(message)s',
2493 },
2494 },
2495 'handlers' : {
2496 'hand1' : {
2497 'class' : __name__ + '.CustomHandler',
2498 'formatter' : 'form1',
2499 'level' : 'NOTSET',
2500 'stream' : 'ext://sys.stdout',
2501 '9' : 'invalid parameter name',
2502 },
2503 },
2504 'loggers' : {
2505 'compiler.parser' : {
2506 'level' : 'DEBUG',
2507 'handlers' : ['hand1'],
2508 },
2509 },
2510 'root' : {
2511 'level' : 'WARNING',
2512 },
2513 }
2514
Mike53f7a7c2017-12-14 14:04:53 +03002515 # config 7 does not define compiler.parser but defines compiler.lexer
2516 # so compiler.parser should be disabled after applying it
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002517 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002518 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002519 'formatters': {
2520 'form1' : {
2521 'format' : '%(levelname)s ++ %(message)s',
2522 },
2523 },
2524 'handlers' : {
2525 'hand1' : {
2526 'class' : 'logging.StreamHandler',
2527 'formatter' : 'form1',
2528 'level' : 'NOTSET',
2529 'stream' : 'ext://sys.stdout',
2530 },
2531 },
2532 'loggers' : {
2533 'compiler.lexer' : {
2534 'level' : 'DEBUG',
2535 'handlers' : ['hand1'],
2536 },
2537 },
2538 'root' : {
2539 'level' : 'WARNING',
2540 },
2541 }
2542
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002543 # config8 defines both compiler and compiler.lexer
2544 # so compiler.parser should not be disabled (since
2545 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002546 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002547 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002548 'disable_existing_loggers' : False,
2549 'formatters': {
2550 'form1' : {
2551 'format' : '%(levelname)s ++ %(message)s',
2552 },
2553 },
2554 'handlers' : {
2555 'hand1' : {
2556 'class' : 'logging.StreamHandler',
2557 'formatter' : 'form1',
2558 'level' : 'NOTSET',
2559 'stream' : 'ext://sys.stdout',
2560 },
2561 },
2562 'loggers' : {
2563 'compiler' : {
2564 'level' : 'DEBUG',
2565 'handlers' : ['hand1'],
2566 },
2567 'compiler.lexer' : {
2568 },
2569 },
2570 'root' : {
2571 'level' : 'WARNING',
2572 },
2573 }
2574
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002575 # config8a disables existing loggers
2576 config8a = {
2577 'version': 1,
2578 'disable_existing_loggers' : True,
2579 'formatters': {
2580 'form1' : {
2581 'format' : '%(levelname)s ++ %(message)s',
2582 },
2583 },
2584 'handlers' : {
2585 'hand1' : {
2586 'class' : 'logging.StreamHandler',
2587 'formatter' : 'form1',
2588 'level' : 'NOTSET',
2589 'stream' : 'ext://sys.stdout',
2590 },
2591 },
2592 'loggers' : {
2593 'compiler' : {
2594 'level' : 'DEBUG',
2595 'handlers' : ['hand1'],
2596 },
2597 'compiler.lexer' : {
2598 },
2599 },
2600 'root' : {
2601 'level' : 'WARNING',
2602 },
2603 }
2604
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002605 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002606 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002607 'formatters': {
2608 'form1' : {
2609 'format' : '%(levelname)s ++ %(message)s',
2610 },
2611 },
2612 'handlers' : {
2613 'hand1' : {
2614 'class' : 'logging.StreamHandler',
2615 'formatter' : 'form1',
2616 'level' : 'WARNING',
2617 'stream' : 'ext://sys.stdout',
2618 },
2619 },
2620 'loggers' : {
2621 'compiler.parser' : {
2622 'level' : 'WARNING',
2623 'handlers' : ['hand1'],
2624 },
2625 },
2626 'root' : {
2627 'level' : 'NOTSET',
2628 },
2629 }
2630
2631 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002632 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002633 'incremental' : True,
2634 'handlers' : {
2635 'hand1' : {
2636 'level' : 'WARNING',
2637 },
2638 },
2639 'loggers' : {
2640 'compiler.parser' : {
2641 'level' : 'INFO',
2642 },
2643 },
2644 }
2645
2646 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002647 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002648 'incremental' : True,
2649 'handlers' : {
2650 'hand1' : {
2651 'level' : 'INFO',
2652 },
2653 },
2654 'loggers' : {
2655 'compiler.parser' : {
2656 'level' : 'INFO',
2657 },
2658 },
2659 }
2660
Mike53f7a7c2017-12-14 14:04:53 +03002661 # As config1 but with a filter added
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002662 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002663 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002664 'formatters': {
2665 'form1' : {
2666 'format' : '%(levelname)s ++ %(message)s',
2667 },
2668 },
2669 'filters' : {
2670 'filt1' : {
2671 'name' : 'compiler.parser',
2672 },
2673 },
2674 'handlers' : {
2675 'hand1' : {
2676 'class' : 'logging.StreamHandler',
2677 'formatter' : 'form1',
2678 'level' : 'NOTSET',
2679 'stream' : 'ext://sys.stdout',
2680 'filters' : ['filt1'],
2681 },
2682 },
2683 'loggers' : {
2684 'compiler.parser' : {
2685 'level' : 'DEBUG',
2686 'filters' : ['filt1'],
2687 },
2688 },
2689 'root' : {
2690 'level' : 'WARNING',
2691 'handlers' : ['hand1'],
2692 },
2693 }
2694
Mike53f7a7c2017-12-14 14:04:53 +03002695 # As config1 but using cfg:// references
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002696 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002697 'version': 1,
2698 'true_formatters': {
2699 'form1' : {
2700 'format' : '%(levelname)s ++ %(message)s',
2701 },
2702 },
2703 'handler_configs': {
2704 'hand1' : {
2705 'class' : 'logging.StreamHandler',
2706 'formatter' : 'form1',
2707 'level' : 'NOTSET',
2708 'stream' : 'ext://sys.stdout',
2709 },
2710 },
2711 'formatters' : 'cfg://true_formatters',
2712 'handlers' : {
2713 'hand1' : 'cfg://handler_configs[hand1]',
2714 },
2715 'loggers' : {
2716 'compiler.parser' : {
2717 'level' : 'DEBUG',
2718 'handlers' : ['hand1'],
2719 },
2720 },
2721 'root' : {
2722 'level' : 'WARNING',
2723 },
2724 }
2725
Mike53f7a7c2017-12-14 14:04:53 +03002726 # As config11 but missing the version key
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002727 config12 = {
2728 'true_formatters': {
2729 'form1' : {
2730 'format' : '%(levelname)s ++ %(message)s',
2731 },
2732 },
2733 'handler_configs': {
2734 'hand1' : {
2735 'class' : 'logging.StreamHandler',
2736 'formatter' : 'form1',
2737 'level' : 'NOTSET',
2738 'stream' : 'ext://sys.stdout',
2739 },
2740 },
2741 'formatters' : 'cfg://true_formatters',
2742 'handlers' : {
2743 'hand1' : 'cfg://handler_configs[hand1]',
2744 },
2745 'loggers' : {
2746 'compiler.parser' : {
2747 'level' : 'DEBUG',
2748 'handlers' : ['hand1'],
2749 },
2750 },
2751 'root' : {
2752 'level' : 'WARNING',
2753 },
2754 }
2755
Mike53f7a7c2017-12-14 14:04:53 +03002756 # As config11 but using an unsupported version
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002757 config13 = {
2758 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002759 'true_formatters': {
2760 'form1' : {
2761 'format' : '%(levelname)s ++ %(message)s',
2762 },
2763 },
2764 'handler_configs': {
2765 'hand1' : {
2766 'class' : 'logging.StreamHandler',
2767 'formatter' : 'form1',
2768 'level' : 'NOTSET',
2769 'stream' : 'ext://sys.stdout',
2770 },
2771 },
2772 'formatters' : 'cfg://true_formatters',
2773 'handlers' : {
2774 'hand1' : 'cfg://handler_configs[hand1]',
2775 },
2776 'loggers' : {
2777 'compiler.parser' : {
2778 'level' : 'DEBUG',
2779 'handlers' : ['hand1'],
2780 },
2781 },
2782 'root' : {
2783 'level' : 'WARNING',
2784 },
2785 }
2786
Vinay Sajip8d270232012-11-15 14:20:18 +00002787 # As config0, but with properties
2788 config14 = {
2789 'version': 1,
2790 'formatters': {
2791 'form1' : {
2792 'format' : '%(levelname)s ++ %(message)s',
2793 },
2794 },
2795 'handlers' : {
2796 'hand1' : {
2797 'class' : 'logging.StreamHandler',
2798 'formatter' : 'form1',
2799 'level' : 'NOTSET',
2800 'stream' : 'ext://sys.stdout',
2801 '.': {
2802 'foo': 'bar',
2803 'terminator': '!\n',
2804 }
2805 },
2806 },
2807 'root' : {
2808 'level' : 'WARNING',
2809 'handlers' : ['hand1'],
2810 },
2811 }
2812
Vinay Sajip3f885b52013-03-22 15:19:54 +00002813 out_of_order = {
2814 "version": 1,
2815 "formatters": {
2816 "mySimpleFormatter": {
Vinay Sajip28421c62013-03-29 17:56:54 +00002817 "format": "%(asctime)s (%(name)s) %(levelname)s: %(message)s",
2818 "style": "$"
Vinay Sajip3f885b52013-03-22 15:19:54 +00002819 }
2820 },
2821 "handlers": {
2822 "fileGlobal": {
2823 "class": "logging.StreamHandler",
2824 "level": "DEBUG",
2825 "formatter": "mySimpleFormatter"
2826 },
2827 "bufferGlobal": {
2828 "class": "logging.handlers.MemoryHandler",
2829 "capacity": 5,
2830 "formatter": "mySimpleFormatter",
2831 "target": "fileGlobal",
2832 "level": "DEBUG"
2833 }
2834 },
2835 "loggers": {
2836 "mymodule": {
2837 "level": "DEBUG",
2838 "handlers": ["bufferGlobal"],
2839 "propagate": "true"
2840 }
2841 }
2842 }
2843
BNMetrics18fb1fb2018-10-15 19:41:36 +01002844 # Configuration with custom logging.Formatter subclass as '()' key and 'validate' set to False
2845 custom_formatter_class_validate = {
2846 'version': 1,
2847 'formatters': {
2848 'form1': {
2849 '()': __name__ + '.ExceptionFormatter',
2850 'format': '%(levelname)s:%(name)s:%(message)s',
2851 'validate': False,
2852 },
2853 },
2854 'handlers' : {
2855 'hand1' : {
2856 'class': 'logging.StreamHandler',
2857 'formatter': 'form1',
2858 'level': 'NOTSET',
2859 'stream': 'ext://sys.stdout',
2860 },
2861 },
2862 "loggers": {
2863 "my_test_logger_custom_formatter": {
2864 "level": "DEBUG",
2865 "handlers": ["hand1"],
2866 "propagate": "true"
2867 }
2868 }
2869 }
2870
2871 # Configuration with custom logging.Formatter subclass as 'class' key and 'validate' set to False
2872 custom_formatter_class_validate2 = {
2873 'version': 1,
2874 'formatters': {
2875 'form1': {
2876 'class': __name__ + '.ExceptionFormatter',
2877 'format': '%(levelname)s:%(name)s:%(message)s',
2878 'validate': False,
2879 },
2880 },
2881 'handlers' : {
2882 'hand1' : {
2883 'class': 'logging.StreamHandler',
2884 'formatter': 'form1',
2885 'level': 'NOTSET',
2886 'stream': 'ext://sys.stdout',
2887 },
2888 },
2889 "loggers": {
2890 "my_test_logger_custom_formatter": {
2891 "level": "DEBUG",
2892 "handlers": ["hand1"],
2893 "propagate": "true"
2894 }
2895 }
2896 }
2897
2898 # Configuration with custom class that is not inherited from logging.Formatter
2899 custom_formatter_class_validate3 = {
2900 'version': 1,
2901 'formatters': {
2902 'form1': {
2903 'class': __name__ + '.myCustomFormatter',
2904 'format': '%(levelname)s:%(name)s:%(message)s',
2905 'validate': False,
2906 },
2907 },
2908 'handlers' : {
2909 'hand1' : {
2910 'class': 'logging.StreamHandler',
2911 'formatter': 'form1',
2912 'level': 'NOTSET',
2913 'stream': 'ext://sys.stdout',
2914 },
2915 },
2916 "loggers": {
2917 "my_test_logger_custom_formatter": {
2918 "level": "DEBUG",
2919 "handlers": ["hand1"],
2920 "propagate": "true"
2921 }
2922 }
2923 }
2924
2925 # Configuration with custom function and 'validate' set to False
2926 custom_formatter_with_function = {
2927 'version': 1,
2928 'formatters': {
2929 'form1': {
2930 '()': formatFunc,
2931 'format': '%(levelname)s:%(name)s:%(message)s',
2932 'validate': False,
2933 },
2934 },
2935 'handlers' : {
2936 'hand1' : {
2937 'class': 'logging.StreamHandler',
2938 'formatter': 'form1',
2939 'level': 'NOTSET',
2940 'stream': 'ext://sys.stdout',
2941 },
2942 },
2943 "loggers": {
2944 "my_test_logger_custom_formatter": {
2945 "level": "DEBUG",
2946 "handlers": ["hand1"],
2947 "propagate": "true"
2948 }
2949 }
2950 }
2951
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002952 def apply_config(self, conf):
2953 logging.config.dictConfig(conf)
2954
2955 def test_config0_ok(self):
2956 # A simple config which overrides the default settings.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002957 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002958 self.apply_config(self.config0)
2959 logger = logging.getLogger()
2960 # Won't output anything
2961 logger.info(self.next_message())
2962 # Outputs a message
2963 logger.error(self.next_message())
2964 self.assert_log_lines([
2965 ('ERROR', '2'),
2966 ], stream=output)
2967 # Original logger output is empty.
2968 self.assert_log_lines([])
2969
2970 def test_config1_ok(self, config=config1):
2971 # A config defining a sub-parser as well.
Vinay Sajip1feedb42014-05-31 08:19:12 +01002972 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002973 self.apply_config(config)
2974 logger = logging.getLogger("compiler.parser")
2975 # Both will output a message
2976 logger.info(self.next_message())
2977 logger.error(self.next_message())
2978 self.assert_log_lines([
2979 ('INFO', '1'),
2980 ('ERROR', '2'),
2981 ], stream=output)
2982 # Original logger output is empty.
2983 self.assert_log_lines([])
2984
2985 def test_config2_failure(self):
2986 # A simple config which overrides the default settings.
2987 self.assertRaises(Exception, self.apply_config, self.config2)
2988
2989 def test_config2a_failure(self):
2990 # A simple config which overrides the default settings.
2991 self.assertRaises(Exception, self.apply_config, self.config2a)
2992
2993 def test_config2b_failure(self):
2994 # A simple config which overrides the default settings.
2995 self.assertRaises(Exception, self.apply_config, self.config2b)
2996
2997 def test_config3_failure(self):
2998 # A simple config which overrides the default settings.
2999 self.assertRaises(Exception, self.apply_config, self.config3)
3000
3001 def test_config4_ok(self):
3002 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003003 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003004 self.apply_config(self.config4)
3005 #logger = logging.getLogger()
3006 try:
3007 raise RuntimeError()
3008 except RuntimeError:
3009 logging.exception("just testing")
3010 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003011 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003012 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3013 # Original logger output is empty
3014 self.assert_log_lines([])
3015
3016 def test_config4a_ok(self):
3017 # A config specifying a custom formatter class.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003018 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003019 self.apply_config(self.config4a)
3020 #logger = logging.getLogger()
3021 try:
3022 raise RuntimeError()
3023 except RuntimeError:
3024 logging.exception("just testing")
3025 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00003026 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003027 "ERROR:root:just testing\nGot a [RuntimeError]\n")
3028 # Original logger output is empty
3029 self.assert_log_lines([])
3030
3031 def test_config5_ok(self):
3032 self.test_config1_ok(config=self.config5)
3033
3034 def test_config6_failure(self):
3035 self.assertRaises(Exception, self.apply_config, self.config6)
3036
3037 def test_config7_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003038 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003039 self.apply_config(self.config1)
3040 logger = logging.getLogger("compiler.parser")
3041 # Both will output a message
3042 logger.info(self.next_message())
3043 logger.error(self.next_message())
3044 self.assert_log_lines([
3045 ('INFO', '1'),
3046 ('ERROR', '2'),
3047 ], stream=output)
3048 # Original logger output is empty.
3049 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003050 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003051 self.apply_config(self.config7)
3052 logger = logging.getLogger("compiler.parser")
3053 self.assertTrue(logger.disabled)
3054 logger = logging.getLogger("compiler.lexer")
3055 # Both will output a message
3056 logger.info(self.next_message())
3057 logger.error(self.next_message())
3058 self.assert_log_lines([
3059 ('INFO', '3'),
3060 ('ERROR', '4'),
3061 ], stream=output)
3062 # Original logger output is empty.
3063 self.assert_log_lines([])
3064
Mike53f7a7c2017-12-14 14:04:53 +03003065 # Same as test_config_7_ok but don't disable old loggers.
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003066 def test_config_8_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003067 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003068 self.apply_config(self.config1)
3069 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003070 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003071 logger.info(self.next_message())
3072 logger.error(self.next_message())
3073 self.assert_log_lines([
3074 ('INFO', '1'),
3075 ('ERROR', '2'),
3076 ], stream=output)
3077 # Original logger output is empty.
3078 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003079 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003080 self.apply_config(self.config8)
3081 logger = logging.getLogger("compiler.parser")
3082 self.assertFalse(logger.disabled)
3083 # Both will output a message
3084 logger.info(self.next_message())
3085 logger.error(self.next_message())
3086 logger = logging.getLogger("compiler.lexer")
3087 # Both will output a message
3088 logger.info(self.next_message())
3089 logger.error(self.next_message())
3090 self.assert_log_lines([
3091 ('INFO', '3'),
3092 ('ERROR', '4'),
3093 ('INFO', '5'),
3094 ('ERROR', '6'),
3095 ], stream=output)
3096 # Original logger output is empty.
3097 self.assert_log_lines([])
3098
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003099 def test_config_8a_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003100 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003101 self.apply_config(self.config1a)
3102 logger = logging.getLogger("compiler.parser")
3103 # See issue #11424. compiler-hyphenated sorts
3104 # between compiler and compiler.xyz and this
3105 # was preventing compiler.xyz from being included
3106 # in the child loggers of compiler because of an
3107 # overzealous loop termination condition.
3108 hyphenated = logging.getLogger('compiler-hyphenated')
3109 # All will output a message
3110 logger.info(self.next_message())
3111 logger.error(self.next_message())
3112 hyphenated.critical(self.next_message())
3113 self.assert_log_lines([
3114 ('INFO', '1'),
3115 ('ERROR', '2'),
3116 ('CRITICAL', '3'),
3117 ], stream=output)
3118 # Original logger output is empty.
3119 self.assert_log_lines([])
Vinay Sajip1feedb42014-05-31 08:19:12 +01003120 with support.captured_stdout() as output:
Vinay Sajip9f9991c2011-03-07 18:02:57 +00003121 self.apply_config(self.config8a)
3122 logger = logging.getLogger("compiler.parser")
3123 self.assertFalse(logger.disabled)
3124 # Both will output a message
3125 logger.info(self.next_message())
3126 logger.error(self.next_message())
3127 logger = logging.getLogger("compiler.lexer")
3128 # Both will output a message
3129 logger.info(self.next_message())
3130 logger.error(self.next_message())
3131 # Will not appear
3132 hyphenated.critical(self.next_message())
3133 self.assert_log_lines([
3134 ('INFO', '4'),
3135 ('ERROR', '5'),
3136 ('INFO', '6'),
3137 ('ERROR', '7'),
3138 ], stream=output)
3139 # Original logger output is empty.
3140 self.assert_log_lines([])
3141
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003142 def test_config_9_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003143 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003144 self.apply_config(self.config9)
3145 logger = logging.getLogger("compiler.parser")
Mike53f7a7c2017-12-14 14:04:53 +03003146 # Nothing will be output since both handler and logger are set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003147 logger.info(self.next_message())
3148 self.assert_log_lines([], stream=output)
3149 self.apply_config(self.config9a)
Mike53f7a7c2017-12-14 14:04:53 +03003150 # Nothing will be output since handler is still set to WARNING
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003151 logger.info(self.next_message())
3152 self.assert_log_lines([], stream=output)
3153 self.apply_config(self.config9b)
Mike53f7a7c2017-12-14 14:04:53 +03003154 # Message should now be output
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003155 logger.info(self.next_message())
3156 self.assert_log_lines([
3157 ('INFO', '3'),
3158 ], stream=output)
3159
3160 def test_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003161 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003162 self.apply_config(self.config10)
3163 logger = logging.getLogger("compiler.parser")
3164 logger.warning(self.next_message())
3165 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003166 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003167 logger.warning(self.next_message())
3168 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003169 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003170 logger.warning(self.next_message())
3171 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003172 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003173 logger.error(self.next_message())
3174 self.assert_log_lines([
3175 ('WARNING', '1'),
3176 ('ERROR', '4'),
3177 ], stream=output)
3178
3179 def test_config11_ok(self):
3180 self.test_config1_ok(self.config11)
3181
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003182 def test_config12_failure(self):
3183 self.assertRaises(Exception, self.apply_config, self.config12)
3184
3185 def test_config13_failure(self):
3186 self.assertRaises(Exception, self.apply_config, self.config13)
3187
Vinay Sajip8d270232012-11-15 14:20:18 +00003188 def test_config14_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003189 with support.captured_stdout() as output:
Vinay Sajip8d270232012-11-15 14:20:18 +00003190 self.apply_config(self.config14)
3191 h = logging._handlers['hand1']
3192 self.assertEqual(h.foo, 'bar')
3193 self.assertEqual(h.terminator, '!\n')
3194 logging.warning('Exclamation')
3195 self.assertTrue(output.getvalue().endswith('Exclamation!\n'))
3196
Xtreak087570a2018-07-02 14:27:46 +05303197 def test_config15_ok(self):
3198
3199 def cleanup(h1, fn):
3200 h1.close()
3201 os.remove(fn)
3202
3203 with self.check_no_resource_warning():
3204 fd, fn = tempfile.mkstemp(".log", "test_logging-X-")
3205 os.close(fd)
3206
3207 config = {
3208 "version": 1,
3209 "handlers": {
3210 "file": {
3211 "class": "logging.FileHandler",
Inada Naokifa51c0c2021-04-29 11:34:56 +09003212 "filename": fn,
3213 "encoding": "utf-8",
Xtreak087570a2018-07-02 14:27:46 +05303214 }
3215 },
3216 "root": {
3217 "handlers": ["file"]
3218 }
3219 }
3220
3221 self.apply_config(config)
3222 self.apply_config(config)
3223
3224 handler = logging.root.handlers[0]
3225 self.addCleanup(cleanup, handler, fn)
3226
Vinay Sajip4ded5512012-10-02 15:56:16 +01003227 def setup_via_listener(self, text, verify=None):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00003228 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00003229 # Ask for a randomly assigned port (by using port 0)
Vinay Sajip4ded5512012-10-02 15:56:16 +01003230 t = logging.config.listen(0, verify)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003231 t.start()
3232 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00003233 # Now get the port allocated
3234 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003235 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003236 try:
3237 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
3238 sock.settimeout(2.0)
3239 sock.connect(('localhost', port))
3240
3241 slen = struct.pack('>L', len(text))
3242 s = slen + text
3243 sentsofar = 0
3244 left = len(s)
3245 while left > 0:
3246 sent = sock.send(s[sentsofar:])
3247 sentsofar += sent
3248 left -= sent
3249 sock.close()
3250 finally:
3251 t.ready.wait(2.0)
3252 logging.config.stopListening()
Hai Shie80697d2020-05-28 06:10:27 +08003253 threading_helper.join_thread(t)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003254
3255 def test_listen_config_10_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003256 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003257 self.setup_via_listener(json.dumps(self.config10))
3258 logger = logging.getLogger("compiler.parser")
3259 logger.warning(self.next_message())
3260 logger = logging.getLogger('compiler')
Mike53f7a7c2017-12-14 14:04:53 +03003261 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003262 logger.warning(self.next_message())
3263 logger = logging.getLogger('compiler.lexer')
Mike53f7a7c2017-12-14 14:04:53 +03003264 # Not output, because filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003265 logger.warning(self.next_message())
3266 logger = logging.getLogger("compiler.parser.codegen")
Mike53f7a7c2017-12-14 14:04:53 +03003267 # Output, as not filtered
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003268 logger.error(self.next_message())
3269 self.assert_log_lines([
3270 ('WARNING', '1'),
3271 ('ERROR', '4'),
3272 ], stream=output)
3273
3274 def test_listen_config_1_ok(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01003275 with support.captured_stdout() as output:
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003276 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
3277 logger = logging.getLogger("compiler.parser")
3278 # Both will output a message
3279 logger.info(self.next_message())
3280 logger.error(self.next_message())
3281 self.assert_log_lines([
3282 ('INFO', '1'),
3283 ('ERROR', '2'),
3284 ], stream=output)
3285 # Original logger output is empty.
3286 self.assert_log_lines([])
3287
Vinay Sajip4ded5512012-10-02 15:56:16 +01003288 def test_listen_verify(self):
3289
3290 def verify_fail(stuff):
3291 return None
3292
3293 def verify_reverse(stuff):
3294 return stuff[::-1]
3295
3296 logger = logging.getLogger("compiler.parser")
3297 to_send = textwrap.dedent(ConfigFileTest.config1)
3298 # First, specify a verification function that will fail.
3299 # We expect to see no output, since our configuration
3300 # never took effect.
Vinay Sajip1feedb42014-05-31 08:19:12 +01003301 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003302 self.setup_via_listener(to_send, verify_fail)
3303 # Both will output a message
3304 logger.info(self.next_message())
3305 logger.error(self.next_message())
3306 self.assert_log_lines([], stream=output)
3307 # Original logger output has the stuff we logged.
3308 self.assert_log_lines([
3309 ('INFO', '1'),
3310 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003311 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003312
3313 # Now, perform no verification. Our configuration
3314 # should take effect.
3315
Vinay Sajip1feedb42014-05-31 08:19:12 +01003316 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003317 self.setup_via_listener(to_send) # no verify callable specified
3318 logger = logging.getLogger("compiler.parser")
3319 # Both will output a message
3320 logger.info(self.next_message())
3321 logger.error(self.next_message())
3322 self.assert_log_lines([
3323 ('INFO', '3'),
3324 ('ERROR', '4'),
3325 ], stream=output)
3326 # Original logger output still has the stuff we logged before.
3327 self.assert_log_lines([
3328 ('INFO', '1'),
3329 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003330 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003331
3332 # Now, perform verification which transforms the bytes.
3333
Vinay Sajip1feedb42014-05-31 08:19:12 +01003334 with support.captured_stdout() as output:
Vinay Sajip4ded5512012-10-02 15:56:16 +01003335 self.setup_via_listener(to_send[::-1], verify_reverse)
3336 logger = logging.getLogger("compiler.parser")
3337 # Both will output a message
3338 logger.info(self.next_message())
3339 logger.error(self.next_message())
3340 self.assert_log_lines([
3341 ('INFO', '5'),
3342 ('ERROR', '6'),
3343 ], stream=output)
3344 # Original logger output still has the stuff we logged before.
3345 self.assert_log_lines([
3346 ('INFO', '1'),
3347 ('ERROR', '2'),
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003348 ], pat=r"^[\w.]+ -> (\w+): (\d+)$")
Vinay Sajip4ded5512012-10-02 15:56:16 +01003349
Vinay Sajip3f885b52013-03-22 15:19:54 +00003350 def test_out_of_order(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01003351 self.assertRaises(ValueError, self.apply_config, self.out_of_order)
3352
3353 def test_out_of_order_with_dollar_style(self):
Pablo Galindo137b0632018-10-16 15:17:57 +01003354 config = copy.deepcopy(self.out_of_order)
BNMetrics18fb1fb2018-10-15 19:41:36 +01003355 config['formatters']['mySimpleFormatter']['format'] = "${asctime} (${name}) ${levelname}: ${message}"
3356
3357 self.apply_config(config)
Vinay Sajip3f885b52013-03-22 15:19:54 +00003358 handler = logging.getLogger('mymodule').handlers[0]
3359 self.assertIsInstance(handler.target, logging.Handler)
Vinay Sajip28421c62013-03-29 17:56:54 +00003360 self.assertIsInstance(handler.formatter._style,
3361 logging.StringTemplateStyle)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003362
BNMetrics18fb1fb2018-10-15 19:41:36 +01003363 def test_custom_formatter_class_with_validate(self):
3364 self.apply_config(self.custom_formatter_class_validate)
3365 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3366 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3367
3368 def test_custom_formatter_class_with_validate2(self):
3369 self.apply_config(self.custom_formatter_class_validate2)
3370 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3371 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3372
3373 def test_custom_formatter_class_with_validate2_with_wrong_fmt(self):
3374 config = self.custom_formatter_class_validate.copy()
3375 config['formatters']['form1']['style'] = "$"
3376
3377 # Exception should not be raise as we have configured 'validate' to False
3378 self.apply_config(config)
3379 handler = logging.getLogger("my_test_logger_custom_formatter").handlers[0]
3380 self.assertIsInstance(handler.formatter, ExceptionFormatter)
3381
3382 def test_custom_formatter_class_with_validate3(self):
3383 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_class_validate3)
3384
3385 def test_custom_formatter_function_with_validate(self):
3386 self.assertRaises(ValueError, self.apply_config, self.custom_formatter_with_function)
3387
Vinay Sajip373baef2011-04-26 20:05:24 +01003388 def test_baseconfig(self):
3389 d = {
3390 'atuple': (1, 2, 3),
3391 'alist': ['a', 'b', 'c'],
3392 'adict': {'d': 'e', 'f': 3 },
3393 'nest1': ('g', ('h', 'i'), 'j'),
3394 'nest2': ['k', ['l', 'm'], 'n'],
3395 'nest3': ['o', 'cfg://alist', 'p'],
3396 }
3397 bc = logging.config.BaseConfigurator(d)
3398 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
3399 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
3400 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
3401 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
3402 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
3403 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
3404 v = bc.convert('cfg://nest3')
3405 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
3406 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
3407 self.assertRaises(ValueError, bc.convert, 'cfg://!')
3408 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003409
Vinay Sajip46abfc12020-01-01 19:32:11 +00003410 def test_namedtuple(self):
3411 # see bpo-39142
3412 from collections import namedtuple
3413
3414 class MyHandler(logging.StreamHandler):
3415 def __init__(self, resource, *args, **kwargs):
3416 super().__init__(*args, **kwargs)
3417 self.resource: namedtuple = resource
3418
3419 def emit(self, record):
3420 record.msg += f' {self.resource.type}'
3421 return super().emit(record)
3422
3423 Resource = namedtuple('Resource', ['type', 'labels'])
3424 resource = Resource(type='my_type', labels=['a'])
3425
3426 config = {
3427 'version': 1,
3428 'handlers': {
3429 'myhandler': {
3430 '()': MyHandler,
3431 'resource': resource
3432 }
3433 },
3434 'root': {'level': 'INFO', 'handlers': ['myhandler']},
3435 }
3436 with support.captured_stderr() as stderr:
3437 self.apply_config(config)
3438 logging.info('some log')
3439 self.assertEqual(stderr.getvalue(), 'some log my_type\n')
3440
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003441class ManagerTest(BaseTest):
3442 def test_manager_loggerclass(self):
3443 logged = []
3444
3445 class MyLogger(logging.Logger):
3446 def _log(self, level, msg, args, exc_info=None, extra=None):
3447 logged.append(msg)
3448
3449 man = logging.Manager(None)
3450 self.assertRaises(TypeError, man.setLoggerClass, int)
3451 man.setLoggerClass(MyLogger)
3452 logger = man.getLogger('test')
3453 logger.warning('should appear in logged')
3454 logging.warning('should not appear in logged')
3455
3456 self.assertEqual(logged, ['should appear in logged'])
3457
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003458 def test_set_log_record_factory(self):
3459 man = logging.Manager(None)
3460 expected = object()
3461 man.setLogRecordFactory(expected)
3462 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003463
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003464class ChildLoggerTest(BaseTest):
3465 def test_child_loggers(self):
3466 r = logging.getLogger()
3467 l1 = logging.getLogger('abc')
3468 l2 = logging.getLogger('def.ghi')
3469 c1 = r.getChild('xyz')
3470 c2 = r.getChild('uvw.xyz')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003471 self.assertIs(c1, logging.getLogger('xyz'))
3472 self.assertIs(c2, logging.getLogger('uvw.xyz'))
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003473 c1 = l1.getChild('def')
3474 c2 = c1.getChild('ghi')
3475 c3 = l1.getChild('def.ghi')
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02003476 self.assertIs(c1, logging.getLogger('abc.def'))
3477 self.assertIs(c2, logging.getLogger('abc.def.ghi'))
3478 self.assertIs(c2, c3)
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003479
3480
Vinay Sajip6fac8172010-10-19 20:44:14 +00003481class DerivedLogRecord(logging.LogRecord):
3482 pass
3483
Vinay Sajip61561522010-12-03 11:50:38 +00003484class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00003485
3486 def setUp(self):
3487 class CheckingFilter(logging.Filter):
3488 def __init__(self, cls):
3489 self.cls = cls
3490
3491 def filter(self, record):
3492 t = type(record)
3493 if t is not self.cls:
3494 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
3495 self.cls)
3496 raise TypeError(msg)
3497 return True
3498
3499 BaseTest.setUp(self)
3500 self.filter = CheckingFilter(DerivedLogRecord)
3501 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00003502 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00003503
3504 def tearDown(self):
3505 self.root_logger.removeFilter(self.filter)
3506 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00003507 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003508
3509 def test_logrecord_class(self):
3510 self.assertRaises(TypeError, self.root_logger.warning,
3511 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00003512 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00003513 self.root_logger.error(self.next_message())
3514 self.assert_log_lines([
3515 ('root', 'ERROR', '2'),
3516 ])
3517
3518
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003519class QueueHandlerTest(BaseTest):
3520 # Do not bother with a logger name group.
Ezio Melotti3ffd29b2012-10-02 19:45:00 +03003521 expected_log_pat = r"^[\w.]+ -> (\w+): (\d+)$"
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003522
3523 def setUp(self):
3524 BaseTest.setUp(self)
3525 self.queue = queue.Queue(-1)
3526 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
favlladfe3442017-08-01 20:12:26 +02003527 self.name = 'que'
Vinay Sajip8552d1f2010-09-14 09:34:09 +00003528 self.que_logger = logging.getLogger('que')
3529 self.que_logger.propagate = False
3530 self.que_logger.setLevel(logging.WARNING)
3531 self.que_logger.addHandler(self.que_hdlr)
3532
3533 def tearDown(self):
3534 self.que_hdlr.close()
3535 BaseTest.tearDown(self)
3536
3537 def test_queue_handler(self):
3538 self.que_logger.debug(self.next_message())
3539 self.assertRaises(queue.Empty, self.queue.get_nowait)
3540 self.que_logger.info(self.next_message())
3541 self.assertRaises(queue.Empty, self.queue.get_nowait)
3542 msg = self.next_message()
3543 self.que_logger.warning(msg)
3544 data = self.queue.get_nowait()
3545 self.assertTrue(isinstance(data, logging.LogRecord))
3546 self.assertEqual(data.name, self.que_logger.name)
3547 self.assertEqual((data.msg, data.args), (msg, None))
3548
favlladfe3442017-08-01 20:12:26 +02003549 def test_formatting(self):
3550 msg = self.next_message()
3551 levelname = logging.getLevelName(logging.WARNING)
3552 log_format_str = '{name} -> {levelname}: {message}'
3553 formatted_msg = log_format_str.format(name=self.name,
3554 levelname=levelname, message=msg)
3555 formatter = logging.Formatter(self.log_format)
3556 self.que_hdlr.setFormatter(formatter)
3557 self.que_logger.warning(msg)
3558 log_record = self.queue.get_nowait()
3559 self.assertEqual(formatted_msg, log_record.msg)
3560 self.assertEqual(formatted_msg, log_record.message)
3561
Victor Stinnercafa2ef2011-05-02 16:11:28 +02003562 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3563 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01003564 def test_queue_listener(self):
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003565 handler = TestHandler(support.Matcher())
Vinay Sajipe723e962011-04-15 22:27:17 +01003566 listener = logging.handlers.QueueListener(self.queue, handler)
3567 listener.start()
3568 try:
3569 self.que_logger.warning(self.next_message())
3570 self.que_logger.error(self.next_message())
3571 self.que_logger.critical(self.next_message())
3572 finally:
3573 listener.stop()
3574 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
3575 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
3576 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
Vinay Sajip365701a2015-02-09 19:49:00 +00003577 handler.close()
3578
3579 # Now test with respect_handler_level set
3580
Serhiy Storchaka515fce42020-04-25 11:35:18 +03003581 handler = TestHandler(support.Matcher())
Vinay Sajip365701a2015-02-09 19:49:00 +00003582 handler.setLevel(logging.CRITICAL)
3583 listener = logging.handlers.QueueListener(self.queue, handler,
3584 respect_handler_level=True)
3585 listener.start()
3586 try:
3587 self.que_logger.warning(self.next_message())
3588 self.que_logger.error(self.next_message())
3589 self.que_logger.critical(self.next_message())
3590 finally:
3591 listener.stop()
3592 self.assertFalse(handler.matches(levelno=logging.WARNING, message='4'))
3593 self.assertFalse(handler.matches(levelno=logging.ERROR, message='5'))
3594 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='6'))
Cheryl Sabellad345bb42018-09-25 19:00:08 -04003595 handler.close()
3596
3597 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3598 'logging.handlers.QueueListener required for this test')
3599 def test_queue_listener_with_StreamHandler(self):
3600 # Test that traceback only appends once (bpo-34334).
3601 listener = logging.handlers.QueueListener(self.queue, self.root_hdlr)
3602 listener.start()
3603 try:
3604 1 / 0
3605 except ZeroDivisionError as e:
3606 exc = e
3607 self.que_logger.exception(self.next_message(), exc_info=exc)
3608 listener.stop()
3609 self.assertEqual(self.stream.getvalue().strip().count('Traceback'), 1)
Vinay Sajip365701a2015-02-09 19:49:00 +00003610
Xtreak2dad9602019-04-07 13:21:27 +05303611 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
3612 'logging.handlers.QueueListener required for this test')
3613 def test_queue_listener_with_multiple_handlers(self):
3614 # Test that queue handler format doesn't affect other handler formats (bpo-35726).
3615 self.que_hdlr.setFormatter(self.root_formatter)
3616 self.que_logger.addHandler(self.root_hdlr)
3617
3618 listener = logging.handlers.QueueListener(self.queue, self.que_hdlr)
3619 listener.start()
3620 self.que_logger.error("error")
3621 listener.stop()
3622 self.assertEqual(self.stream.getvalue().strip(), "que -> ERROR: error")
3623
Vinay Sajipd61910c2016-09-08 01:13:39 +01003624if hasattr(logging.handlers, 'QueueListener'):
3625 import multiprocessing
3626 from unittest.mock import patch
3627
3628 class QueueListenerTest(BaseTest):
3629 """
3630 Tests based on patch submitted for issue #27930. Ensure that
3631 QueueListener handles all log messages.
3632 """
3633
3634 repeat = 20
3635
3636 @staticmethod
3637 def setup_and_log(log_queue, ident):
3638 """
3639 Creates a logger with a QueueHandler that logs to a queue read by a
3640 QueueListener. Starts the listener, logs five messages, and stops
3641 the listener.
3642 """
3643 logger = logging.getLogger('test_logger_with_id_%s' % ident)
3644 logger.setLevel(logging.DEBUG)
3645 handler = logging.handlers.QueueHandler(log_queue)
3646 logger.addHandler(handler)
3647 listener = logging.handlers.QueueListener(log_queue)
3648 listener.start()
3649
3650 logger.info('one')
3651 logger.info('two')
3652 logger.info('three')
3653 logger.info('four')
3654 logger.info('five')
3655
3656 listener.stop()
3657 logger.removeHandler(handler)
3658 handler.close()
3659
3660 @patch.object(logging.handlers.QueueListener, 'handle')
3661 def test_handle_called_with_queue_queue(self, mock_handle):
3662 for i in range(self.repeat):
3663 log_queue = queue.Queue()
3664 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
3665 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3666 'correct number of handled log messages')
3667
3668 @patch.object(logging.handlers.QueueListener, 'handle')
3669 def test_handle_called_with_mp_queue(self, mock_handle):
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003670 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003671 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003672 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003673 for i in range(self.repeat):
3674 log_queue = multiprocessing.Queue()
3675 self.setup_and_log(log_queue, '%s_%s' % (self.id(), i))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003676 log_queue.close()
3677 log_queue.join_thread()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003678 self.assertEqual(mock_handle.call_count, 5 * self.repeat,
3679 'correct number of handled log messages')
3680
3681 @staticmethod
3682 def get_all_from_queue(log_queue):
3683 try:
3684 while True:
3685 yield log_queue.get_nowait()
3686 except queue.Empty:
3687 return []
3688
3689 def test_no_messages_in_queue_after_stop(self):
3690 """
3691 Five messages are logged then the QueueListener is stopped. This
3692 test then gets everything off the queue. Failure of this test
3693 indicates that messages were not registered on the queue until
3694 _after_ the QueueListener stopped.
3695 """
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003696 # bpo-28668: The multiprocessing (mp) module is not functional
xdegayebf2b65e2017-12-01 08:08:49 +01003697 # when the mp.synchronize module cannot be imported.
Victor Stinnerddbeb2f2020-06-18 14:53:19 +02003698 support.skip_if_broken_multiprocessing_synchronize()
Vinay Sajipd61910c2016-09-08 01:13:39 +01003699 for i in range(self.repeat):
3700 queue = multiprocessing.Queue()
3701 self.setup_and_log(queue, '%s_%s' %(self.id(), i))
3702 # time.sleep(1)
3703 items = list(self.get_all_from_queue(queue))
Victor Stinner8ca2f2f2017-04-26 15:56:25 +02003704 queue.close()
3705 queue.join_thread()
3706
Vinay Sajipd61910c2016-09-08 01:13:39 +01003707 expected = [[], [logging.handlers.QueueListener._sentinel]]
3708 self.assertIn(items, expected,
3709 'Found unexpected messages in queue: %s' % (
3710 [m.msg if isinstance(m, logging.LogRecord)
3711 else m for m in items]))
3712
Bar Harel6b282e12019-06-01 12:19:09 +03003713 def test_calls_task_done_after_stop(self):
3714 # Issue 36813: Make sure queue.join does not deadlock.
3715 log_queue = queue.Queue()
3716 listener = logging.handlers.QueueListener(log_queue)
3717 listener.start()
3718 listener.stop()
3719 with self.assertRaises(ValueError):
3720 # Make sure all tasks are done and .join won't block.
3721 log_queue.task_done()
3722
Vinay Sajipe723e962011-04-15 22:27:17 +01003723
Vinay Sajip37eb3382011-04-26 20:26:41 +01003724ZERO = datetime.timedelta(0)
3725
3726class UTC(datetime.tzinfo):
3727 def utcoffset(self, dt):
3728 return ZERO
3729
3730 dst = utcoffset
3731
3732 def tzname(self, dt):
3733 return 'UTC'
3734
3735utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01003736
Eric Larson9fdb76c2020-09-25 14:08:50 -04003737class AssertErrorMessage:
3738
3739 def assert_error_message(self, exception, message, *args, **kwargs):
3740 try:
3741 self.assertRaises((), *args, **kwargs)
3742 except exception as e:
3743 self.assertEqual(message, str(e))
3744
3745class FormatterTest(unittest.TestCase, AssertErrorMessage):
Vinay Sajipa39c5712010-10-25 13:57:39 +00003746 def setUp(self):
3747 self.common = {
3748 'name': 'formatter.test',
3749 'level': logging.DEBUG,
3750 'pathname': os.path.join('path', 'to', 'dummy.ext'),
3751 'lineno': 42,
3752 'exc_info': None,
3753 'func': None,
3754 'msg': 'Message with %d %s',
3755 'args': (2, 'placeholders'),
3756 }
3757 self.variants = {
Bar Harel8f192d12020-06-18 17:18:58 +03003758 'custom': {
3759 'custom': 1234
3760 }
Vinay Sajipa39c5712010-10-25 13:57:39 +00003761 }
3762
3763 def get_record(self, name=None):
3764 result = dict(self.common)
3765 if name is not None:
3766 result.update(self.variants[name])
3767 return logging.makeLogRecord(result)
3768
3769 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003770 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003771 r = self.get_record()
3772 f = logging.Formatter('${%(message)s}')
3773 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
3774 f = logging.Formatter('%(random)s')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003775 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003776 self.assertFalse(f.usesTime())
3777 f = logging.Formatter('%(asctime)s')
3778 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003779 f = logging.Formatter('%(asctime)-15s')
3780 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003781 f = logging.Formatter('%(asctime)#15s')
3782 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003783
3784 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003785 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003786 r = self.get_record()
3787 f = logging.Formatter('$%{message}%$', style='{')
3788 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3789 f = logging.Formatter('{random}', style='{')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003790 self.assertRaises(ValueError, f.format, r)
3791 f = logging.Formatter("{message}", style='{')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003792 self.assertFalse(f.usesTime())
3793 f = logging.Formatter('{asctime}', style='{')
3794 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00003795 f = logging.Formatter('{asctime!s:15}', style='{')
3796 self.assertTrue(f.usesTime())
3797 f = logging.Formatter('{asctime:15}', style='{')
3798 self.assertTrue(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00003799
3800 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00003801 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00003802 r = self.get_record()
BNMetrics18fb1fb2018-10-15 19:41:36 +01003803 f = logging.Formatter('${message}', style='$')
3804 self.assertEqual(f.format(r), 'Message with 2 placeholders')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003805 f = logging.Formatter('$message', style='$')
3806 self.assertEqual(f.format(r), 'Message with 2 placeholders')
3807 f = logging.Formatter('$$%${message}%$$', style='$')
3808 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
3809 f = logging.Formatter('${random}', style='$')
BNMetrics18fb1fb2018-10-15 19:41:36 +01003810 self.assertRaises(ValueError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003811 self.assertFalse(f.usesTime())
3812 f = logging.Formatter('${asctime}', style='$')
3813 self.assertTrue(f.usesTime())
3814 f = logging.Formatter('$asctime', style='$')
3815 self.assertTrue(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003816 f = logging.Formatter('${message}', style='$')
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00003817 self.assertFalse(f.usesTime())
BNMetrics18fb1fb2018-10-15 19:41:36 +01003818 f = logging.Formatter('${asctime}--', style='$')
3819 self.assertTrue(f.usesTime())
3820
3821 def test_format_validate(self):
3822 # Check correct formatting
3823 # Percentage style
3824 f = logging.Formatter("%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3825 self.assertEqual(f._fmt, "%(levelname)-15s - %(message) 5s - %(process)03d - %(module) - %(asctime)*.3s")
3826 f = logging.Formatter("%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3827 self.assertEqual(f._fmt, "%(asctime)*s - %(asctime)*.3s - %(process)-34.33o")
3828 f = logging.Formatter("%(process)#+027.23X")
3829 self.assertEqual(f._fmt, "%(process)#+027.23X")
3830 f = logging.Formatter("%(foo)#.*g")
3831 self.assertEqual(f._fmt, "%(foo)#.*g")
3832
3833 # StrFormat Style
3834 f = logging.Formatter("$%{message}%$ - {asctime!a:15} - {customfield['key']}", style="{")
3835 self.assertEqual(f._fmt, "$%{message}%$ - {asctime!a:15} - {customfield['key']}")
3836 f = logging.Formatter("{process:.2f} - {custom.f:.4f}", style="{")
3837 self.assertEqual(f._fmt, "{process:.2f} - {custom.f:.4f}")
3838 f = logging.Formatter("{customfield!s:#<30}", style="{")
3839 self.assertEqual(f._fmt, "{customfield!s:#<30}")
3840 f = logging.Formatter("{message!r}", style="{")
3841 self.assertEqual(f._fmt, "{message!r}")
3842 f = logging.Formatter("{message!s}", style="{")
3843 self.assertEqual(f._fmt, "{message!s}")
3844 f = logging.Formatter("{message!a}", style="{")
3845 self.assertEqual(f._fmt, "{message!a}")
3846 f = logging.Formatter("{process!r:4.2}", style="{")
3847 self.assertEqual(f._fmt, "{process!r:4.2}")
3848 f = logging.Formatter("{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}", style="{")
3849 self.assertEqual(f._fmt, "{process!s:<#30,.12f}- {custom:=+#30,.1d} - {module:^30}")
3850 f = logging.Formatter("{process!s:{w},.{p}}", style="{")
3851 self.assertEqual(f._fmt, "{process!s:{w},.{p}}")
3852 f = logging.Formatter("{foo:12.{p}}", style="{")
3853 self.assertEqual(f._fmt, "{foo:12.{p}}")
3854 f = logging.Formatter("{foo:{w}.6}", style="{")
3855 self.assertEqual(f._fmt, "{foo:{w}.6}")
3856 f = logging.Formatter("{foo[0].bar[1].baz}", style="{")
3857 self.assertEqual(f._fmt, "{foo[0].bar[1].baz}")
3858 f = logging.Formatter("{foo[k1].bar[k2].baz}", style="{")
3859 self.assertEqual(f._fmt, "{foo[k1].bar[k2].baz}")
3860 f = logging.Formatter("{12[k1].bar[k2].baz}", style="{")
3861 self.assertEqual(f._fmt, "{12[k1].bar[k2].baz}")
3862
3863 # Dollar style
3864 f = logging.Formatter("${asctime} - $message", style="$")
3865 self.assertEqual(f._fmt, "${asctime} - $message")
3866 f = logging.Formatter("$bar $$", style="$")
3867 self.assertEqual(f._fmt, "$bar $$")
3868 f = logging.Formatter("$bar $$$$", style="$")
3869 self.assertEqual(f._fmt, "$bar $$$$") # this would print two $($$)
3870
3871 # Testing when ValueError being raised from incorrect format
3872 # Percentage Style
3873 self.assertRaises(ValueError, logging.Formatter, "%(asctime)Z")
3874 self.assertRaises(ValueError, logging.Formatter, "%(asctime)b")
3875 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*")
3876 self.assertRaises(ValueError, logging.Formatter, "%(asctime)*3s")
3877 self.assertRaises(ValueError, logging.Formatter, "%(asctime)_")
3878 self.assertRaises(ValueError, logging.Formatter, '{asctime}')
3879 self.assertRaises(ValueError, logging.Formatter, '${message}')
3880 self.assertRaises(ValueError, logging.Formatter, '%(foo)#12.3*f') # with both * and decimal number as precision
3881 self.assertRaises(ValueError, logging.Formatter, '%(foo)0*.8*f')
3882
3883 # StrFormat Style
3884 # Testing failure for '-' in field name
3885 self.assert_error_message(
3886 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003887 "invalid format: invalid field name/expression: 'name-thing'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003888 logging.Formatter, "{name-thing}", style="{"
3889 )
3890 # Testing failure for style mismatch
3891 self.assert_error_message(
3892 ValueError,
3893 "invalid format: no fields",
3894 logging.Formatter, '%(asctime)s', style='{'
3895 )
3896 # Testing failure for invalid conversion
3897 self.assert_error_message(
3898 ValueError,
3899 "invalid conversion: 'Z'"
3900 )
3901 self.assertRaises(ValueError, logging.Formatter, '{asctime!s:#30,15f}', style='{')
3902 self.assert_error_message(
3903 ValueError,
3904 "invalid format: expected ':' after conversion specifier",
3905 logging.Formatter, '{asctime!aa:15}', style='{'
3906 )
3907 # Testing failure for invalid spec
3908 self.assert_error_message(
3909 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003910 "invalid format: bad specifier: '.2ff'",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003911 logging.Formatter, '{process:.2ff}', style='{'
3912 )
3913 self.assertRaises(ValueError, logging.Formatter, '{process:.2Z}', style='{')
3914 self.assertRaises(ValueError, logging.Formatter, '{process!s:<##30,12g}', style='{')
3915 self.assertRaises(ValueError, logging.Formatter, '{process!s:<#30#,12g}', style='{')
3916 self.assertRaises(ValueError, logging.Formatter, '{process!s:{{w}},{{p}}}', style='{')
3917 # Testing failure for mismatch braces
3918 self.assert_error_message(
3919 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003920 "invalid format: expected '}' before end of string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003921 logging.Formatter, '{process', style='{'
3922 )
3923 self.assert_error_message(
3924 ValueError,
Eric Larson9fdb76c2020-09-25 14:08:50 -04003925 "invalid format: Single '}' encountered in format string",
BNMetrics18fb1fb2018-10-15 19:41:36 +01003926 logging.Formatter, 'process}', style='{'
3927 )
3928 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}', style='{')
3929 self.assertRaises(ValueError, logging.Formatter, '{{foo!r:4.2}}', style='{')
3930 self.assertRaises(ValueError, logging.Formatter, '{foo/bar}', style='{')
3931 self.assertRaises(ValueError, logging.Formatter, '{foo:{{w}}.{{p}}}}', style='{')
3932 self.assertRaises(ValueError, logging.Formatter, '{foo!X:{{w}}.{{p}}}', style='{')
3933 self.assertRaises(ValueError, logging.Formatter, '{foo!a:random}', style='{')
3934 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{dom}', style='{')
3935 self.assertRaises(ValueError, logging.Formatter, '{foo!a:ran{d}om}', style='{')
3936 self.assertRaises(ValueError, logging.Formatter, '{foo.!a:d}', style='{')
3937
3938 # Dollar style
3939 # Testing failure for mismatch bare $
3940 self.assert_error_message(
3941 ValueError,
3942 "invalid format: bare \'$\' not allowed",
3943 logging.Formatter, '$bar $$$', style='$'
3944 )
3945 self.assert_error_message(
3946 ValueError,
3947 "invalid format: bare \'$\' not allowed",
3948 logging.Formatter, 'bar $', style='$'
3949 )
3950 self.assert_error_message(
3951 ValueError,
3952 "invalid format: bare \'$\' not allowed",
3953 logging.Formatter, 'foo $.', style='$'
3954 )
3955 # Testing failure for mismatch style
3956 self.assert_error_message(
3957 ValueError,
3958 "invalid format: no fields",
3959 logging.Formatter, '{asctime}', style='$'
3960 )
3961 self.assertRaises(ValueError, logging.Formatter, '%(asctime)s', style='$')
3962
3963 # Testing failure for incorrect fields
3964 self.assert_error_message(
3965 ValueError,
3966 "invalid format: no fields",
3967 logging.Formatter, 'foo', style='$'
3968 )
3969 self.assertRaises(ValueError, logging.Formatter, '${asctime', style='$')
Vinay Sajipa39c5712010-10-25 13:57:39 +00003970
Bar Harel8f192d12020-06-18 17:18:58 +03003971 def test_defaults_parameter(self):
3972 fmts = ['%(custom)s %(message)s', '{custom} {message}', '$custom $message']
3973 styles = ['%', '{', '$']
3974 for fmt, style in zip(fmts, styles):
3975 f = logging.Formatter(fmt, style=style, defaults={'custom': 'Default'})
3976 r = self.get_record()
3977 self.assertEqual(f.format(r), 'Default Message with 2 placeholders')
3978 r = self.get_record("custom")
3979 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3980
3981 # Without default
3982 f = logging.Formatter(fmt, style=style)
3983 r = self.get_record()
3984 self.assertRaises(ValueError, f.format, r)
3985
3986 # Non-existing default is ignored
3987 f = logging.Formatter(fmt, style=style, defaults={'Non-existing': 'Default'})
3988 r = self.get_record("custom")
3989 self.assertEqual(f.format(r), '1234 Message with 2 placeholders')
3990
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003991 def test_invalid_style(self):
3992 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
3993
Vinay Sajip86a96ce2011-04-27 08:30:30 +01003994 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003995 r = self.get_record()
Vinay Sajiped1f7c82012-07-03 21:36:36 +01003996 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 0, utc)
3997 # We use None to indicate we want the local timezone
3998 # We're essentially converting a UTC time to local time
3999 r.created = time.mktime(dt.astimezone(None).timetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004000 r.msecs = 123
4001 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01004002 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004003 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
4004 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01004005 f.format(r)
4006 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
4007
Mariusz Felisiak06a35542020-04-17 18:02:47 +02004008 def test_default_msec_format_none(self):
4009 class NoMsecFormatter(logging.Formatter):
4010 default_msec_format = None
4011 default_time_format = '%d/%m/%Y %H:%M:%S'
4012
4013 r = self.get_record()
4014 dt = datetime.datetime(1993, 4, 21, 8, 3, 0, 123, utc)
4015 r.created = time.mktime(dt.astimezone(None).timetuple())
4016 f = NoMsecFormatter()
4017 f.converter = time.gmtime
4018 self.assertEqual(f.formatTime(r), '21/04/1993 08:03:00')
4019
4020
Vinay Sajip985ef872011-04-26 19:34:04 +01004021class TestBufferingFormatter(logging.BufferingFormatter):
4022 def formatHeader(self, records):
4023 return '[(%d)' % len(records)
4024
4025 def formatFooter(self, records):
4026 return '(%d)]' % len(records)
4027
4028class BufferingFormatterTest(unittest.TestCase):
4029 def setUp(self):
4030 self.records = [
4031 logging.makeLogRecord({'msg': 'one'}),
4032 logging.makeLogRecord({'msg': 'two'}),
4033 ]
4034
4035 def test_default(self):
4036 f = logging.BufferingFormatter()
4037 self.assertEqual('', f.format([]))
4038 self.assertEqual('onetwo', f.format(self.records))
4039
4040 def test_custom(self):
4041 f = TestBufferingFormatter()
4042 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
4043 lf = logging.Formatter('<%(message)s>')
4044 f = TestBufferingFormatter(lf)
4045 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004046
4047class ExceptionTest(BaseTest):
4048 def test_formatting(self):
4049 r = self.root_logger
4050 h = RecordingHandler()
4051 r.addHandler(h)
4052 try:
4053 raise RuntimeError('deliberate mistake')
4054 except:
4055 logging.exception('failed', stack_info=True)
4056 r.removeHandler(h)
4057 h.close()
4058 r = h.records[0]
4059 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
4060 'call last):\n'))
4061 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
4062 'deliberate mistake'))
4063 self.assertTrue(r.stack_info.startswith('Stack (most recent '
4064 'call last):\n'))
4065 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
4066 'stack_info=True)'))
4067
4068
Vinay Sajip5a27d402010-12-10 11:42:57 +00004069class LastResortTest(BaseTest):
4070 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00004071 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00004072 root = self.root_logger
4073 root.removeHandler(self.root_hdlr)
Vinay Sajip5a27d402010-12-10 11:42:57 +00004074 old_lastresort = logging.lastResort
4075 old_raise_exceptions = logging.raiseExceptions
Vinay Sajip1feedb42014-05-31 08:19:12 +01004076
Vinay Sajip5a27d402010-12-10 11:42:57 +00004077 try:
Vinay Sajip1feedb42014-05-31 08:19:12 +01004078 with support.captured_stderr() as stderr:
4079 root.debug('This should not appear')
4080 self.assertEqual(stderr.getvalue(), '')
4081 root.warning('Final chance!')
4082 self.assertEqual(stderr.getvalue(), 'Final chance!\n')
4083
4084 # No handlers and no last resort, so 'No handlers' message
Vinay Sajip5a27d402010-12-10 11:42:57 +00004085 logging.lastResort = None
Vinay Sajip1feedb42014-05-31 08:19:12 +01004086 with support.captured_stderr() as stderr:
4087 root.warning('Final chance!')
4088 msg = 'No handlers could be found for logger "root"\n'
4089 self.assertEqual(stderr.getvalue(), msg)
4090
Vinay Sajip5a27d402010-12-10 11:42:57 +00004091 # 'No handlers' message only printed once
Vinay Sajip1feedb42014-05-31 08:19:12 +01004092 with support.captured_stderr() as stderr:
4093 root.warning('Final chance!')
4094 self.assertEqual(stderr.getvalue(), '')
4095
4096 # If raiseExceptions is False, no message is printed
Vinay Sajip5a27d402010-12-10 11:42:57 +00004097 root.manager.emittedNoHandlerWarning = False
Vinay Sajip5a27d402010-12-10 11:42:57 +00004098 logging.raiseExceptions = False
Vinay Sajip1feedb42014-05-31 08:19:12 +01004099 with support.captured_stderr() as stderr:
4100 root.warning('Final chance!')
4101 self.assertEqual(stderr.getvalue(), '')
Vinay Sajip5a27d402010-12-10 11:42:57 +00004102 finally:
Vinay Sajip5a27d402010-12-10 11:42:57 +00004103 root.addHandler(self.root_hdlr)
4104 logging.lastResort = old_lastresort
4105 logging.raiseExceptions = old_raise_exceptions
4106
4107
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004108class FakeHandler:
4109
4110 def __init__(self, identifier, called):
4111 for method in ('acquire', 'flush', 'close', 'release'):
4112 setattr(self, method, self.record_call(identifier, method, called))
4113
4114 def record_call(self, identifier, method_name, called):
4115 def inner():
4116 called.append('{} - {}'.format(identifier, method_name))
4117 return inner
4118
4119
4120class RecordingHandler(logging.NullHandler):
4121
4122 def __init__(self, *args, **kwargs):
4123 super(RecordingHandler, self).__init__(*args, **kwargs)
4124 self.records = []
4125
4126 def handle(self, record):
4127 """Keep track of all the emitted records."""
4128 self.records.append(record)
4129
4130
4131class ShutdownTest(BaseTest):
4132
Vinay Sajip5e66b162011-04-20 15:41:14 +01004133 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004134
4135 def setUp(self):
4136 super(ShutdownTest, self).setUp()
4137 self.called = []
4138
4139 raise_exceptions = logging.raiseExceptions
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004140 self.addCleanup(setattr, logging, 'raiseExceptions', raise_exceptions)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004141
4142 def raise_error(self, error):
4143 def inner():
4144 raise error()
4145 return inner
4146
4147 def test_no_failure(self):
4148 # create some fake handlers
4149 handler0 = FakeHandler(0, self.called)
4150 handler1 = FakeHandler(1, self.called)
4151 handler2 = FakeHandler(2, self.called)
4152
4153 # create live weakref to those handlers
4154 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
4155
4156 logging.shutdown(handlerList=list(handlers))
4157
4158 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
4159 '1 - acquire', '1 - flush', '1 - close', '1 - release',
4160 '0 - acquire', '0 - flush', '0 - close', '0 - release']
4161 self.assertEqual(expected, self.called)
4162
4163 def _test_with_failure_in_method(self, method, error):
4164 handler = FakeHandler(0, self.called)
4165 setattr(handler, method, self.raise_error(error))
4166 handlers = [logging.weakref.ref(handler)]
4167
4168 logging.shutdown(handlerList=list(handlers))
4169
4170 self.assertEqual('0 - release', self.called[-1])
4171
4172 def test_with_ioerror_in_acquire(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004173 self._test_with_failure_in_method('acquire', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004174
4175 def test_with_ioerror_in_flush(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004176 self._test_with_failure_in_method('flush', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004177
4178 def test_with_ioerror_in_close(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +02004179 self._test_with_failure_in_method('close', OSError)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004180
4181 def test_with_valueerror_in_acquire(self):
4182 self._test_with_failure_in_method('acquire', ValueError)
4183
4184 def test_with_valueerror_in_flush(self):
4185 self._test_with_failure_in_method('flush', ValueError)
4186
4187 def test_with_valueerror_in_close(self):
4188 self._test_with_failure_in_method('close', ValueError)
4189
4190 def test_with_other_error_in_acquire_without_raise(self):
4191 logging.raiseExceptions = False
4192 self._test_with_failure_in_method('acquire', IndexError)
4193
4194 def test_with_other_error_in_flush_without_raise(self):
4195 logging.raiseExceptions = False
4196 self._test_with_failure_in_method('flush', IndexError)
4197
4198 def test_with_other_error_in_close_without_raise(self):
4199 logging.raiseExceptions = False
4200 self._test_with_failure_in_method('close', IndexError)
4201
4202 def test_with_other_error_in_acquire_with_raise(self):
4203 logging.raiseExceptions = True
4204 self.assertRaises(IndexError, self._test_with_failure_in_method,
4205 'acquire', IndexError)
4206
4207 def test_with_other_error_in_flush_with_raise(self):
4208 logging.raiseExceptions = True
4209 self.assertRaises(IndexError, self._test_with_failure_in_method,
4210 'flush', IndexError)
4211
4212 def test_with_other_error_in_close_with_raise(self):
4213 logging.raiseExceptions = True
4214 self.assertRaises(IndexError, self._test_with_failure_in_method,
4215 'close', IndexError)
4216
4217
4218class ModuleLevelMiscTest(BaseTest):
4219
Vinay Sajip5e66b162011-04-20 15:41:14 +01004220 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004221
4222 def test_disable(self):
4223 old_disable = logging.root.manager.disable
4224 # confirm our assumptions are correct
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004225 self.assertEqual(old_disable, 0)
4226 self.addCleanup(logging.disable, old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004227
4228 logging.disable(83)
4229 self.assertEqual(logging.root.manager.disable, 83)
4230
Matthias Bussonnierb32d8b42020-12-16 01:43:39 -08004231 self.assertRaises(ValueError, logging.disable, "doesnotexists")
4232
4233 class _NotAnIntOrString:
4234 pass
4235
4236 self.assertRaises(TypeError, logging.disable, _NotAnIntOrString())
4237
4238 logging.disable("WARN")
4239
Vinay Sajipd489ac92016-12-31 11:40:11 +00004240 # test the default value introduced in 3.7
4241 # (Issue #28524)
4242 logging.disable()
4243 self.assertEqual(logging.root.manager.disable, logging.CRITICAL)
4244
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004245 def _test_log(self, method, level=None):
4246 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004247 support.patch(self, logging, 'basicConfig',
4248 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004249
4250 recording = RecordingHandler()
4251 logging.root.addHandler(recording)
4252
4253 log_method = getattr(logging, method)
4254 if level is not None:
4255 log_method(level, "test me: %r", recording)
4256 else:
4257 log_method("test me: %r", recording)
4258
4259 self.assertEqual(len(recording.records), 1)
4260 record = recording.records[0]
4261 self.assertEqual(record.getMessage(), "test me: %r" % recording)
4262
4263 expected_level = level if level is not None else getattr(logging, method.upper())
4264 self.assertEqual(record.levelno, expected_level)
4265
4266 # basicConfig was not called!
4267 self.assertEqual(called, [])
4268
4269 def test_log(self):
4270 self._test_log('log', logging.ERROR)
4271
4272 def test_debug(self):
4273 self._test_log('debug')
4274
4275 def test_info(self):
4276 self._test_log('info')
4277
4278 def test_warning(self):
4279 self._test_log('warning')
4280
4281 def test_error(self):
4282 self._test_log('error')
4283
4284 def test_critical(self):
4285 self._test_log('critical')
4286
4287 def test_set_logger_class(self):
4288 self.assertRaises(TypeError, logging.setLoggerClass, object)
4289
4290 class MyLogger(logging.Logger):
4291 pass
4292
4293 logging.setLoggerClass(MyLogger)
4294 self.assertEqual(logging.getLoggerClass(), MyLogger)
4295
4296 logging.setLoggerClass(logging.Logger)
4297 self.assertEqual(logging.getLoggerClass(), logging.Logger)
4298
Vinay Sajip01500012019-06-19 11:46:53 +01004299 def test_subclass_logger_cache(self):
4300 # bpo-37258
4301 message = []
4302
4303 class MyLogger(logging.getLoggerClass()):
4304 def __init__(self, name='MyLogger', level=logging.NOTSET):
4305 super().__init__(name, level)
4306 message.append('initialized')
4307
4308 logging.setLoggerClass(MyLogger)
4309 logger = logging.getLogger('just_some_logger')
4310 self.assertEqual(message, ['initialized'])
4311 stream = io.StringIO()
4312 h = logging.StreamHandler(stream)
4313 logger.addHandler(h)
4314 try:
4315 logger.setLevel(logging.DEBUG)
4316 logger.debug("hello")
4317 self.assertEqual(stream.getvalue().strip(), "hello")
4318
4319 stream.truncate(0)
4320 stream.seek(0)
4321
4322 logger.setLevel(logging.INFO)
4323 logger.debug("hello")
4324 self.assertEqual(stream.getvalue(), "")
4325 finally:
4326 logger.removeHandler(h)
4327 h.close()
4328 logging.setLoggerClass(logging.Logger)
4329
Antoine Pitrou712cb732013-12-21 15:51:54 +01004330 def test_logging_at_shutdown(self):
Victor Stinner45df61f2020-11-02 23:17:46 +01004331 # bpo-20037: Doing text I/O late at interpreter shutdown must not crash
4332 code = textwrap.dedent("""
Antoine Pitrou712cb732013-12-21 15:51:54 +01004333 import logging
4334
4335 class A:
4336 def __del__(self):
4337 try:
4338 raise ValueError("some error")
4339 except Exception:
4340 logging.exception("exception in __del__")
4341
Victor Stinner45df61f2020-11-02 23:17:46 +01004342 a = A()
4343 """)
Antoine Pitrou712cb732013-12-21 15:51:54 +01004344 rc, out, err = assert_python_ok("-c", code)
4345 err = err.decode()
4346 self.assertIn("exception in __del__", err)
4347 self.assertIn("ValueError: some error", err)
4348
Victor Stinner45df61f2020-11-02 23:17:46 +01004349 def test_logging_at_shutdown_open(self):
4350 # bpo-26789: FileHandler keeps a reference to the builtin open()
4351 # function to be able to open or reopen the file during Python
4352 # finalization.
4353 filename = os_helper.TESTFN
4354 self.addCleanup(os_helper.unlink, filename)
4355
4356 code = textwrap.dedent(f"""
4357 import builtins
4358 import logging
4359
4360 class A:
4361 def __del__(self):
4362 logging.error("log in __del__")
4363
4364 # basicConfig() opens the file, but logging.shutdown() closes
4365 # it at Python exit. When A.__del__() is called,
4366 # FileHandler._open() must be called again to re-open the file.
Inada Naokifb786922021-04-06 11:18:41 +09004367 logging.basicConfig(filename={filename!r}, encoding="utf-8")
Victor Stinner45df61f2020-11-02 23:17:46 +01004368
4369 a = A()
4370
4371 # Simulate the Python finalization which removes the builtin
4372 # open() function.
4373 del builtins.open
4374 """)
4375 assert_python_ok("-c", code)
4376
Inada Naokifb786922021-04-06 11:18:41 +09004377 with open(filename, encoding="utf-8") as fp:
Victor Stinner45df61f2020-11-02 23:17:46 +01004378 self.assertEqual(fp.read().rstrip(), "ERROR:root:log in __del__")
4379
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004380 def test_recursion_error(self):
4381 # Issue 36272
Victor Stinner45df61f2020-11-02 23:17:46 +01004382 code = textwrap.dedent("""
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004383 import logging
4384
4385 def rec():
4386 logging.error("foo")
4387 rec()
4388
Victor Stinner45df61f2020-11-02 23:17:46 +01004389 rec()
4390 """)
Rémi Lapeyre65f64b12019-03-15 07:53:34 +01004391 rc, out, err = assert_python_failure("-c", code)
4392 err = err.decode()
4393 self.assertNotIn("Cannot recover from stack overflow.", err)
4394 self.assertEqual(rc, 1)
4395
Antoine Pitrou712cb732013-12-21 15:51:54 +01004396
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004397class LogRecordTest(BaseTest):
4398 def test_str_rep(self):
4399 r = logging.makeLogRecord({})
4400 s = str(r)
4401 self.assertTrue(s.startswith('<LogRecord: '))
4402 self.assertTrue(s.endswith('>'))
4403
4404 def test_dict_arg(self):
4405 h = RecordingHandler()
4406 r = logging.getLogger()
4407 r.addHandler(h)
4408 d = {'less' : 'more' }
4409 logging.warning('less is %(less)s', d)
4410 self.assertIs(h.records[0].args, d)
4411 self.assertEqual(h.records[0].message, 'less is more')
4412 r.removeHandler(h)
4413 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004414
Irit Katriel3fd69992020-09-08 20:40:04 +01004415 @staticmethod # pickled as target of child process in the following test
4416 def _extract_logrecord_process_name(key, logMultiprocessing, conn=None):
4417 prev_logMultiprocessing = logging.logMultiprocessing
4418 logging.logMultiprocessing = logMultiprocessing
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004419 try:
4420 import multiprocessing as mp
Irit Katriel3fd69992020-09-08 20:40:04 +01004421 name = mp.current_process().name
4422
4423 r1 = logging.makeLogRecord({'msg': f'msg1_{key}'})
4424 del sys.modules['multiprocessing']
4425 r2 = logging.makeLogRecord({'msg': f'msg2_{key}'})
4426
4427 results = {'processName' : name,
4428 'r1.processName': r1.processName,
4429 'r2.processName': r2.processName,
4430 }
4431 finally:
4432 logging.logMultiprocessing = prev_logMultiprocessing
4433 if conn:
4434 conn.send(results)
4435 else:
4436 return results
4437
4438 def test_multiprocessing(self):
4439 multiprocessing_imported = 'multiprocessing' in sys.modules
4440 try:
4441 # logMultiprocessing is True by default
4442 self.assertEqual(logging.logMultiprocessing, True)
4443
4444 LOG_MULTI_PROCESSING = True
4445 # When logMultiprocessing == True:
4446 # In the main process processName = 'MainProcess'
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004447 r = logging.makeLogRecord({})
Irit Katriel3fd69992020-09-08 20:40:04 +01004448 self.assertEqual(r.processName, 'MainProcess')
4449
4450 results = self._extract_logrecord_process_name(1, LOG_MULTI_PROCESSING)
4451 self.assertEqual('MainProcess', results['processName'])
4452 self.assertEqual('MainProcess', results['r1.processName'])
4453 self.assertEqual('MainProcess', results['r2.processName'])
4454
4455 # In other processes, processName is correct when multiprocessing in imported,
4456 # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762).
4457 import multiprocessing
4458 parent_conn, child_conn = multiprocessing.Pipe()
4459 p = multiprocessing.Process(
4460 target=self._extract_logrecord_process_name,
4461 args=(2, LOG_MULTI_PROCESSING, child_conn,)
4462 )
4463 p.start()
4464 results = parent_conn.recv()
4465 self.assertNotEqual('MainProcess', results['processName'])
4466 self.assertEqual(results['processName'], results['r1.processName'])
4467 self.assertEqual('MainProcess', results['r2.processName'])
4468 p.join()
4469
4470 finally:
4471 if multiprocessing_imported:
4472 import multiprocessing
4473
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004474
4475 def test_optional(self):
4476 r = logging.makeLogRecord({})
4477 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004478 NOT_NONE(r.thread)
4479 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004480 NOT_NONE(r.process)
4481 NOT_NONE(r.processName)
4482 log_threads = logging.logThreads
4483 log_processes = logging.logProcesses
4484 log_multiprocessing = logging.logMultiprocessing
4485 try:
4486 logging.logThreads = False
4487 logging.logProcesses = False
4488 logging.logMultiprocessing = False
4489 r = logging.makeLogRecord({})
4490 NONE = self.assertIsNone
4491 NONE(r.thread)
4492 NONE(r.threadName)
4493 NONE(r.process)
4494 NONE(r.processName)
4495 finally:
4496 logging.logThreads = log_threads
4497 logging.logProcesses = log_processes
4498 logging.logMultiprocessing = log_multiprocessing
4499
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004500class BasicConfigTest(unittest.TestCase):
4501
Vinay Sajip95bf5042011-04-20 11:50:56 +01004502 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004503
4504 def setUp(self):
4505 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004506 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004507 self.saved_handlers = logging._handlers.copy()
4508 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004509 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004510 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004511 logging.root.handlers = []
4512
4513 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004514 for h in logging.root.handlers[:]:
4515 logging.root.removeHandler(h)
4516 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004517 super(BasicConfigTest, self).tearDown()
4518
Vinay Sajip3def7e02011-04-20 10:58:06 +01004519 def cleanup(self):
4520 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004521 logging._handlers.clear()
4522 logging._handlers.update(self.saved_handlers)
4523 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004524 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004525
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004526 def test_no_kwargs(self):
4527 logging.basicConfig()
4528
4529 # handler defaults to a StreamHandler to sys.stderr
4530 self.assertEqual(len(logging.root.handlers), 1)
4531 handler = logging.root.handlers[0]
4532 self.assertIsInstance(handler, logging.StreamHandler)
4533 self.assertEqual(handler.stream, sys.stderr)
4534
4535 formatter = handler.formatter
4536 # format defaults to logging.BASIC_FORMAT
4537 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4538 # datefmt defaults to None
4539 self.assertIsNone(formatter.datefmt)
4540 # style defaults to %
4541 self.assertIsInstance(formatter._style, logging.PercentStyle)
4542
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004543 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004544 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004545
Vinay Sajip1fd12022014-01-13 21:59:56 +00004546 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004547 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004548 logging.basicConfig(stream=sys.stdout, style="{")
4549 logging.error("Log an error")
4550 sys.stdout.seek(0)
4551 self.assertEqual(output.getvalue().strip(),
4552 "ERROR:root:Log an error")
4553
4554 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004555 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004556 logging.basicConfig(stream=sys.stdout, style="$")
4557 logging.error("Log an error")
4558 sys.stdout.seek(0)
4559 self.assertEqual(output.getvalue().strip(),
4560 "ERROR:root:Log an error")
4561
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004562 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004563
4564 def cleanup(h1, h2, fn):
4565 h1.close()
4566 h2.close()
4567 os.remove(fn)
4568
Inada Naokifb786922021-04-06 11:18:41 +09004569 logging.basicConfig(filename='test.log', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004570
4571 self.assertEqual(len(logging.root.handlers), 1)
4572 handler = logging.root.handlers[0]
4573 self.assertIsInstance(handler, logging.FileHandler)
4574
Inada Naokifb786922021-04-06 11:18:41 +09004575 expected = logging.FileHandler('test.log', 'a', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004576 self.assertEqual(handler.stream.mode, expected.stream.mode)
4577 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004578 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004579
4580 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004581
4582 def cleanup(h1, h2, fn):
4583 h1.close()
4584 h2.close()
4585 os.remove(fn)
4586
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004587 logging.basicConfig(filename='test.log', filemode='wb')
4588
4589 handler = logging.root.handlers[0]
4590 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004591 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004592 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004593
4594 def test_stream(self):
4595 stream = io.StringIO()
4596 self.addCleanup(stream.close)
4597 logging.basicConfig(stream=stream)
4598
4599 self.assertEqual(len(logging.root.handlers), 1)
4600 handler = logging.root.handlers[0]
4601 self.assertIsInstance(handler, logging.StreamHandler)
4602 self.assertEqual(handler.stream, stream)
4603
4604 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004605 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004606
4607 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004608 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004609
4610 def test_datefmt(self):
4611 logging.basicConfig(datefmt='bar')
4612
4613 formatter = logging.root.handlers[0].formatter
4614 self.assertEqual(formatter.datefmt, 'bar')
4615
4616 def test_style(self):
4617 logging.basicConfig(style='$')
4618
4619 formatter = logging.root.handlers[0].formatter
4620 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4621
4622 def test_level(self):
4623 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004624 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004625
4626 logging.basicConfig(level=57)
4627 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004628 # Test that second call has no effect
4629 logging.basicConfig(level=58)
4630 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004631
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004632 def test_incompatible(self):
4633 assertRaises = self.assertRaises
4634 handlers = [logging.StreamHandler()]
4635 stream = sys.stderr
4636 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004637 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004638 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004639 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004640 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004641 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004642 # Issue 23207: test for invalid kwargs
4643 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4644 # Should pop both filename and filemode even if filename is None
4645 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004646
4647 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004648 handlers = [
4649 logging.StreamHandler(),
4650 logging.StreamHandler(sys.stdout),
4651 logging.StreamHandler(),
4652 ]
4653 f = logging.Formatter()
4654 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004655 logging.basicConfig(handlers=handlers)
4656 self.assertIs(handlers[0], logging.root.handlers[0])
4657 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004658 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004659 self.assertIsNotNone(handlers[0].formatter)
4660 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004661 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004662 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4663
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004664 def test_force(self):
4665 old_string_io = io.StringIO()
4666 new_string_io = io.StringIO()
4667 old_handlers = [logging.StreamHandler(old_string_io)]
4668 new_handlers = [logging.StreamHandler(new_string_io)]
4669 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4670 logging.warning('warn')
4671 logging.info('info')
4672 logging.debug('debug')
4673 self.assertEqual(len(logging.root.handlers), 1)
4674 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4675 force=True)
4676 logging.warning('warn')
4677 logging.info('info')
4678 logging.debug('debug')
4679 self.assertEqual(len(logging.root.handlers), 1)
4680 self.assertEqual(old_string_io.getvalue().strip(),
4681 'WARNING:root:warn')
4682 self.assertEqual(new_string_io.getvalue().strip(),
4683 'WARNING:root:warn\nINFO:root:info')
4684
Vinay Sajipca7b5042019-06-17 17:40:52 +01004685 def test_encoding(self):
4686 try:
4687 encoding = 'utf-8'
4688 logging.basicConfig(filename='test.log', encoding=encoding,
4689 errors='strict',
4690 format='%(message)s', level=logging.DEBUG)
4691
4692 self.assertEqual(len(logging.root.handlers), 1)
4693 handler = logging.root.handlers[0]
4694 self.assertIsInstance(handler, logging.FileHandler)
4695 self.assertEqual(handler.encoding, encoding)
4696 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4697 finally:
4698 handler.close()
4699 with open('test.log', encoding='utf-8') as f:
4700 data = f.read().strip()
4701 os.remove('test.log')
4702 self.assertEqual(data,
4703 'The Øresund Bridge joins Copenhagen to Malmö')
4704
4705 def test_encoding_errors(self):
4706 try:
4707 encoding = 'ascii'
4708 logging.basicConfig(filename='test.log', encoding=encoding,
4709 errors='ignore',
4710 format='%(message)s', level=logging.DEBUG)
4711
4712 self.assertEqual(len(logging.root.handlers), 1)
4713 handler = logging.root.handlers[0]
4714 self.assertIsInstance(handler, logging.FileHandler)
4715 self.assertEqual(handler.encoding, encoding)
4716 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4717 finally:
4718 handler.close()
4719 with open('test.log', encoding='utf-8') as f:
4720 data = f.read().strip()
4721 os.remove('test.log')
4722 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4723
4724 def test_encoding_errors_default(self):
4725 try:
4726 encoding = 'ascii'
4727 logging.basicConfig(filename='test.log', encoding=encoding,
4728 format='%(message)s', level=logging.DEBUG)
4729
4730 self.assertEqual(len(logging.root.handlers), 1)
4731 handler = logging.root.handlers[0]
4732 self.assertIsInstance(handler, logging.FileHandler)
4733 self.assertEqual(handler.encoding, encoding)
4734 self.assertEqual(handler.errors, 'backslashreplace')
4735 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4736 finally:
4737 handler.close()
4738 with open('test.log', encoding='utf-8') as f:
4739 data = f.read().strip()
4740 os.remove('test.log')
4741 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4742 r'Bridge joins Copenhagen to Malm\xf6')
4743
4744 def test_encoding_errors_none(self):
4745 # Specifying None should behave as 'strict'
4746 try:
4747 encoding = 'ascii'
4748 logging.basicConfig(filename='test.log', encoding=encoding,
4749 errors=None,
4750 format='%(message)s', level=logging.DEBUG)
4751
4752 self.assertEqual(len(logging.root.handlers), 1)
4753 handler = logging.root.handlers[0]
4754 self.assertIsInstance(handler, logging.FileHandler)
4755 self.assertEqual(handler.encoding, encoding)
4756 self.assertIsNone(handler.errors)
4757
4758 message = []
4759
4760 def dummy_handle_error(record):
4761 _, v, _ = sys.exc_info()
4762 message.append(str(v))
4763
4764 handler.handleError = dummy_handle_error
4765 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4766 self.assertTrue(message)
4767 self.assertIn("'ascii' codec can't encode "
4768 "character '\\xd8' in position 4:", message[0])
4769 finally:
4770 handler.close()
4771 with open('test.log', encoding='utf-8') as f:
4772 data = f.read().strip()
4773 os.remove('test.log')
4774 # didn't write anything due to the encoding error
4775 self.assertEqual(data, r'')
4776
4777
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004778 def _test_log(self, method, level=None):
4779 # logging.root has no handlers so basicConfig should be called
4780 called = []
4781
4782 old_basic_config = logging.basicConfig
4783 def my_basic_config(*a, **kw):
4784 old_basic_config()
4785 old_level = logging.root.level
4786 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004787 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004788 called.append((a, kw))
4789
Vinay Sajip1feedb42014-05-31 08:19:12 +01004790 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004791
4792 log_method = getattr(logging, method)
4793 if level is not None:
4794 log_method(level, "test me")
4795 else:
4796 log_method("test me")
4797
4798 # basicConfig was called with no arguments
4799 self.assertEqual(called, [((), {})])
4800
4801 def test_log(self):
4802 self._test_log('log', logging.WARNING)
4803
4804 def test_debug(self):
4805 self._test_log('debug')
4806
4807 def test_info(self):
4808 self._test_log('info')
4809
4810 def test_warning(self):
4811 self._test_log('warning')
4812
4813 def test_error(self):
4814 self._test_log('error')
4815
4816 def test_critical(self):
4817 self._test_log('critical')
4818
4819
4820class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004821 def setUp(self):
4822 super(LoggerAdapterTest, self).setUp()
4823 old_handler_list = logging._handlerList[:]
4824
4825 self.recording = RecordingHandler()
4826 self.logger = logging.root
4827 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004828 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004829 self.addCleanup(self.recording.close)
4830
4831 def cleanup():
4832 logging._handlerList[:] = old_handler_list
4833
4834 self.addCleanup(cleanup)
4835 self.addCleanup(logging.shutdown)
4836 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4837
4838 def test_exception(self):
4839 msg = 'testing exception: %r'
4840 exc = None
4841 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004842 1 / 0
4843 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004844 exc = e
4845 self.adapter.exception(msg, self.recording)
4846
4847 self.assertEqual(len(self.recording.records), 1)
4848 record = self.recording.records[0]
4849 self.assertEqual(record.levelno, logging.ERROR)
4850 self.assertEqual(record.msg, msg)
4851 self.assertEqual(record.args, (self.recording,))
4852 self.assertEqual(record.exc_info,
4853 (exc.__class__, exc, exc.__traceback__))
4854
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004855 def test_exception_excinfo(self):
4856 try:
4857 1 / 0
4858 except ZeroDivisionError as e:
4859 exc = e
4860
4861 self.adapter.exception('exc_info test', exc_info=exc)
4862
4863 self.assertEqual(len(self.recording.records), 1)
4864 record = self.recording.records[0]
4865 self.assertEqual(record.exc_info,
4866 (exc.__class__, exc, exc.__traceback__))
4867
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004868 def test_critical(self):
4869 msg = 'critical test! %r'
4870 self.adapter.critical(msg, self.recording)
4871
4872 self.assertEqual(len(self.recording.records), 1)
4873 record = self.recording.records[0]
4874 self.assertEqual(record.levelno, logging.CRITICAL)
4875 self.assertEqual(record.msg, msg)
4876 self.assertEqual(record.args, (self.recording,))
4877
4878 def test_is_enabled_for(self):
4879 old_disable = self.adapter.logger.manager.disable
4880 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004881 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4882 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004883 self.assertFalse(self.adapter.isEnabledFor(32))
4884
4885 def test_has_handlers(self):
4886 self.assertTrue(self.adapter.hasHandlers())
4887
4888 for handler in self.logger.handlers:
4889 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004890
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004891 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004892 self.assertFalse(self.adapter.hasHandlers())
4893
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004894 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004895 class Adapter(logging.LoggerAdapter):
4896 prefix = 'Adapter'
4897
4898 def process(self, msg, kwargs):
4899 return f"{self.prefix} {msg}", kwargs
4900
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004901 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004902 adapter = Adapter(logger=self.logger, extra=None)
4903 adapter_adapter = Adapter(logger=adapter, extra=None)
4904 adapter_adapter.prefix = 'AdapterAdapter'
4905 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004906 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004907 self.assertEqual(len(self.recording.records), 1)
4908 record = self.recording.records[0]
4909 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004910 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004911 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004912 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004913 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004914 self.assertIs(self.logger.manager, orig_manager)
4915 temp_manager = object()
4916 try:
4917 adapter_adapter.manager = temp_manager
4918 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004919 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004920 self.assertIs(self.logger.manager, temp_manager)
4921 finally:
4922 adapter_adapter.manager = orig_manager
4923 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004924 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004925 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004926
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004927
Eric Larson9fdb76c2020-09-25 14:08:50 -04004928class LoggerTest(BaseTest, AssertErrorMessage):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004929
4930 def setUp(self):
4931 super(LoggerTest, self).setUp()
4932 self.recording = RecordingHandler()
4933 self.logger = logging.Logger(name='blah')
4934 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004935 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004936 self.addCleanup(self.recording.close)
4937 self.addCleanup(logging.shutdown)
4938
4939 def test_set_invalid_level(self):
Eric Larson9fdb76c2020-09-25 14:08:50 -04004940 self.assert_error_message(
4941 TypeError, 'Level not an integer or a valid string: None',
4942 self.logger.setLevel, None)
4943 self.assert_error_message(
4944 TypeError, 'Level not an integer or a valid string: (0, 0)',
4945 self.logger.setLevel, (0, 0))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004946
4947 def test_exception(self):
4948 msg = 'testing exception: %r'
4949 exc = None
4950 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004951 1 / 0
4952 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004953 exc = e
4954 self.logger.exception(msg, self.recording)
4955
4956 self.assertEqual(len(self.recording.records), 1)
4957 record = self.recording.records[0]
4958 self.assertEqual(record.levelno, logging.ERROR)
4959 self.assertEqual(record.msg, msg)
4960 self.assertEqual(record.args, (self.recording,))
4961 self.assertEqual(record.exc_info,
4962 (exc.__class__, exc, exc.__traceback__))
4963
4964 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004965 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004966 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004967
4968 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004969 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004970 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004971
4972 def test_find_caller_with_stack_info(self):
4973 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004974 support.patch(self, logging.traceback, 'print_stack',
4975 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004976
4977 self.logger.findCaller(stack_info=True)
4978
4979 self.assertEqual(len(called), 1)
4980 self.assertEqual('Stack (most recent call last):\n', called[0])
4981
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004982 def test_find_caller_with_stacklevel(self):
4983 the_level = 1
4984
4985 def innermost():
4986 self.logger.warning('test', stacklevel=the_level)
4987
4988 def inner():
4989 innermost()
4990
4991 def outer():
4992 inner()
4993
4994 records = self.recording.records
4995 outer()
4996 self.assertEqual(records[-1].funcName, 'innermost')
4997 lineno = records[-1].lineno
4998 the_level += 1
4999 outer()
5000 self.assertEqual(records[-1].funcName, 'inner')
5001 self.assertGreater(records[-1].lineno, lineno)
5002 lineno = records[-1].lineno
5003 the_level += 1
5004 outer()
5005 self.assertEqual(records[-1].funcName, 'outer')
5006 self.assertGreater(records[-1].lineno, lineno)
5007 lineno = records[-1].lineno
5008 the_level += 1
5009 outer()
5010 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5011 self.assertGreater(records[-1].lineno, lineno)
5012
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005013 def test_make_record_with_extra_overwrite(self):
5014 name = 'my record'
5015 level = 13
5016 fn = lno = msg = args = exc_info = func = sinfo = None
5017 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
5018 exc_info, func, sinfo)
5019
5020 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
5021 extra = {key: 'some value'}
5022 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
5023 fn, lno, msg, args, exc_info,
5024 extra=extra, sinfo=sinfo)
5025
5026 def test_make_record_with_extra_no_overwrite(self):
5027 name = 'my record'
5028 level = 13
5029 fn = lno = msg = args = exc_info = func = sinfo = None
5030 extra = {'valid_key': 'some value'}
5031 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
5032 exc_info, extra=extra, sinfo=sinfo)
5033 self.assertIn('valid_key', result.__dict__)
5034
5035 def test_has_handlers(self):
5036 self.assertTrue(self.logger.hasHandlers())
5037
5038 for handler in self.logger.handlers:
5039 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005040 self.assertFalse(self.logger.hasHandlers())
5041
5042 def test_has_handlers_no_propagate(self):
5043 child_logger = logging.getLogger('blah.child')
5044 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005045 self.assertFalse(child_logger.hasHandlers())
5046
5047 def test_is_enabled_for(self):
5048 old_disable = self.logger.manager.disable
5049 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01005050 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005051 self.assertFalse(self.logger.isEnabledFor(22))
5052
Timo Furrer6e3ca642018-06-01 09:29:46 +02005053 def test_is_enabled_for_disabled_logger(self):
5054 old_disabled = self.logger.disabled
5055 old_disable = self.logger.manager.disable
5056
5057 self.logger.disabled = True
5058 self.logger.manager.disable = 21
5059
5060 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
5061 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
5062
5063 self.assertFalse(self.logger.isEnabledFor(22))
5064
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005065 def test_root_logger_aliases(self):
5066 root = logging.getLogger()
5067 self.assertIs(root, logging.root)
5068 self.assertIs(root, logging.getLogger(None))
5069 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01005070 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005071 self.assertIs(root, logging.getLogger('foo').root)
5072 self.assertIs(root, logging.getLogger('foo.bar').root)
5073 self.assertIs(root, logging.getLogger('foo').parent)
5074
5075 self.assertIsNot(root, logging.getLogger('\0'))
5076 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
5077
5078 def test_invalid_names(self):
5079 self.assertRaises(TypeError, logging.getLogger, any)
5080 self.assertRaises(TypeError, logging.getLogger, b'foo')
5081
Vinay Sajip6260d9f2017-06-06 16:34:29 +01005082 def test_pickling(self):
5083 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
5084 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
5085 logger = logging.getLogger(name)
5086 s = pickle.dumps(logger, proto)
5087 unpickled = pickle.loads(s)
5088 self.assertIs(unpickled, logger)
5089
Avram Lubkin78c18a92017-07-30 05:36:33 -04005090 def test_caching(self):
5091 root = self.root_logger
5092 logger1 = logging.getLogger("abc")
5093 logger2 = logging.getLogger("abc.def")
5094
5095 # Set root logger level and ensure cache is empty
5096 root.setLevel(logging.ERROR)
5097 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
5098 self.assertEqual(logger2._cache, {})
5099
5100 # Ensure cache is populated and calls are consistent
5101 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5102 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
5103 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
5104 self.assertEqual(root._cache, {})
5105 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5106
5107 # Ensure root cache gets populated
5108 self.assertEqual(root._cache, {})
5109 self.assertTrue(root.isEnabledFor(logging.ERROR))
5110 self.assertEqual(root._cache, {logging.ERROR: True})
5111
5112 # Set parent logger level and ensure caches are emptied
5113 logger1.setLevel(logging.CRITICAL)
5114 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5115 self.assertEqual(logger2._cache, {})
5116
5117 # Ensure logger2 uses parent logger's effective level
5118 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5119
5120 # Set level to NOTSET and ensure caches are empty
5121 logger2.setLevel(logging.NOTSET)
5122 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5123 self.assertEqual(logger2._cache, {})
5124 self.assertEqual(logger1._cache, {})
5125 self.assertEqual(root._cache, {})
5126
5127 # Verify logger2 follows parent and not root
5128 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5129 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
5130 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
5131 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
5132 self.assertTrue(root.isEnabledFor(logging.ERROR))
5133
5134 # Disable logging in manager and ensure caches are clear
5135 logging.disable()
5136 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5137 self.assertEqual(logger2._cache, {})
5138 self.assertEqual(logger1._cache, {})
5139 self.assertEqual(root._cache, {})
5140
5141 # Ensure no loggers are enabled
5142 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5143 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5144 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5145
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005146
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005147class BaseFileTest(BaseTest):
5148 "Base class for handler tests that write log files"
5149
5150 def setUp(self):
5151 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005152 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5153 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005154 self.rmfiles = []
5155
5156 def tearDown(self):
5157 for fn in self.rmfiles:
5158 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005159 if os.path.exists(self.fn):
5160 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005161 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005162
5163 def assertLogFile(self, filename):
5164 "Assert a log file is there and register it for deletion"
5165 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005166 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005167 self.rmfiles.append(filename)
5168
5169
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005170class FileHandlerTest(BaseFileTest):
5171 def test_delay(self):
5172 os.unlink(self.fn)
Inada Naokifb786922021-04-06 11:18:41 +09005173 fh = logging.FileHandler(self.fn, encoding='utf-8', delay=True)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005174 self.assertIsNone(fh.stream)
5175 self.assertFalse(os.path.exists(self.fn))
5176 fh.handle(logging.makeLogRecord({}))
5177 self.assertIsNotNone(fh.stream)
5178 self.assertTrue(os.path.exists(self.fn))
5179 fh.close()
5180
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005181class RotatingFileHandlerTest(BaseFileTest):
5182 def next_rec(self):
5183 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5184 self.next_message(), None, None, None)
5185
5186 def test_should_not_rollover(self):
5187 # If maxbytes is zero rollover never occurs
Inada Naokifb786922021-04-06 11:18:41 +09005188 rh = logging.handlers.RotatingFileHandler(
5189 self.fn, encoding="utf-8", maxBytes=0)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005190 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005191 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005192
5193 def test_should_rollover(self):
Inada Naokifb786922021-04-06 11:18:41 +09005194 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8", maxBytes=1)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005195 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005196 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005197
5198 def test_file_created(self):
5199 # checks that the file is created and assumes it was created
5200 # by us
Inada Naokifb786922021-04-06 11:18:41 +09005201 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8")
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005202 rh.emit(self.next_rec())
5203 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005204 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005205
5206 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005207 def namer(name):
5208 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005209 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005210 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005211 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005212 rh.emit(self.next_rec())
5213 self.assertLogFile(self.fn)
5214 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005215 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005216 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005217 self.assertLogFile(namer(self.fn + ".2"))
5218 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5219 rh.close()
5220
l0rb519cb872019-11-06 22:21:40 +01005221 def test_namer_rotator_inheritance(self):
5222 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5223 def namer(self, name):
5224 return name + ".test"
5225
5226 def rotator(self, source, dest):
5227 if os.path.exists(source):
Victor Stinnerfe52eb62021-04-28 15:47:10 +02005228 os.replace(source, dest + ".rotated")
l0rb519cb872019-11-06 22:21:40 +01005229
5230 rh = HandlerWithNamerAndRotator(
Inada Naokifb786922021-04-06 11:18:41 +09005231 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
l0rb519cb872019-11-06 22:21:40 +01005232 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5233 rh.emit(self.next_rec())
5234 self.assertLogFile(self.fn)
5235 rh.emit(self.next_rec())
5236 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5237 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5238 rh.close()
5239
Hai Shia3ec3ad2020-05-19 06:02:57 +08005240 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005241 def test_rotator(self):
5242 def namer(name):
5243 return name + ".gz"
5244
5245 def rotator(source, dest):
5246 with open(source, "rb") as sf:
5247 data = sf.read()
5248 compressed = zlib.compress(data, 9)
5249 with open(dest, "wb") as df:
5250 df.write(compressed)
5251 os.remove(source)
5252
5253 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005254 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005255 rh.rotator = rotator
5256 rh.namer = namer
5257 m1 = self.next_rec()
5258 rh.emit(m1)
5259 self.assertLogFile(self.fn)
5260 m2 = self.next_rec()
5261 rh.emit(m2)
5262 fn = namer(self.fn + ".1")
5263 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005264 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005265 with open(fn, "rb") as f:
5266 compressed = f.read()
5267 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005268 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005269 rh.emit(self.next_rec())
5270 fn = namer(self.fn + ".2")
5271 self.assertLogFile(fn)
5272 with open(fn, "rb") as f:
5273 compressed = f.read()
5274 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005275 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005276 rh.emit(self.next_rec())
5277 fn = namer(self.fn + ".2")
5278 with open(fn, "rb") as f:
5279 compressed = f.read()
5280 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005281 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005282 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005283 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005284
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005285class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01005286 # other test methods added below
5287 def test_rollover(self):
Inada Naokifa51c0c2021-04-29 11:34:56 +09005288 fh = logging.handlers.TimedRotatingFileHandler(
5289 self.fn, 'S', encoding="utf-8", backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005290 fmt = logging.Formatter('%(asctime)s %(message)s')
5291 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005292 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005293 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005294 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005295 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005296 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005297 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005298 fh.close()
5299 # At this point, we should have a recent rotated file which we
5300 # can test for the existence of. However, in practice, on some
5301 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005302 # in time to go to look for the log file. So, we go back a fair
5303 # bit, and stop as soon as we see a rotated file. In theory this
5304 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005305 found = False
5306 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005307 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005308 for secs in range(GO_BACK):
5309 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005310 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5311 found = os.path.exists(fn)
5312 if found:
5313 self.rmfiles.append(fn)
5314 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005315 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5316 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005317 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005318 dn, fn = os.path.split(self.fn)
5319 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005320 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5321 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005322 for f in files:
5323 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005324 path = os.path.join(dn, f)
5325 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005326 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005327 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005328
Vinay Sajip0372e102011-05-05 12:59:14 +01005329 def test_invalid(self):
5330 assertRaises = self.assertRaises
5331 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005332 self.fn, 'X', 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, 'W', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005335 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005336 self.fn, 'W7', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005337
Vinay Sajipa7130792013-04-12 17:04:23 +01005338 def test_compute_rollover_daily_attime(self):
5339 currentTime = 0
5340 atTime = datetime.time(12, 0, 0)
5341 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005342 self.fn, encoding="utf-8", when='MIDNIGHT', interval=1, backupCount=0,
5343 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005344 try:
5345 actual = rh.computeRollover(currentTime)
5346 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005347
Vinay Sajipd86ac962013-04-14 12:20:46 +01005348 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5349 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5350 finally:
5351 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005352
Vinay Sajip10e8c492013-05-18 10:19:54 -07005353 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005354 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005355 currentTime = int(time.time())
5356 today = currentTime - currentTime % 86400
5357
Vinay Sajipa7130792013-04-12 17:04:23 +01005358 atTime = datetime.time(12, 0, 0)
5359
Vinay Sajip10e8c492013-05-18 10:19:54 -07005360 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005361 for day in range(7):
5362 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005363 self.fn, encoding="utf-8", when='W%d' % day, interval=1, backupCount=0,
5364 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005365 try:
5366 if wday > day:
5367 # The rollover day has already passed this week, so we
5368 # go over into next week
5369 expected = (7 - wday + day)
5370 else:
5371 expected = (day - wday)
5372 # At this point expected is in days from now, convert to seconds
5373 expected *= 24 * 60 * 60
5374 # Add in the rollover time
5375 expected += 12 * 60 * 60
5376 # Add in adjustment for today
5377 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005378 actual = rh.computeRollover(today)
5379 if actual != expected:
5380 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005381 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005382 self.assertEqual(actual, expected)
5383 if day == wday:
5384 # goes into following week
5385 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005386 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005387 if actual != expected:
5388 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005389 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005390 self.assertEqual(actual, expected)
5391 finally:
5392 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005393
5394
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005395def secs(**kw):
5396 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5397
5398for when, exp in (('S', 1),
5399 ('M', 60),
5400 ('H', 60 * 60),
5401 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005402 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005403 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005404 ('W0', secs(days=4, hours=24)),
5405 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005406 def test_compute_rollover(self, when=when, exp=exp):
5407 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005408 self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
Vinay Sajiped0473c2011-02-26 15:35:38 +00005409 currentTime = 0.0
5410 actual = rh.computeRollover(currentTime)
5411 if exp != actual:
5412 # Failures occur on some systems for MIDNIGHT and W0.
5413 # Print detailed calculation for MIDNIGHT so we can try to see
5414 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005415 if when == 'MIDNIGHT':
5416 try:
5417 if rh.utc:
5418 t = time.gmtime(currentTime)
5419 else:
5420 t = time.localtime(currentTime)
5421 currentHour = t[3]
5422 currentMinute = t[4]
5423 currentSecond = t[5]
5424 # r is the number of seconds left between now and midnight
5425 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5426 currentMinute) * 60 +
5427 currentSecond)
5428 result = currentTime + r
5429 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5430 print('currentHour: %s' % currentHour, file=sys.stderr)
5431 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5432 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5433 print('r: %s' % r, file=sys.stderr)
5434 print('result: %s' % result, file=sys.stderr)
5435 except Exception:
5436 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5437 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005438 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005439 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5440
Vinay Sajip60ccd822011-05-09 17:32:09 +01005441
Vinay Sajip223349c2015-10-01 20:37:54 +01005442@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005443class NTEventLogHandlerTest(BaseTest):
5444 def test_basic(self):
5445 logtype = 'Application'
5446 elh = win32evtlog.OpenEventLog(None, logtype)
5447 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005448
5449 try:
5450 h = logging.handlers.NTEventLogHandler('test_logging')
5451 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005452 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005453 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005454 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005455
Vinay Sajip60ccd822011-05-09 17:32:09 +01005456 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5457 h.handle(r)
5458 h.close()
5459 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005460 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005461 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5462 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5463 found = False
5464 GO_BACK = 100
5465 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5466 for e in events:
5467 if e.SourceName != 'test_logging':
5468 continue
5469 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5470 if msg != 'Test Log Message\r\n':
5471 continue
5472 found = True
5473 break
5474 msg = 'Record not found in event log, went back %d records' % GO_BACK
5475 self.assertTrue(found, msg=msg)
5476
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005477
5478class MiscTestCase(unittest.TestCase):
5479 def test__all__(self):
Victor Stinnerfbf43f02020-08-17 07:20:40 +02005480 not_exported = {
5481 'logThreads', 'logMultiprocessing', 'logProcesses', 'currentframe',
5482 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5483 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', 'root',
5484 'threading'}
5485 support.check__all__(self, logging, not_exported=not_exported)
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005486
5487
Christian Heimes180510d2008-03-03 19:15:45 +00005488# Set the locale to the platform-dependent default. I have no idea
5489# why the test does this, but in any case we save the current locale
5490# first and restore it at the end.
Vinay Sajip1feedb42014-05-31 08:19:12 +01005491@support.run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00005492def test_main():
Vinay Sajipd61910c2016-09-08 01:13:39 +01005493 tests = [
Vinay Sajip1feedb42014-05-31 08:19:12 +01005494 BuiltinLevelsTest, BasicFilterTest, CustomLevelsAndFiltersTest,
5495 HandlerTest, MemoryHandlerTest, ConfigFileTest, SocketHandlerTest,
5496 DatagramHandlerTest, MemoryTest, EncodingTest, WarningsTest,
5497 ConfigDictTest, ManagerTest, FormatterTest, BufferingFormatterTest,
5498 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
5499 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
5500 LoggerAdapterTest, LoggerTest, SMTPHandlerTest, FileHandlerTest,
5501 RotatingFileHandlerTest, LastResortTest, LogRecordTest,
Xiang Zhang0b4b57d2017-06-01 21:11:56 +08005502 ExceptionTest, SysLogHandlerTest, IPv6SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip1feedb42014-05-31 08:19:12 +01005503 NTEventLogHandlerTest, TimedRotatingFileHandlerTest,
Vinay Sajipd61910c2016-09-08 01:13:39 +01005504 UnixSocketHandlerTest, UnixDatagramHandlerTest, UnixSysLogHandlerTest,
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005505 MiscTestCase
Vinay Sajipd61910c2016-09-08 01:13:39 +01005506 ]
5507 if hasattr(logging.handlers, 'QueueListener'):
5508 tests.append(QueueListenerTest)
5509 support.run_unittest(*tests)
Jeremy Hylton096d9862003-07-18 03:19:20 +00005510
Christian Heimes180510d2008-03-03 19:15:45 +00005511if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00005512 test_main()