blob: 1a5f8b60f5ccb5d8baa344aae31361decc6c1d7c [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 Lapeyre65f64b192019-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 Lapeyre65f64b192019-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 Lapeyre65f64b192019-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 Lapeyre65f64b192019-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}'})
Miss Islington (bot)0e960862021-09-19 16:13:04 -07004424
4425 # https://bugs.python.org/issue45128
4426 with support.swap_item(sys.modules, 'multiprocessing', None):
4427 r2 = logging.makeLogRecord({'msg': f'msg2_{key}'})
Irit Katriel3fd69992020-09-08 20:40:04 +01004428
4429 results = {'processName' : name,
4430 'r1.processName': r1.processName,
4431 'r2.processName': r2.processName,
4432 }
4433 finally:
4434 logging.logMultiprocessing = prev_logMultiprocessing
4435 if conn:
4436 conn.send(results)
4437 else:
4438 return results
4439
4440 def test_multiprocessing(self):
4441 multiprocessing_imported = 'multiprocessing' in sys.modules
4442 try:
4443 # logMultiprocessing is True by default
4444 self.assertEqual(logging.logMultiprocessing, True)
4445
4446 LOG_MULTI_PROCESSING = True
4447 # When logMultiprocessing == True:
4448 # In the main process processName = 'MainProcess'
Vinay Sajipbd3d5cf2011-05-17 07:41:18 +01004449 r = logging.makeLogRecord({})
Irit Katriel3fd69992020-09-08 20:40:04 +01004450 self.assertEqual(r.processName, 'MainProcess')
4451
4452 results = self._extract_logrecord_process_name(1, LOG_MULTI_PROCESSING)
4453 self.assertEqual('MainProcess', results['processName'])
4454 self.assertEqual('MainProcess', results['r1.processName'])
4455 self.assertEqual('MainProcess', results['r2.processName'])
4456
4457 # In other processes, processName is correct when multiprocessing in imported,
4458 # but it is (incorrectly) defaulted to 'MainProcess' otherwise (bpo-38762).
4459 import multiprocessing
4460 parent_conn, child_conn = multiprocessing.Pipe()
4461 p = multiprocessing.Process(
4462 target=self._extract_logrecord_process_name,
4463 args=(2, LOG_MULTI_PROCESSING, child_conn,)
4464 )
4465 p.start()
4466 results = parent_conn.recv()
4467 self.assertNotEqual('MainProcess', results['processName'])
4468 self.assertEqual(results['processName'], results['r1.processName'])
4469 self.assertEqual('MainProcess', results['r2.processName'])
4470 p.join()
4471
4472 finally:
4473 if multiprocessing_imported:
4474 import multiprocessing
4475
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004476 def test_optional(self):
4477 r = logging.makeLogRecord({})
4478 NOT_NONE = self.assertIsNotNone
Antoine Pitroua6a4dc82017-09-07 18:56:24 +02004479 NOT_NONE(r.thread)
4480 NOT_NONE(r.threadName)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01004481 NOT_NONE(r.process)
4482 NOT_NONE(r.processName)
4483 log_threads = logging.logThreads
4484 log_processes = logging.logProcesses
4485 log_multiprocessing = logging.logMultiprocessing
4486 try:
4487 logging.logThreads = False
4488 logging.logProcesses = False
4489 logging.logMultiprocessing = False
4490 r = logging.makeLogRecord({})
4491 NONE = self.assertIsNone
4492 NONE(r.thread)
4493 NONE(r.threadName)
4494 NONE(r.process)
4495 NONE(r.processName)
4496 finally:
4497 logging.logThreads = log_threads
4498 logging.logProcesses = log_processes
4499 logging.logMultiprocessing = log_multiprocessing
4500
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004501class BasicConfigTest(unittest.TestCase):
4502
Vinay Sajip95bf5042011-04-20 11:50:56 +01004503 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004504
4505 def setUp(self):
4506 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01004507 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01004508 self.saved_handlers = logging._handlers.copy()
4509 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01004510 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01004511 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004512 logging.root.handlers = []
4513
4514 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01004515 for h in logging.root.handlers[:]:
4516 logging.root.removeHandler(h)
4517 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004518 super(BasicConfigTest, self).tearDown()
4519
Vinay Sajip3def7e02011-04-20 10:58:06 +01004520 def cleanup(self):
4521 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01004522 logging._handlers.clear()
4523 logging._handlers.update(self.saved_handlers)
4524 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1d094af2019-09-22 03:51:51 +01004525 logging.root.setLevel(self.original_logging_level)
Vinay Sajip3def7e02011-04-20 10:58:06 +01004526
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004527 def test_no_kwargs(self):
4528 logging.basicConfig()
4529
4530 # handler defaults to a StreamHandler to sys.stderr
4531 self.assertEqual(len(logging.root.handlers), 1)
4532 handler = logging.root.handlers[0]
4533 self.assertIsInstance(handler, logging.StreamHandler)
4534 self.assertEqual(handler.stream, sys.stderr)
4535
4536 formatter = handler.formatter
4537 # format defaults to logging.BASIC_FORMAT
4538 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
4539 # datefmt defaults to None
4540 self.assertIsNone(formatter.datefmt)
4541 # style defaults to %
4542 self.assertIsInstance(formatter._style, logging.PercentStyle)
4543
Florent Xiclunae41f0de2011-11-11 19:39:25 +01004544 # level is not explicitly set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01004545 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004546
Vinay Sajip1fd12022014-01-13 21:59:56 +00004547 def test_strformatstyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004548 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004549 logging.basicConfig(stream=sys.stdout, style="{")
4550 logging.error("Log an error")
4551 sys.stdout.seek(0)
4552 self.assertEqual(output.getvalue().strip(),
4553 "ERROR:root:Log an error")
4554
4555 def test_stringtemplatestyle(self):
Vinay Sajip1feedb42014-05-31 08:19:12 +01004556 with support.captured_stdout() as output:
Vinay Sajip1fd12022014-01-13 21:59:56 +00004557 logging.basicConfig(stream=sys.stdout, style="$")
4558 logging.error("Log an error")
4559 sys.stdout.seek(0)
4560 self.assertEqual(output.getvalue().strip(),
4561 "ERROR:root:Log an error")
4562
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004563 def test_filename(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004564
4565 def cleanup(h1, h2, fn):
4566 h1.close()
4567 h2.close()
4568 os.remove(fn)
4569
Inada Naokifb786922021-04-06 11:18:41 +09004570 logging.basicConfig(filename='test.log', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004571
4572 self.assertEqual(len(logging.root.handlers), 1)
4573 handler = logging.root.handlers[0]
4574 self.assertIsInstance(handler, logging.FileHandler)
4575
Inada Naokifb786922021-04-06 11:18:41 +09004576 expected = logging.FileHandler('test.log', 'a', encoding='utf-8')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004577 self.assertEqual(handler.stream.mode, expected.stream.mode)
4578 self.assertEqual(handler.stream.name, expected.stream.name)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004579 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004580
4581 def test_filemode(self):
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004582
4583 def cleanup(h1, h2, fn):
4584 h1.close()
4585 h2.close()
4586 os.remove(fn)
4587
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004588 logging.basicConfig(filename='test.log', filemode='wb')
4589
4590 handler = logging.root.handlers[0]
4591 expected = logging.FileHandler('test.log', 'wb')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004592 self.assertEqual(handler.stream.mode, expected.stream.mode)
Vinay Sajipbecf5c62013-03-08 09:50:57 +00004593 self.addCleanup(cleanup, handler, expected, 'test.log')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004594
4595 def test_stream(self):
4596 stream = io.StringIO()
4597 self.addCleanup(stream.close)
4598 logging.basicConfig(stream=stream)
4599
4600 self.assertEqual(len(logging.root.handlers), 1)
4601 handler = logging.root.handlers[0]
4602 self.assertIsInstance(handler, logging.StreamHandler)
4603 self.assertEqual(handler.stream, stream)
4604
4605 def test_format(self):
BNMetrics18fb1fb2018-10-15 19:41:36 +01004606 logging.basicConfig(format='%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004607
4608 formatter = logging.root.handlers[0].formatter
BNMetrics18fb1fb2018-10-15 19:41:36 +01004609 self.assertEqual(formatter._style._fmt, '%(asctime)s - %(message)s')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004610
4611 def test_datefmt(self):
4612 logging.basicConfig(datefmt='bar')
4613
4614 formatter = logging.root.handlers[0].formatter
4615 self.assertEqual(formatter.datefmt, 'bar')
4616
4617 def test_style(self):
4618 logging.basicConfig(style='$')
4619
4620 formatter = logging.root.handlers[0].formatter
4621 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
4622
4623 def test_level(self):
4624 old_level = logging.root.level
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004625 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004626
4627 logging.basicConfig(level=57)
4628 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004629 # Test that second call has no effect
4630 logging.basicConfig(level=58)
4631 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004632
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004633 def test_incompatible(self):
4634 assertRaises = self.assertRaises
4635 handlers = [logging.StreamHandler()]
4636 stream = sys.stderr
4637 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004638 stream=stream)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004639 assertRaises(ValueError, logging.basicConfig, filename='test.log',
BNMetrics18fb1fb2018-10-15 19:41:36 +01004640 handlers=handlers)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004641 assertRaises(ValueError, logging.basicConfig, stream=stream,
BNMetrics18fb1fb2018-10-15 19:41:36 +01004642 handlers=handlers)
Vinay Sajipd55436a2015-03-18 08:47:58 +00004643 # Issue 23207: test for invalid kwargs
4644 assertRaises(ValueError, logging.basicConfig, loglevel=logging.INFO)
4645 # Should pop both filename and filemode even if filename is None
4646 logging.basicConfig(filename=None, filemode='a')
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004647
4648 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004649 handlers = [
4650 logging.StreamHandler(),
4651 logging.StreamHandler(sys.stdout),
4652 logging.StreamHandler(),
4653 ]
4654 f = logging.Formatter()
4655 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004656 logging.basicConfig(handlers=handlers)
4657 self.assertIs(handlers[0], logging.root.handlers[0])
4658 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004659 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004660 self.assertIsNotNone(handlers[0].formatter)
4661 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01004662 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01004663 self.assertIs(handlers[0].formatter, handlers[1].formatter)
4664
Dong-hee Nacf67d6a2018-06-25 13:11:09 +09004665 def test_force(self):
4666 old_string_io = io.StringIO()
4667 new_string_io = io.StringIO()
4668 old_handlers = [logging.StreamHandler(old_string_io)]
4669 new_handlers = [logging.StreamHandler(new_string_io)]
4670 logging.basicConfig(level=logging.WARNING, handlers=old_handlers)
4671 logging.warning('warn')
4672 logging.info('info')
4673 logging.debug('debug')
4674 self.assertEqual(len(logging.root.handlers), 1)
4675 logging.basicConfig(level=logging.INFO, handlers=new_handlers,
4676 force=True)
4677 logging.warning('warn')
4678 logging.info('info')
4679 logging.debug('debug')
4680 self.assertEqual(len(logging.root.handlers), 1)
4681 self.assertEqual(old_string_io.getvalue().strip(),
4682 'WARNING:root:warn')
4683 self.assertEqual(new_string_io.getvalue().strip(),
4684 'WARNING:root:warn\nINFO:root:info')
4685
Vinay Sajipca7b5042019-06-17 17:40:52 +01004686 def test_encoding(self):
4687 try:
4688 encoding = 'utf-8'
4689 logging.basicConfig(filename='test.log', encoding=encoding,
4690 errors='strict',
4691 format='%(message)s', level=logging.DEBUG)
4692
4693 self.assertEqual(len(logging.root.handlers), 1)
4694 handler = logging.root.handlers[0]
4695 self.assertIsInstance(handler, logging.FileHandler)
4696 self.assertEqual(handler.encoding, encoding)
4697 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4698 finally:
4699 handler.close()
4700 with open('test.log', encoding='utf-8') as f:
4701 data = f.read().strip()
4702 os.remove('test.log')
4703 self.assertEqual(data,
4704 'The Øresund Bridge joins Copenhagen to Malmö')
4705
4706 def test_encoding_errors(self):
4707 try:
4708 encoding = 'ascii'
4709 logging.basicConfig(filename='test.log', encoding=encoding,
4710 errors='ignore',
4711 format='%(message)s', level=logging.DEBUG)
4712
4713 self.assertEqual(len(logging.root.handlers), 1)
4714 handler = logging.root.handlers[0]
4715 self.assertIsInstance(handler, logging.FileHandler)
4716 self.assertEqual(handler.encoding, encoding)
4717 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4718 finally:
4719 handler.close()
4720 with open('test.log', encoding='utf-8') as f:
4721 data = f.read().strip()
4722 os.remove('test.log')
4723 self.assertEqual(data, 'The resund Bridge joins Copenhagen to Malm')
4724
4725 def test_encoding_errors_default(self):
4726 try:
4727 encoding = 'ascii'
4728 logging.basicConfig(filename='test.log', encoding=encoding,
4729 format='%(message)s', level=logging.DEBUG)
4730
4731 self.assertEqual(len(logging.root.handlers), 1)
4732 handler = logging.root.handlers[0]
4733 self.assertIsInstance(handler, logging.FileHandler)
4734 self.assertEqual(handler.encoding, encoding)
4735 self.assertEqual(handler.errors, 'backslashreplace')
4736 logging.debug('😂: ☃️: The Øresund Bridge joins Copenhagen to Malmö')
4737 finally:
4738 handler.close()
4739 with open('test.log', encoding='utf-8') as f:
4740 data = f.read().strip()
4741 os.remove('test.log')
4742 self.assertEqual(data, r'\U0001f602: \u2603\ufe0f: The \xd8resund '
4743 r'Bridge joins Copenhagen to Malm\xf6')
4744
4745 def test_encoding_errors_none(self):
4746 # Specifying None should behave as 'strict'
4747 try:
4748 encoding = 'ascii'
4749 logging.basicConfig(filename='test.log', encoding=encoding,
4750 errors=None,
4751 format='%(message)s', level=logging.DEBUG)
4752
4753 self.assertEqual(len(logging.root.handlers), 1)
4754 handler = logging.root.handlers[0]
4755 self.assertIsInstance(handler, logging.FileHandler)
4756 self.assertEqual(handler.encoding, encoding)
4757 self.assertIsNone(handler.errors)
4758
4759 message = []
4760
4761 def dummy_handle_error(record):
4762 _, v, _ = sys.exc_info()
4763 message.append(str(v))
4764
4765 handler.handleError = dummy_handle_error
4766 logging.debug('The Øresund Bridge joins Copenhagen to Malmö')
4767 self.assertTrue(message)
4768 self.assertIn("'ascii' codec can't encode "
4769 "character '\\xd8' in position 4:", message[0])
4770 finally:
4771 handler.close()
4772 with open('test.log', encoding='utf-8') as f:
4773 data = f.read().strip()
4774 os.remove('test.log')
4775 # didn't write anything due to the encoding error
4776 self.assertEqual(data, r'')
4777
4778
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004779 def _test_log(self, method, level=None):
4780 # logging.root has no handlers so basicConfig should be called
4781 called = []
4782
4783 old_basic_config = logging.basicConfig
4784 def my_basic_config(*a, **kw):
4785 old_basic_config()
4786 old_level = logging.root.level
4787 logging.root.setLevel(100) # avoid having messages in stderr
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004788 self.addCleanup(logging.root.setLevel, old_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004789 called.append((a, kw))
4790
Vinay Sajip1feedb42014-05-31 08:19:12 +01004791 support.patch(self, logging, 'basicConfig', my_basic_config)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004792
4793 log_method = getattr(logging, method)
4794 if level is not None:
4795 log_method(level, "test me")
4796 else:
4797 log_method("test me")
4798
4799 # basicConfig was called with no arguments
4800 self.assertEqual(called, [((), {})])
4801
4802 def test_log(self):
4803 self._test_log('log', logging.WARNING)
4804
4805 def test_debug(self):
4806 self._test_log('debug')
4807
4808 def test_info(self):
4809 self._test_log('info')
4810
4811 def test_warning(self):
4812 self._test_log('warning')
4813
4814 def test_error(self):
4815 self._test_log('error')
4816
4817 def test_critical(self):
4818 self._test_log('critical')
4819
4820
4821class LoggerAdapterTest(unittest.TestCase):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004822 def setUp(self):
4823 super(LoggerAdapterTest, self).setUp()
4824 old_handler_list = logging._handlerList[:]
4825
4826 self.recording = RecordingHandler()
4827 self.logger = logging.root
4828 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004829 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004830 self.addCleanup(self.recording.close)
4831
4832 def cleanup():
4833 logging._handlerList[:] = old_handler_list
4834
4835 self.addCleanup(cleanup)
4836 self.addCleanup(logging.shutdown)
4837 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
4838
4839 def test_exception(self):
4840 msg = 'testing exception: %r'
4841 exc = None
4842 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004843 1 / 0
4844 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004845 exc = e
4846 self.adapter.exception(msg, self.recording)
4847
4848 self.assertEqual(len(self.recording.records), 1)
4849 record = self.recording.records[0]
4850 self.assertEqual(record.levelno, logging.ERROR)
4851 self.assertEqual(record.msg, msg)
4852 self.assertEqual(record.args, (self.recording,))
4853 self.assertEqual(record.exc_info,
4854 (exc.__class__, exc, exc.__traceback__))
4855
Vinay Sajip02a8f9e2014-09-14 21:29:11 +01004856 def test_exception_excinfo(self):
4857 try:
4858 1 / 0
4859 except ZeroDivisionError as e:
4860 exc = e
4861
4862 self.adapter.exception('exc_info test', exc_info=exc)
4863
4864 self.assertEqual(len(self.recording.records), 1)
4865 record = self.recording.records[0]
4866 self.assertEqual(record.exc_info,
4867 (exc.__class__, exc, exc.__traceback__))
4868
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004869 def test_critical(self):
4870 msg = 'critical test! %r'
4871 self.adapter.critical(msg, self.recording)
4872
4873 self.assertEqual(len(self.recording.records), 1)
4874 record = self.recording.records[0]
4875 self.assertEqual(record.levelno, logging.CRITICAL)
4876 self.assertEqual(record.msg, msg)
4877 self.assertEqual(record.args, (self.recording,))
4878
4879 def test_is_enabled_for(self):
4880 old_disable = self.adapter.logger.manager.disable
4881 self.adapter.logger.manager.disable = 33
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004882 self.addCleanup(setattr, self.adapter.logger.manager, 'disable',
4883 old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004884 self.assertFalse(self.adapter.isEnabledFor(32))
4885
4886 def test_has_handlers(self):
4887 self.assertTrue(self.adapter.hasHandlers())
4888
4889 for handler in self.logger.handlers:
4890 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004891
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004892 self.assertFalse(self.logger.hasHandlers())
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004893 self.assertFalse(self.adapter.hasHandlers())
4894
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004895 def test_nested(self):
Łukasz Langace9e6252017-10-19 10:24:55 -07004896 class Adapter(logging.LoggerAdapter):
4897 prefix = 'Adapter'
4898
4899 def process(self, msg, kwargs):
4900 return f"{self.prefix} {msg}", kwargs
4901
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004902 msg = 'Adapters can be nested, yo.'
Łukasz Langace9e6252017-10-19 10:24:55 -07004903 adapter = Adapter(logger=self.logger, extra=None)
4904 adapter_adapter = Adapter(logger=adapter, extra=None)
4905 adapter_adapter.prefix = 'AdapterAdapter'
4906 self.assertEqual(repr(adapter), repr(adapter_adapter))
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004907 adapter_adapter.log(logging.CRITICAL, msg, self.recording)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004908 self.assertEqual(len(self.recording.records), 1)
4909 record = self.recording.records[0]
4910 self.assertEqual(record.levelno, logging.CRITICAL)
Łukasz Langace9e6252017-10-19 10:24:55 -07004911 self.assertEqual(record.msg, f"Adapter AdapterAdapter {msg}")
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004912 self.assertEqual(record.args, (self.recording,))
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004913 orig_manager = adapter_adapter.manager
Łukasz Langace9e6252017-10-19 10:24:55 -07004914 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004915 self.assertIs(self.logger.manager, orig_manager)
4916 temp_manager = object()
4917 try:
4918 adapter_adapter.manager = temp_manager
4919 self.assertIs(adapter_adapter.manager, temp_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004920 self.assertIs(adapter.manager, temp_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004921 self.assertIs(self.logger.manager, temp_manager)
4922 finally:
4923 adapter_adapter.manager = orig_manager
4924 self.assertIs(adapter_adapter.manager, orig_manager)
Łukasz Langace9e6252017-10-19 10:24:55 -07004925 self.assertIs(adapter.manager, orig_manager)
Łukasz Langa0b6a1182017-10-18 17:28:51 -07004926 self.assertIs(self.logger.manager, orig_manager)
Łukasz Langa1bbd4822017-09-14 11:34:47 -04004927
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004928
Eric Larson9fdb76c2020-09-25 14:08:50 -04004929class LoggerTest(BaseTest, AssertErrorMessage):
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004930
4931 def setUp(self):
4932 super(LoggerTest, self).setUp()
4933 self.recording = RecordingHandler()
4934 self.logger = logging.Logger(name='blah')
4935 self.logger.addHandler(self.recording)
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004936 self.addCleanup(self.logger.removeHandler, self.recording)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004937 self.addCleanup(self.recording.close)
4938 self.addCleanup(logging.shutdown)
4939
4940 def test_set_invalid_level(self):
Eric Larson9fdb76c2020-09-25 14:08:50 -04004941 self.assert_error_message(
4942 TypeError, 'Level not an integer or a valid string: None',
4943 self.logger.setLevel, None)
4944 self.assert_error_message(
4945 TypeError, 'Level not an integer or a valid string: (0, 0)',
4946 self.logger.setLevel, (0, 0))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004947
4948 def test_exception(self):
4949 msg = 'testing exception: %r'
4950 exc = None
4951 try:
Vinay Sajip5056c8c2011-06-05 09:31:34 +01004952 1 / 0
4953 except ZeroDivisionError as e:
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004954 exc = e
4955 self.logger.exception(msg, self.recording)
4956
4957 self.assertEqual(len(self.recording.records), 1)
4958 record = self.recording.records[0]
4959 self.assertEqual(record.levelno, logging.ERROR)
4960 self.assertEqual(record.msg, msg)
4961 self.assertEqual(record.args, (self.recording,))
4962 self.assertEqual(record.exc_info,
4963 (exc.__class__, exc, exc.__traceback__))
4964
4965 def test_log_invalid_level_with_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004966 with support.swap_attr(logging, 'raiseExceptions', True):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004967 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004968
4969 def test_log_invalid_level_no_raise(self):
Serhiy Storchaka296b3472015-07-21 22:40:18 +03004970 with support.swap_attr(logging, 'raiseExceptions', False):
Serhiy Storchaka750eae12015-07-21 22:39:26 +03004971 self.logger.log('10', 'test message') # no exception happens
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004972
4973 def test_find_caller_with_stack_info(self):
4974 called = []
Vinay Sajip1feedb42014-05-31 08:19:12 +01004975 support.patch(self, logging.traceback, 'print_stack',
4976 lambda f, file: called.append(file.getvalue()))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01004977
4978 self.logger.findCaller(stack_info=True)
4979
4980 self.assertEqual(len(called), 1)
4981 self.assertEqual('Stack (most recent call last):\n', called[0])
4982
Vinay Sajipdde9fdb2018-06-05 17:24:18 +01004983 def test_find_caller_with_stacklevel(self):
4984 the_level = 1
4985
4986 def innermost():
4987 self.logger.warning('test', stacklevel=the_level)
4988
4989 def inner():
4990 innermost()
4991
4992 def outer():
4993 inner()
4994
4995 records = self.recording.records
4996 outer()
4997 self.assertEqual(records[-1].funcName, 'innermost')
4998 lineno = records[-1].lineno
4999 the_level += 1
5000 outer()
5001 self.assertEqual(records[-1].funcName, 'inner')
5002 self.assertGreater(records[-1].lineno, lineno)
5003 lineno = records[-1].lineno
5004 the_level += 1
5005 outer()
5006 self.assertEqual(records[-1].funcName, 'outer')
5007 self.assertGreater(records[-1].lineno, lineno)
5008 lineno = records[-1].lineno
5009 the_level += 1
5010 outer()
5011 self.assertEqual(records[-1].funcName, 'test_find_caller_with_stacklevel')
5012 self.assertGreater(records[-1].lineno, lineno)
5013
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005014 def test_make_record_with_extra_overwrite(self):
5015 name = 'my record'
5016 level = 13
5017 fn = lno = msg = args = exc_info = func = sinfo = None
5018 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
5019 exc_info, func, sinfo)
5020
5021 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
5022 extra = {key: 'some value'}
5023 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
5024 fn, lno, msg, args, exc_info,
5025 extra=extra, sinfo=sinfo)
5026
5027 def test_make_record_with_extra_no_overwrite(self):
5028 name = 'my record'
5029 level = 13
5030 fn = lno = msg = args = exc_info = func = sinfo = None
5031 extra = {'valid_key': 'some value'}
5032 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
5033 exc_info, extra=extra, sinfo=sinfo)
5034 self.assertIn('valid_key', result.__dict__)
5035
5036 def test_has_handlers(self):
5037 self.assertTrue(self.logger.hasHandlers())
5038
5039 for handler in self.logger.handlers:
5040 self.logger.removeHandler(handler)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005041 self.assertFalse(self.logger.hasHandlers())
5042
5043 def test_has_handlers_no_propagate(self):
5044 child_logger = logging.getLogger('blah.child')
5045 child_logger.propagate = False
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005046 self.assertFalse(child_logger.hasHandlers())
5047
5048 def test_is_enabled_for(self):
5049 old_disable = self.logger.manager.disable
5050 self.logger.manager.disable = 23
Vinay Sajip5056c8c2011-06-05 09:31:34 +01005051 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005052 self.assertFalse(self.logger.isEnabledFor(22))
5053
Timo Furrer6e3ca642018-06-01 09:29:46 +02005054 def test_is_enabled_for_disabled_logger(self):
5055 old_disabled = self.logger.disabled
5056 old_disable = self.logger.manager.disable
5057
5058 self.logger.disabled = True
5059 self.logger.manager.disable = 21
5060
5061 self.addCleanup(setattr, self.logger, 'disabled', old_disabled)
5062 self.addCleanup(setattr, self.logger.manager, 'disable', old_disable)
5063
5064 self.assertFalse(self.logger.isEnabledFor(22))
5065
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005066 def test_root_logger_aliases(self):
5067 root = logging.getLogger()
5068 self.assertIs(root, logging.root)
5069 self.assertIs(root, logging.getLogger(None))
5070 self.assertIs(root, logging.getLogger(''))
Vinay Sajipcb65b3a2019-08-02 16:53:00 +01005071 self.assertIs(root, logging.getLogger('root'))
Florent Xicluna5252f9f2011-11-07 19:43:05 +01005072 self.assertIs(root, logging.getLogger('foo').root)
5073 self.assertIs(root, logging.getLogger('foo.bar').root)
5074 self.assertIs(root, logging.getLogger('foo').parent)
5075
5076 self.assertIsNot(root, logging.getLogger('\0'))
5077 self.assertIsNot(root, logging.getLogger('foo.bar').parent)
5078
5079 def test_invalid_names(self):
5080 self.assertRaises(TypeError, logging.getLogger, any)
5081 self.assertRaises(TypeError, logging.getLogger, b'foo')
5082
Vinay Sajip6260d9f2017-06-06 16:34:29 +01005083 def test_pickling(self):
5084 for proto in range(pickle.HIGHEST_PROTOCOL + 1):
5085 for name in ('', 'root', 'foo', 'foo.bar', 'baz.bar'):
5086 logger = logging.getLogger(name)
5087 s = pickle.dumps(logger, proto)
5088 unpickled = pickle.loads(s)
5089 self.assertIs(unpickled, logger)
5090
Avram Lubkin78c18a92017-07-30 05:36:33 -04005091 def test_caching(self):
5092 root = self.root_logger
5093 logger1 = logging.getLogger("abc")
5094 logger2 = logging.getLogger("abc.def")
5095
5096 # Set root logger level and ensure cache is empty
5097 root.setLevel(logging.ERROR)
5098 self.assertEqual(logger2.getEffectiveLevel(), logging.ERROR)
5099 self.assertEqual(logger2._cache, {})
5100
5101 # Ensure cache is populated and calls are consistent
5102 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5103 self.assertFalse(logger2.isEnabledFor(logging.DEBUG))
5104 self.assertEqual(logger2._cache, {logging.ERROR: True, logging.DEBUG: False})
5105 self.assertEqual(root._cache, {})
5106 self.assertTrue(logger2.isEnabledFor(logging.ERROR))
5107
5108 # Ensure root cache gets populated
5109 self.assertEqual(root._cache, {})
5110 self.assertTrue(root.isEnabledFor(logging.ERROR))
5111 self.assertEqual(root._cache, {logging.ERROR: True})
5112
5113 # Set parent logger level and ensure caches are emptied
5114 logger1.setLevel(logging.CRITICAL)
5115 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5116 self.assertEqual(logger2._cache, {})
5117
5118 # Ensure logger2 uses parent logger's effective level
5119 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5120
5121 # Set level to NOTSET and ensure caches are empty
5122 logger2.setLevel(logging.NOTSET)
5123 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5124 self.assertEqual(logger2._cache, {})
5125 self.assertEqual(logger1._cache, {})
5126 self.assertEqual(root._cache, {})
5127
5128 # Verify logger2 follows parent and not root
5129 self.assertFalse(logger2.isEnabledFor(logging.ERROR))
5130 self.assertTrue(logger2.isEnabledFor(logging.CRITICAL))
5131 self.assertFalse(logger1.isEnabledFor(logging.ERROR))
5132 self.assertTrue(logger1.isEnabledFor(logging.CRITICAL))
5133 self.assertTrue(root.isEnabledFor(logging.ERROR))
5134
5135 # Disable logging in manager and ensure caches are clear
5136 logging.disable()
5137 self.assertEqual(logger2.getEffectiveLevel(), logging.CRITICAL)
5138 self.assertEqual(logger2._cache, {})
5139 self.assertEqual(logger1._cache, {})
5140 self.assertEqual(root._cache, {})
5141
5142 # Ensure no loggers are enabled
5143 self.assertFalse(logger1.isEnabledFor(logging.CRITICAL))
5144 self.assertFalse(logger2.isEnabledFor(logging.CRITICAL))
5145 self.assertFalse(root.isEnabledFor(logging.CRITICAL))
5146
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01005147
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005148class BaseFileTest(BaseTest):
5149 "Base class for handler tests that write log files"
5150
5151 def setUp(self):
5152 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005153 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
5154 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005155 self.rmfiles = []
5156
5157 def tearDown(self):
5158 for fn in self.rmfiles:
5159 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00005160 if os.path.exists(self.fn):
5161 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00005162 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005163
5164 def assertLogFile(self, filename):
5165 "Assert a log file is there and register it for deletion"
5166 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01005167 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005168 self.rmfiles.append(filename)
5169
5170
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005171class FileHandlerTest(BaseFileTest):
5172 def test_delay(self):
5173 os.unlink(self.fn)
Inada Naokifb786922021-04-06 11:18:41 +09005174 fh = logging.FileHandler(self.fn, encoding='utf-8', delay=True)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01005175 self.assertIsNone(fh.stream)
5176 self.assertFalse(os.path.exists(self.fn))
5177 fh.handle(logging.makeLogRecord({}))
5178 self.assertIsNotNone(fh.stream)
5179 self.assertTrue(os.path.exists(self.fn))
5180 fh.close()
5181
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005182class RotatingFileHandlerTest(BaseFileTest):
5183 def next_rec(self):
5184 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
5185 self.next_message(), None, None, None)
5186
5187 def test_should_not_rollover(self):
5188 # If maxbytes is zero rollover never occurs
Inada Naokifb786922021-04-06 11:18:41 +09005189 rh = logging.handlers.RotatingFileHandler(
5190 self.fn, encoding="utf-8", maxBytes=0)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005191 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00005192 rh.close()
Miss Islington (bot)5aca34f2021-10-11 03:01:12 -07005193 # bpo-45401 - test with special file
5194 # We set maxBytes to 1 so that rollover would normally happen, except
5195 # for the check for regular files
5196 rh = logging.handlers.RotatingFileHandler(
5197 os.devnull, encoding="utf-8", maxBytes=1)
5198 self.assertFalse(rh.shouldRollover(self.next_rec()))
5199 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005200
5201 def test_should_rollover(self):
Inada Naokifb786922021-04-06 11:18:41 +09005202 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8", maxBytes=1)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005203 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00005204 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005205
5206 def test_file_created(self):
5207 # checks that the file is created and assumes it was created
5208 # by us
Inada Naokifb786922021-04-06 11:18:41 +09005209 rh = logging.handlers.RotatingFileHandler(self.fn, encoding="utf-8")
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005210 rh.emit(self.next_rec())
5211 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00005212 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005213
5214 def test_rollover_filenames(self):
Vinay Sajip23b94d02012-01-04 12:02:26 +00005215 def namer(name):
5216 return name + ".test"
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005217 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005218 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005219 rh.namer = namer
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005220 rh.emit(self.next_rec())
5221 self.assertLogFile(self.fn)
5222 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005223 self.assertLogFile(namer(self.fn + ".1"))
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005224 rh.emit(self.next_rec())
Vinay Sajip23b94d02012-01-04 12:02:26 +00005225 self.assertLogFile(namer(self.fn + ".2"))
5226 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
5227 rh.close()
5228
l0rb519cb872019-11-06 22:21:40 +01005229 def test_namer_rotator_inheritance(self):
5230 class HandlerWithNamerAndRotator(logging.handlers.RotatingFileHandler):
5231 def namer(self, name):
5232 return name + ".test"
5233
5234 def rotator(self, source, dest):
5235 if os.path.exists(source):
Victor Stinnerfe52eb62021-04-28 15:47:10 +02005236 os.replace(source, dest + ".rotated")
l0rb519cb872019-11-06 22:21:40 +01005237
5238 rh = HandlerWithNamerAndRotator(
Inada Naokifb786922021-04-06 11:18:41 +09005239 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
l0rb519cb872019-11-06 22:21:40 +01005240 self.assertEqual(rh.namer(self.fn), self.fn + ".test")
5241 rh.emit(self.next_rec())
5242 self.assertLogFile(self.fn)
5243 rh.emit(self.next_rec())
5244 self.assertLogFile(rh.namer(self.fn + ".1") + ".rotated")
5245 self.assertFalse(os.path.exists(rh.namer(self.fn + ".1")))
5246 rh.close()
5247
Hai Shia3ec3ad2020-05-19 06:02:57 +08005248 @support.requires_zlib()
Vinay Sajip23b94d02012-01-04 12:02:26 +00005249 def test_rotator(self):
5250 def namer(name):
5251 return name + ".gz"
5252
5253 def rotator(source, dest):
5254 with open(source, "rb") as sf:
5255 data = sf.read()
5256 compressed = zlib.compress(data, 9)
5257 with open(dest, "wb") as df:
5258 df.write(compressed)
5259 os.remove(source)
5260
5261 rh = logging.handlers.RotatingFileHandler(
Inada Naokifb786922021-04-06 11:18:41 +09005262 self.fn, encoding="utf-8", backupCount=2, maxBytes=1)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005263 rh.rotator = rotator
5264 rh.namer = namer
5265 m1 = self.next_rec()
5266 rh.emit(m1)
5267 self.assertLogFile(self.fn)
5268 m2 = self.next_rec()
5269 rh.emit(m2)
5270 fn = namer(self.fn + ".1")
5271 self.assertLogFile(fn)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005272 newline = os.linesep
Vinay Sajip23b94d02012-01-04 12:02:26 +00005273 with open(fn, "rb") as f:
5274 compressed = f.read()
5275 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005276 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005277 rh.emit(self.next_rec())
5278 fn = namer(self.fn + ".2")
5279 self.assertLogFile(fn)
5280 with open(fn, "rb") as f:
5281 compressed = f.read()
5282 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005283 self.assertEqual(data.decode("ascii"), m1.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005284 rh.emit(self.next_rec())
5285 fn = namer(self.fn + ".2")
5286 with open(fn, "rb") as f:
5287 compressed = f.read()
5288 data = zlib.decompress(compressed)
Vinay Sajip2c94cdd2012-01-07 23:26:17 +00005289 self.assertEqual(data.decode("ascii"), m2.msg + newline)
Vinay Sajip23b94d02012-01-04 12:02:26 +00005290 self.assertFalse(os.path.exists(namer(self.fn + ".3")))
Vinay Sajipb046b802010-10-30 09:50:18 +00005291 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005292
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005293class TimedRotatingFileHandlerTest(BaseFileTest):
Miss Islington (bot)5aca34f2021-10-11 03:01:12 -07005294 def test_should_not_rollover(self):
5295 # See bpo-45401. Should only ever rollover regular files
5296 fh = logging.handlers.TimedRotatingFileHandler(
5297 os.devnull, 'S', encoding="utf-8", backupCount=1)
5298 time.sleep(1.1) # a little over a second ...
5299 r = logging.makeLogRecord({'msg': 'testing - device file'})
5300 self.assertFalse(fh.shouldRollover(r))
5301
Vinay Sajip7367d082011-05-02 13:17:27 +01005302 # other test methods added below
5303 def test_rollover(self):
Inada Naokifa51c0c2021-04-29 11:34:56 +09005304 fh = logging.handlers.TimedRotatingFileHandler(
5305 self.fn, 'S', encoding="utf-8", backupCount=1)
Vinay Sajipb6f66702012-03-05 09:43:47 +00005306 fmt = logging.Formatter('%(asctime)s %(message)s')
5307 fh.setFormatter(fmt)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005308 r1 = logging.makeLogRecord({'msg': 'testing - initial'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005309 fh.emit(r1)
Vinay Sajipfd285022011-05-07 17:01:22 +01005310 self.assertLogFile(self.fn)
Vinay Sajip8d217c62012-03-05 12:45:51 +00005311 time.sleep(1.1) # a little over a second ...
Vinay Sajipb2fcd3a2012-03-05 20:02:53 +00005312 r2 = logging.makeLogRecord({'msg': 'testing - after delay'})
Vinay Sajip8d217c62012-03-05 12:45:51 +00005313 fh.emit(r2)
Vinay Sajipfd285022011-05-07 17:01:22 +01005314 fh.close()
5315 # At this point, we should have a recent rotated file which we
5316 # can test for the existence of. However, in practice, on some
5317 # machines which run really slowly, we don't know how far back
Vinay Sajip672c5812011-05-13 07:09:40 +01005318 # in time to go to look for the log file. So, we go back a fair
5319 # bit, and stop as soon as we see a rotated file. In theory this
5320 # could of course still fail, but the chances are lower.
Vinay Sajipfd285022011-05-07 17:01:22 +01005321 found = False
5322 now = datetime.datetime.now()
Vinay Sajip252048c2011-05-13 16:41:57 +01005323 GO_BACK = 5 * 60 # seconds
Vinay Sajip672c5812011-05-13 07:09:40 +01005324 for secs in range(GO_BACK):
5325 prev = now - datetime.timedelta(seconds=secs)
Vinay Sajipfd285022011-05-07 17:01:22 +01005326 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
5327 found = os.path.exists(fn)
5328 if found:
5329 self.rmfiles.append(fn)
5330 break
Vinay Sajip672c5812011-05-13 07:09:40 +01005331 msg = 'No rotated files found, went back %d seconds' % GO_BACK
5332 if not found:
Mike53f7a7c2017-12-14 14:04:53 +03005333 # print additional diagnostics
Vinay Sajipa171f9c2011-05-13 14:45:08 +01005334 dn, fn = os.path.split(self.fn)
5335 files = [f for f in os.listdir(dn) if f.startswith(fn)]
Victor Stinner949c8902011-05-23 01:16:44 +02005336 print('Test time: %s' % now.strftime("%Y-%m-%d %H-%M-%S"), file=sys.stderr)
5337 print('The only matching files are: %s' % files, file=sys.stderr)
Vinay Sajip19b1d502012-02-28 19:02:43 +00005338 for f in files:
5339 print('Contents of %s:' % f)
Vinay Sajipd263d182012-03-03 16:20:37 +00005340 path = os.path.join(dn, f)
5341 with open(path, 'r') as tf:
Vinay Sajip19b1d502012-02-28 19:02:43 +00005342 print(tf.read())
Vinay Sajipfd285022011-05-07 17:01:22 +01005343 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005344
Vinay Sajip0372e102011-05-05 12:59:14 +01005345 def test_invalid(self):
5346 assertRaises = self.assertRaises
5347 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005348 self.fn, 'X', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005349 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005350 self.fn, 'W', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005351 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Inada Naokifa51c0c2021-04-29 11:34:56 +09005352 self.fn, 'W7', encoding="utf-8", delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01005353
Vinay Sajipa7130792013-04-12 17:04:23 +01005354 def test_compute_rollover_daily_attime(self):
5355 currentTime = 0
5356 atTime = datetime.time(12, 0, 0)
5357 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005358 self.fn, encoding="utf-8", when='MIDNIGHT', interval=1, backupCount=0,
5359 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005360 try:
5361 actual = rh.computeRollover(currentTime)
5362 self.assertEqual(actual, currentTime + 12 * 60 * 60)
Vinay Sajipa7130792013-04-12 17:04:23 +01005363
Vinay Sajipd86ac962013-04-14 12:20:46 +01005364 actual = rh.computeRollover(currentTime + 13 * 60 * 60)
5365 self.assertEqual(actual, currentTime + 36 * 60 * 60)
5366 finally:
5367 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005368
Vinay Sajip10e8c492013-05-18 10:19:54 -07005369 #@unittest.skipIf(True, 'Temporarily skipped while failures investigated.')
Vinay Sajipa7130792013-04-12 17:04:23 +01005370 def test_compute_rollover_weekly_attime(self):
Vinay Sajipd86ac962013-04-14 12:20:46 +01005371 currentTime = int(time.time())
5372 today = currentTime - currentTime % 86400
5373
Vinay Sajipa7130792013-04-12 17:04:23 +01005374 atTime = datetime.time(12, 0, 0)
5375
Vinay Sajip10e8c492013-05-18 10:19:54 -07005376 wday = time.gmtime(today).tm_wday
Vinay Sajipa7130792013-04-12 17:04:23 +01005377 for day in range(7):
5378 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005379 self.fn, encoding="utf-8", when='W%d' % day, interval=1, backupCount=0,
5380 utc=True, atTime=atTime)
Vinay Sajipd86ac962013-04-14 12:20:46 +01005381 try:
5382 if wday > day:
5383 # The rollover day has already passed this week, so we
5384 # go over into next week
5385 expected = (7 - wday + day)
5386 else:
5387 expected = (day - wday)
5388 # At this point expected is in days from now, convert to seconds
5389 expected *= 24 * 60 * 60
5390 # Add in the rollover time
5391 expected += 12 * 60 * 60
5392 # Add in adjustment for today
5393 expected += today
Vinay Sajip66940a12013-04-15 11:59:35 +01005394 actual = rh.computeRollover(today)
5395 if actual != expected:
5396 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005397 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005398 self.assertEqual(actual, expected)
5399 if day == wday:
5400 # goes into following week
5401 expected += 7 * 24 * 60 * 60
Vinay Sajip8b4c7192013-04-14 12:25:25 +01005402 actual = rh.computeRollover(today + 13 * 60 * 60)
Vinay Sajip66940a12013-04-15 11:59:35 +01005403 if actual != expected:
5404 print('failed in timezone: %d' % time.timezone)
Vinay Sajipcaf9eb82013-05-12 00:04:58 +01005405 print('local vars: %s' % locals())
Vinay Sajipd86ac962013-04-14 12:20:46 +01005406 self.assertEqual(actual, expected)
5407 finally:
5408 rh.close()
Vinay Sajipa7130792013-04-12 17:04:23 +01005409
5410
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005411def secs(**kw):
5412 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
5413
5414for when, exp in (('S', 1),
5415 ('M', 60),
5416 ('H', 60 * 60),
5417 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00005418 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005419 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00005420 ('W0', secs(days=4, hours=24)),
5421 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005422 def test_compute_rollover(self, when=when, exp=exp):
5423 rh = logging.handlers.TimedRotatingFileHandler(
Inada Naokifa51c0c2021-04-29 11:34:56 +09005424 self.fn, encoding="utf-8", when=when, interval=1, backupCount=0, utc=True)
Vinay Sajiped0473c2011-02-26 15:35:38 +00005425 currentTime = 0.0
5426 actual = rh.computeRollover(currentTime)
5427 if exp != actual:
5428 # Failures occur on some systems for MIDNIGHT and W0.
5429 # Print detailed calculation for MIDNIGHT so we can try to see
5430 # what's going on
Vinay Sajiped0473c2011-02-26 15:35:38 +00005431 if when == 'MIDNIGHT':
5432 try:
5433 if rh.utc:
5434 t = time.gmtime(currentTime)
5435 else:
5436 t = time.localtime(currentTime)
5437 currentHour = t[3]
5438 currentMinute = t[4]
5439 currentSecond = t[5]
5440 # r is the number of seconds left between now and midnight
5441 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
5442 currentMinute) * 60 +
5443 currentSecond)
5444 result = currentTime + r
5445 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
5446 print('currentHour: %s' % currentHour, file=sys.stderr)
5447 print('currentMinute: %s' % currentMinute, file=sys.stderr)
5448 print('currentSecond: %s' % currentSecond, file=sys.stderr)
5449 print('r: %s' % r, file=sys.stderr)
5450 print('result: %s' % result, file=sys.stderr)
5451 except Exception:
5452 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
5453 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00005454 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00005455 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
5456
Vinay Sajip60ccd822011-05-09 17:32:09 +01005457
Vinay Sajip223349c2015-10-01 20:37:54 +01005458@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil/pywintypes required for this test.')
Vinay Sajip60ccd822011-05-09 17:32:09 +01005459class NTEventLogHandlerTest(BaseTest):
5460 def test_basic(self):
5461 logtype = 'Application'
5462 elh = win32evtlog.OpenEventLog(None, logtype)
5463 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
Vinay Sajip223349c2015-10-01 20:37:54 +01005464
5465 try:
5466 h = logging.handlers.NTEventLogHandler('test_logging')
5467 except pywintypes.error as e:
Zachary Ware22226c52015-10-06 15:22:13 -05005468 if e.winerror == 5: # access denied
Vinay Sajip223349c2015-10-01 20:37:54 +01005469 raise unittest.SkipTest('Insufficient privileges to run test')
Zachary Ware106ddf02015-10-06 15:28:43 -05005470 raise
Vinay Sajip223349c2015-10-01 20:37:54 +01005471
Vinay Sajip60ccd822011-05-09 17:32:09 +01005472 r = logging.makeLogRecord({'msg': 'Test Log Message'})
5473 h.handle(r)
5474 h.close()
5475 # Now see if the event is recorded
Giampaolo Rodola'284529d2012-10-17 13:24:27 +02005476 self.assertLess(num_recs, win32evtlog.GetNumberOfEventLogRecords(elh))
Vinay Sajip60ccd822011-05-09 17:32:09 +01005477 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
5478 win32evtlog.EVENTLOG_SEQUENTIAL_READ
5479 found = False
5480 GO_BACK = 100
5481 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
5482 for e in events:
5483 if e.SourceName != 'test_logging':
5484 continue
5485 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
5486 if msg != 'Test Log Message\r\n':
5487 continue
5488 found = True
5489 break
5490 msg = 'Record not found in event log, went back %d records' % GO_BACK
5491 self.assertTrue(found, msg=msg)
5492
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005493
5494class MiscTestCase(unittest.TestCase):
5495 def test__all__(self):
Victor Stinnerfbf43f02020-08-17 07:20:40 +02005496 not_exported = {
5497 'logThreads', 'logMultiprocessing', 'logProcesses', 'currentframe',
5498 'PercentStyle', 'StrFormatStyle', 'StringTemplateStyle',
5499 'Filterer', 'PlaceHolder', 'Manager', 'RootLogger', 'root',
5500 'threading'}
5501 support.check__all__(self, logging, not_exported=not_exported)
Vinay Sajipdb8f4632016-09-08 01:37:03 +01005502
5503
Christian Heimes180510d2008-03-03 19:15:45 +00005504# Set the locale to the platform-dependent default. I have no idea
5505# why the test does this, but in any case we save the current locale
5506# first and restore it at the end.
Serhiy Storchakabedce352021-09-19 22:36:03 +03005507def setUpModule():
5508 cm = support.run_with_locale('LC_ALL', '')
5509 cm.__enter__()
5510 unittest.addModuleCleanup(cm.__exit__, None, None, None)
5511
Jeremy Hylton096d9862003-07-18 03:19:20 +00005512
Christian Heimes180510d2008-03-03 19:15:45 +00005513if __name__ == "__main__":
Serhiy Storchakabedce352021-09-19 22:36:03 +03005514 unittest.main()