blob: bfb240486a540276618a754859b01ea639a63678 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajiped0473c2011-02-26 15:35:38 +00003# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajiped0473c2011-02-26 15:35:38 +000021Copyright (C) 2001-2011 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Christian Heimes180510d2008-03-03 19:15:45 +000036import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000037import select
Christian Heimes180510d2008-03-03 19:15:45 +000038import socket
Alexandre Vassalottice261952008-05-12 02:31:37 +000039from socketserver import ThreadingTCPServer, StreamRequestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010043from test.support import captured_stdout, run_with_locale, run_unittest, patch
Christian Heimes180510d2008-03-03 19:15:45 +000044import textwrap
Christian Heimes180510d2008-03-03 19:15:45 +000045import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000046import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000047import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000048try:
49 import threading
50except ImportError:
51 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000052
53
Christian Heimes180510d2008-03-03 19:15:45 +000054class BaseTest(unittest.TestCase):
55
56 """Base class for logging tests."""
57
58 log_format = "%(name)s -> %(levelname)s: %(message)s"
59 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
60 message_num = 0
61
62 def setUp(self):
63 """Setup the default logging stream to an internal StringIO instance,
64 so that we can examine log output as we want."""
65 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000066 logging._acquireLock()
67 try:
Christian Heimes180510d2008-03-03 19:15:45 +000068 self.saved_handlers = logging._handlers.copy()
69 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000070 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000071 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000072 self.logger_states = logger_states = {}
73 for name in saved_loggers:
74 logger_states[name] = getattr(saved_loggers[name],
75 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000076 finally:
77 logging._releaseLock()
78
Benjamin Peterson22005fc2010-04-11 16:25:06 +000079 # Set two unused loggers: one non-ASCII and one Unicode.
80 # This is to test correct operation when sorting existing
81 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000082 self.logger1 = logging.getLogger("\xab\xd7\xbb")
83 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000084
Christian Heimes180510d2008-03-03 19:15:45 +000085 self.root_logger = logging.getLogger("")
86 self.original_logging_level = self.root_logger.getEffectiveLevel()
87
88 self.stream = io.StringIO()
89 self.root_logger.setLevel(logging.DEBUG)
90 self.root_hdlr = logging.StreamHandler(self.stream)
91 self.root_formatter = logging.Formatter(self.log_format)
92 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000093 if self.logger1.hasHandlers():
94 hlist = self.logger1.handlers + self.root_logger.handlers
95 raise AssertionError('Unexpected handlers: %s' % hlist)
96 if self.logger2.hasHandlers():
97 hlist = self.logger2.handlers + self.root_logger.handlers
98 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +000099 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000100 self.assertTrue(self.logger1.hasHandlers())
101 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000102
103 def tearDown(self):
104 """Remove our logging stream, and restore the original logging
105 level."""
106 self.stream.close()
107 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000108 while self.root_logger.handlers:
109 h = self.root_logger.handlers[0]
110 self.root_logger.removeHandler(h)
111 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000112 self.root_logger.setLevel(self.original_logging_level)
113 logging._acquireLock()
114 try:
115 logging._levelNames.clear()
116 logging._levelNames.update(self.saved_level_names)
117 logging._handlers.clear()
118 logging._handlers.update(self.saved_handlers)
119 logging._handlerList[:] = self.saved_handler_list
120 loggerDict = logging.getLogger().manager.loggerDict
121 loggerDict.clear()
122 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000123 logger_states = self.logger_states
124 for name in self.logger_states:
125 if logger_states[name] is not None:
126 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000127 finally:
128 logging._releaseLock()
129
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000130 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000131 """Match the collected log lines against the regular expression
132 self.expected_log_pat, and compare the extracted group values to
133 the expected_values list of tuples."""
134 stream = stream or self.stream
135 pat = re.compile(self.expected_log_pat)
136 try:
137 stream.reset()
138 actual_lines = stream.readlines()
139 except AttributeError:
140 # StringIO.StringIO lacks a reset() method.
141 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000142 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000143 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000144 for actual, expected in zip(actual_lines, expected_values):
145 match = pat.search(actual)
146 if not match:
147 self.fail("Log line does not match expected pattern:\n" +
148 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000149 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000150 s = stream.read()
151 if s:
152 self.fail("Remaining output at end of log stream:\n" + s)
153
154 def next_message(self):
155 """Generate a message consisting solely of an auto-incrementing
156 integer."""
157 self.message_num += 1
158 return "%d" % self.message_num
159
160
161class BuiltinLevelsTest(BaseTest):
162 """Test builtin levels and their inheritance."""
163
164 def test_flat(self):
165 #Logging levels in a flat logger namespace.
166 m = self.next_message
167
168 ERR = logging.getLogger("ERR")
169 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000170 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000171 INF.setLevel(logging.INFO)
172 DEB = logging.getLogger("DEB")
173 DEB.setLevel(logging.DEBUG)
174
175 # These should log.
176 ERR.log(logging.CRITICAL, m())
177 ERR.error(m())
178
179 INF.log(logging.CRITICAL, m())
180 INF.error(m())
181 INF.warn(m())
182 INF.info(m())
183
184 DEB.log(logging.CRITICAL, m())
185 DEB.error(m())
186 DEB.warn (m())
187 DEB.info (m())
188 DEB.debug(m())
189
190 # These should not log.
191 ERR.warn(m())
192 ERR.info(m())
193 ERR.debug(m())
194
195 INF.debug(m())
196
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000197 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000198 ('ERR', 'CRITICAL', '1'),
199 ('ERR', 'ERROR', '2'),
200 ('INF', 'CRITICAL', '3'),
201 ('INF', 'ERROR', '4'),
202 ('INF', 'WARNING', '5'),
203 ('INF', 'INFO', '6'),
204 ('DEB', 'CRITICAL', '7'),
205 ('DEB', 'ERROR', '8'),
206 ('DEB', 'WARNING', '9'),
207 ('DEB', 'INFO', '10'),
208 ('DEB', 'DEBUG', '11'),
209 ])
210
211 def test_nested_explicit(self):
212 # Logging levels in a nested namespace, all explicitly set.
213 m = self.next_message
214
215 INF = logging.getLogger("INF")
216 INF.setLevel(logging.INFO)
217 INF_ERR = logging.getLogger("INF.ERR")
218 INF_ERR.setLevel(logging.ERROR)
219
220 # These should log.
221 INF_ERR.log(logging.CRITICAL, m())
222 INF_ERR.error(m())
223
224 # These should not log.
225 INF_ERR.warn(m())
226 INF_ERR.info(m())
227 INF_ERR.debug(m())
228
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000229 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000230 ('INF.ERR', 'CRITICAL', '1'),
231 ('INF.ERR', 'ERROR', '2'),
232 ])
233
234 def test_nested_inherited(self):
235 #Logging levels in a nested namespace, inherited from parent loggers.
236 m = self.next_message
237
238 INF = logging.getLogger("INF")
239 INF.setLevel(logging.INFO)
240 INF_ERR = logging.getLogger("INF.ERR")
241 INF_ERR.setLevel(logging.ERROR)
242 INF_UNDEF = logging.getLogger("INF.UNDEF")
243 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
244 UNDEF = logging.getLogger("UNDEF")
245
246 # These should log.
247 INF_UNDEF.log(logging.CRITICAL, m())
248 INF_UNDEF.error(m())
249 INF_UNDEF.warn(m())
250 INF_UNDEF.info(m())
251 INF_ERR_UNDEF.log(logging.CRITICAL, m())
252 INF_ERR_UNDEF.error(m())
253
254 # These should not log.
255 INF_UNDEF.debug(m())
256 INF_ERR_UNDEF.warn(m())
257 INF_ERR_UNDEF.info(m())
258 INF_ERR_UNDEF.debug(m())
259
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000260 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000261 ('INF.UNDEF', 'CRITICAL', '1'),
262 ('INF.UNDEF', 'ERROR', '2'),
263 ('INF.UNDEF', 'WARNING', '3'),
264 ('INF.UNDEF', 'INFO', '4'),
265 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
266 ('INF.ERR.UNDEF', 'ERROR', '6'),
267 ])
268
269 def test_nested_with_virtual_parent(self):
270 # Logging levels when some parent does not exist yet.
271 m = self.next_message
272
273 INF = logging.getLogger("INF")
274 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
275 CHILD = logging.getLogger("INF.BADPARENT")
276 INF.setLevel(logging.INFO)
277
278 # These should log.
279 GRANDCHILD.log(logging.FATAL, m())
280 GRANDCHILD.info(m())
281 CHILD.log(logging.FATAL, m())
282 CHILD.info(m())
283
284 # These should not log.
285 GRANDCHILD.debug(m())
286 CHILD.debug(m())
287
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000288 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000289 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
290 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
291 ('INF.BADPARENT', 'CRITICAL', '3'),
292 ('INF.BADPARENT', 'INFO', '4'),
293 ])
294
295
296class BasicFilterTest(BaseTest):
297
298 """Test the bundled Filter class."""
299
300 def test_filter(self):
301 # Only messages satisfying the specified criteria pass through the
302 # filter.
303 filter_ = logging.Filter("spam.eggs")
304 handler = self.root_logger.handlers[0]
305 try:
306 handler.addFilter(filter_)
307 spam = logging.getLogger("spam")
308 spam_eggs = logging.getLogger("spam.eggs")
309 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
310 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
311
312 spam.info(self.next_message())
313 spam_eggs.info(self.next_message()) # Good.
314 spam_eggs_fish.info(self.next_message()) # Good.
315 spam_bakedbeans.info(self.next_message())
316
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000317 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000318 ('spam.eggs', 'INFO', '2'),
319 ('spam.eggs.fish', 'INFO', '3'),
320 ])
321 finally:
322 handler.removeFilter(filter_)
323
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000324 def test_callable_filter(self):
325 # Only messages satisfying the specified criteria pass through the
326 # filter.
327
328 def filterfunc(record):
329 parts = record.name.split('.')
330 prefix = '.'.join(parts[:2])
331 return prefix == 'spam.eggs'
332
333 handler = self.root_logger.handlers[0]
334 try:
335 handler.addFilter(filterfunc)
336 spam = logging.getLogger("spam")
337 spam_eggs = logging.getLogger("spam.eggs")
338 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
339 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
340
341 spam.info(self.next_message())
342 spam_eggs.info(self.next_message()) # Good.
343 spam_eggs_fish.info(self.next_message()) # Good.
344 spam_bakedbeans.info(self.next_message())
345
346 self.assert_log_lines([
347 ('spam.eggs', 'INFO', '2'),
348 ('spam.eggs.fish', 'INFO', '3'),
349 ])
350 finally:
351 handler.removeFilter(filterfunc)
352
Christian Heimes180510d2008-03-03 19:15:45 +0000353
354#
355# First, we define our levels. There can be as many as you want - the only
356# limitations are that they should be integers, the lowest should be > 0 and
357# larger values mean less information being logged. If you need specific
358# level values which do not fit into these limitations, you can use a
359# mapping dictionary to convert between your application levels and the
360# logging system.
361#
362SILENT = 120
363TACITURN = 119
364TERSE = 118
365EFFUSIVE = 117
366SOCIABLE = 116
367VERBOSE = 115
368TALKATIVE = 114
369GARRULOUS = 113
370CHATTERBOX = 112
371BORING = 111
372
373LEVEL_RANGE = range(BORING, SILENT + 1)
374
375#
376# Next, we define names for our levels. You don't need to do this - in which
377# case the system will use "Level n" to denote the text for the level.
378#
379my_logging_levels = {
380 SILENT : 'Silent',
381 TACITURN : 'Taciturn',
382 TERSE : 'Terse',
383 EFFUSIVE : 'Effusive',
384 SOCIABLE : 'Sociable',
385 VERBOSE : 'Verbose',
386 TALKATIVE : 'Talkative',
387 GARRULOUS : 'Garrulous',
388 CHATTERBOX : 'Chatterbox',
389 BORING : 'Boring',
390}
391
392class GarrulousFilter(logging.Filter):
393
394 """A filter which blocks garrulous messages."""
395
396 def filter(self, record):
397 return record.levelno != GARRULOUS
398
399class VerySpecificFilter(logging.Filter):
400
401 """A filter which blocks sociable and taciturn messages."""
402
403 def filter(self, record):
404 return record.levelno not in [SOCIABLE, TACITURN]
405
406
407class CustomLevelsAndFiltersTest(BaseTest):
408
409 """Test various filtering possibilities with custom logging levels."""
410
411 # Skip the logger name group.
412 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
413
414 def setUp(self):
415 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000416 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000417 logging.addLevelName(k, v)
418
419 def log_at_all_levels(self, logger):
420 for lvl in LEVEL_RANGE:
421 logger.log(lvl, self.next_message())
422
423 def test_logger_filter(self):
424 # Filter at logger level.
425 self.root_logger.setLevel(VERBOSE)
426 # Levels >= 'Verbose' are good.
427 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000428 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000429 ('Verbose', '5'),
430 ('Sociable', '6'),
431 ('Effusive', '7'),
432 ('Terse', '8'),
433 ('Taciturn', '9'),
434 ('Silent', '10'),
435 ])
436
437 def test_handler_filter(self):
438 # Filter at handler level.
439 self.root_logger.handlers[0].setLevel(SOCIABLE)
440 try:
441 # Levels >= 'Sociable' are good.
442 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000443 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000444 ('Sociable', '6'),
445 ('Effusive', '7'),
446 ('Terse', '8'),
447 ('Taciturn', '9'),
448 ('Silent', '10'),
449 ])
450 finally:
451 self.root_logger.handlers[0].setLevel(logging.NOTSET)
452
453 def test_specific_filters(self):
454 # Set a specific filter object on the handler, and then add another
455 # filter object on the logger itself.
456 handler = self.root_logger.handlers[0]
457 specific_filter = None
458 garr = GarrulousFilter()
459 handler.addFilter(garr)
460 try:
461 self.log_at_all_levels(self.root_logger)
462 first_lines = [
463 # Notice how 'Garrulous' is missing
464 ('Boring', '1'),
465 ('Chatterbox', '2'),
466 ('Talkative', '4'),
467 ('Verbose', '5'),
468 ('Sociable', '6'),
469 ('Effusive', '7'),
470 ('Terse', '8'),
471 ('Taciturn', '9'),
472 ('Silent', '10'),
473 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000474 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000475
476 specific_filter = VerySpecificFilter()
477 self.root_logger.addFilter(specific_filter)
478 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000479 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000480 # Not only 'Garrulous' is still missing, but also 'Sociable'
481 # and 'Taciturn'
482 ('Boring', '11'),
483 ('Chatterbox', '12'),
484 ('Talkative', '14'),
485 ('Verbose', '15'),
486 ('Effusive', '17'),
487 ('Terse', '18'),
488 ('Silent', '20'),
489 ])
490 finally:
491 if specific_filter:
492 self.root_logger.removeFilter(specific_filter)
493 handler.removeFilter(garr)
494
495
496class MemoryHandlerTest(BaseTest):
497
498 """Tests for the MemoryHandler."""
499
500 # Do not bother with a logger name group.
501 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
502
503 def setUp(self):
504 BaseTest.setUp(self)
505 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
506 self.root_hdlr)
507 self.mem_logger = logging.getLogger('mem')
508 self.mem_logger.propagate = 0
509 self.mem_logger.addHandler(self.mem_hdlr)
510
511 def tearDown(self):
512 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000513 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000514
515 def test_flush(self):
516 # The memory handler flushes to its target handler based on specific
517 # criteria (message count and message level).
518 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000519 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000520 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000521 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000522 # This will flush because the level is >= logging.WARNING
523 self.mem_logger.warn(self.next_message())
524 lines = [
525 ('DEBUG', '1'),
526 ('INFO', '2'),
527 ('WARNING', '3'),
528 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000529 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000530 for n in (4, 14):
531 for i in range(9):
532 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000533 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000534 # This will flush because it's the 10th message since the last
535 # flush.
536 self.mem_logger.debug(self.next_message())
537 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000538 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000539
540 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000541 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000542
543
544class ExceptionFormatter(logging.Formatter):
545 """A special exception formatter."""
546 def formatException(self, ei):
547 return "Got a [%s]" % ei[0].__name__
548
549
550class ConfigFileTest(BaseTest):
551
552 """Reading logging config from a .ini-style config file."""
553
554 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
555
556 # config0 is a standard configuration.
557 config0 = """
558 [loggers]
559 keys=root
560
561 [handlers]
562 keys=hand1
563
564 [formatters]
565 keys=form1
566
567 [logger_root]
568 level=WARNING
569 handlers=hand1
570
571 [handler_hand1]
572 class=StreamHandler
573 level=NOTSET
574 formatter=form1
575 args=(sys.stdout,)
576
577 [formatter_form1]
578 format=%(levelname)s ++ %(message)s
579 datefmt=
580 """
581
582 # config1 adds a little to the standard configuration.
583 config1 = """
584 [loggers]
585 keys=root,parser
586
587 [handlers]
588 keys=hand1
589
590 [formatters]
591 keys=form1
592
593 [logger_root]
594 level=WARNING
595 handlers=
596
597 [logger_parser]
598 level=DEBUG
599 handlers=hand1
600 propagate=1
601 qualname=compiler.parser
602
603 [handler_hand1]
604 class=StreamHandler
605 level=NOTSET
606 formatter=form1
607 args=(sys.stdout,)
608
609 [formatter_form1]
610 format=%(levelname)s ++ %(message)s
611 datefmt=
612 """
613
Vinay Sajip3f84b072011-03-07 17:49:33 +0000614 # config1a moves the handler to the root.
615 config1a = """
616 [loggers]
617 keys=root,parser
618
619 [handlers]
620 keys=hand1
621
622 [formatters]
623 keys=form1
624
625 [logger_root]
626 level=WARNING
627 handlers=hand1
628
629 [logger_parser]
630 level=DEBUG
631 handlers=
632 propagate=1
633 qualname=compiler.parser
634
635 [handler_hand1]
636 class=StreamHandler
637 level=NOTSET
638 formatter=form1
639 args=(sys.stdout,)
640
641 [formatter_form1]
642 format=%(levelname)s ++ %(message)s
643 datefmt=
644 """
645
Christian Heimes180510d2008-03-03 19:15:45 +0000646 # config2 has a subtle configuration error that should be reported
647 config2 = config1.replace("sys.stdout", "sys.stbout")
648
649 # config3 has a less subtle configuration error
650 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
651
652 # config4 specifies a custom formatter class to be loaded
653 config4 = """
654 [loggers]
655 keys=root
656
657 [handlers]
658 keys=hand1
659
660 [formatters]
661 keys=form1
662
663 [logger_root]
664 level=NOTSET
665 handlers=hand1
666
667 [handler_hand1]
668 class=StreamHandler
669 level=NOTSET
670 formatter=form1
671 args=(sys.stdout,)
672
673 [formatter_form1]
674 class=""" + __name__ + """.ExceptionFormatter
675 format=%(levelname)s:%(name)s:%(message)s
676 datefmt=
677 """
678
Georg Brandl3dbca812008-07-23 16:10:53 +0000679 # config5 specifies a custom handler class to be loaded
680 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
681
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000682 # config6 uses ', ' delimiters in the handlers and formatters sections
683 config6 = """
684 [loggers]
685 keys=root,parser
686
687 [handlers]
688 keys=hand1, hand2
689
690 [formatters]
691 keys=form1, form2
692
693 [logger_root]
694 level=WARNING
695 handlers=
696
697 [logger_parser]
698 level=DEBUG
699 handlers=hand1
700 propagate=1
701 qualname=compiler.parser
702
703 [handler_hand1]
704 class=StreamHandler
705 level=NOTSET
706 formatter=form1
707 args=(sys.stdout,)
708
709 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000710 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000711 level=NOTSET
712 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000713 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000714
715 [formatter_form1]
716 format=%(levelname)s ++ %(message)s
717 datefmt=
718
719 [formatter_form2]
720 format=%(message)s
721 datefmt=
722 """
723
Vinay Sajip3f84b072011-03-07 17:49:33 +0000724 # config7 adds a compiler logger.
725 config7 = """
726 [loggers]
727 keys=root,parser,compiler
728
729 [handlers]
730 keys=hand1
731
732 [formatters]
733 keys=form1
734
735 [logger_root]
736 level=WARNING
737 handlers=hand1
738
739 [logger_compiler]
740 level=DEBUG
741 handlers=
742 propagate=1
743 qualname=compiler
744
745 [logger_parser]
746 level=DEBUG
747 handlers=
748 propagate=1
749 qualname=compiler.parser
750
751 [handler_hand1]
752 class=StreamHandler
753 level=NOTSET
754 formatter=form1
755 args=(sys.stdout,)
756
757 [formatter_form1]
758 format=%(levelname)s ++ %(message)s
759 datefmt=
760 """
761
Christian Heimes180510d2008-03-03 19:15:45 +0000762 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000763 file = io.StringIO(textwrap.dedent(conf))
764 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000765
766 def test_config0_ok(self):
767 # A simple config file which overrides the default settings.
768 with captured_stdout() as output:
769 self.apply_config(self.config0)
770 logger = logging.getLogger()
771 # Won't output anything
772 logger.info(self.next_message())
773 # Outputs a message
774 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000775 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000776 ('ERROR', '2'),
777 ], stream=output)
778 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000779 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000780
Georg Brandl3dbca812008-07-23 16:10:53 +0000781 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000782 # A config file defining a sub-parser as well.
783 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000784 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000785 logger = logging.getLogger("compiler.parser")
786 # Both will output a message
787 logger.info(self.next_message())
788 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000789 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000790 ('INFO', '1'),
791 ('ERROR', '2'),
792 ], stream=output)
793 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000794 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000795
796 def test_config2_failure(self):
797 # A simple config file which overrides the default settings.
798 self.assertRaises(Exception, self.apply_config, self.config2)
799
800 def test_config3_failure(self):
801 # A simple config file which overrides the default settings.
802 self.assertRaises(Exception, self.apply_config, self.config3)
803
804 def test_config4_ok(self):
805 # A config file specifying a custom formatter class.
806 with captured_stdout() as output:
807 self.apply_config(self.config4)
808 logger = logging.getLogger()
809 try:
810 raise RuntimeError()
811 except RuntimeError:
812 logging.exception("just testing")
813 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000814 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000815 "ERROR:root:just testing\nGot a [RuntimeError]\n")
816 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000817 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000818
Georg Brandl3dbca812008-07-23 16:10:53 +0000819 def test_config5_ok(self):
820 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000821
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000822 def test_config6_ok(self):
823 self.test_config1_ok(config=self.config6)
824
Vinay Sajip3f84b072011-03-07 17:49:33 +0000825 def test_config7_ok(self):
826 with captured_stdout() as output:
827 self.apply_config(self.config1a)
828 logger = logging.getLogger("compiler.parser")
829 # See issue #11424. compiler-hyphenated sorts
830 # between compiler and compiler.xyz and this
831 # was preventing compiler.xyz from being included
832 # in the child loggers of compiler because of an
833 # overzealous loop termination condition.
834 hyphenated = logging.getLogger('compiler-hyphenated')
835 # All will output a message
836 logger.info(self.next_message())
837 logger.error(self.next_message())
838 hyphenated.critical(self.next_message())
839 self.assert_log_lines([
840 ('INFO', '1'),
841 ('ERROR', '2'),
842 ('CRITICAL', '3'),
843 ], stream=output)
844 # Original logger output is empty.
845 self.assert_log_lines([])
846 with captured_stdout() as output:
847 self.apply_config(self.config7)
848 logger = logging.getLogger("compiler.parser")
849 self.assertFalse(logger.disabled)
850 # Both will output a message
851 logger.info(self.next_message())
852 logger.error(self.next_message())
853 logger = logging.getLogger("compiler.lexer")
854 # Both will output a message
855 logger.info(self.next_message())
856 logger.error(self.next_message())
857 # Will not appear
858 hyphenated.critical(self.next_message())
859 self.assert_log_lines([
860 ('INFO', '4'),
861 ('ERROR', '5'),
862 ('INFO', '6'),
863 ('ERROR', '7'),
864 ], stream=output)
865 # Original logger output is empty.
866 self.assert_log_lines([])
867
Christian Heimes180510d2008-03-03 19:15:45 +0000868class LogRecordStreamHandler(StreamRequestHandler):
869
870 """Handler for a streaming logging request. It saves the log message in the
871 TCP server's 'log_output' attribute."""
872
873 TCP_LOG_END = "!!!END!!!"
874
875 def handle(self):
876 """Handle multiple requests - each expected to be of 4-byte length,
877 followed by the LogRecord in pickle format. Logs the record
878 according to whatever policy is configured locally."""
879 while True:
880 chunk = self.connection.recv(4)
881 if len(chunk) < 4:
882 break
883 slen = struct.unpack(">L", chunk)[0]
884 chunk = self.connection.recv(slen)
885 while len(chunk) < slen:
886 chunk = chunk + self.connection.recv(slen - len(chunk))
887 obj = self.unpickle(chunk)
888 record = logging.makeLogRecord(obj)
889 self.handle_log_record(record)
890
891 def unpickle(self, data):
892 return pickle.loads(data)
893
894 def handle_log_record(self, record):
895 # If the end-of-messages sentinel is seen, tell the server to
896 # terminate.
897 if self.TCP_LOG_END in record.msg:
898 self.server.abort = 1
899 return
900 self.server.log_output += record.msg + "\n"
901
Guido van Rossum376e6362003-04-25 14:22:00 +0000902
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000903class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000904
905 """A simple-minded TCP socket-based logging receiver suitable for test
906 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000907
908 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000909 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000910
911 def __init__(self, host='localhost',
912 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
913 handler=LogRecordStreamHandler):
914 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +0000915 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +0000916 self.timeout = 0.1
917 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000918
919 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000920 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000921 rd, wr, ex = select.select([self.socket.fileno()], [], [],
922 self.timeout)
923 if rd:
924 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +0000925 # Notify the main thread that we're about to exit
926 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000927 # close the listen socket
928 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000929
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000930
Victor Stinner45df8202010-04-28 22:31:17 +0000931@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +0000932class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000933
Christian Heimes180510d2008-03-03 19:15:45 +0000934 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000935
Christian Heimes180510d2008-03-03 19:15:45 +0000936 def setUp(self):
937 """Set up a TCP server to receive log messages, and a SocketHandler
938 pointing to that server's address and port."""
939 BaseTest.setUp(self)
940 self.tcpserver = LogRecordSocketReceiver(port=0)
941 self.port = self.tcpserver.socket.getsockname()[1]
942 self.threads = [
943 threading.Thread(target=self.tcpserver.serve_until_stopped)]
944 for thread in self.threads:
945 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000946
Christian Heimes180510d2008-03-03 19:15:45 +0000947 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
948 self.sock_hdlr.setFormatter(self.root_formatter)
949 self.root_logger.removeHandler(self.root_logger.handlers[0])
950 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000951
Christian Heimes180510d2008-03-03 19:15:45 +0000952 def tearDown(self):
953 """Shutdown the TCP server."""
954 try:
955 self.tcpserver.abort = True
956 del self.tcpserver
957 self.root_logger.removeHandler(self.sock_hdlr)
958 self.sock_hdlr.close()
959 for thread in self.threads:
960 thread.join(2.0)
961 finally:
962 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000963
Christian Heimes180510d2008-03-03 19:15:45 +0000964 def get_output(self):
965 """Get the log output as received by the TCP server."""
966 # Signal the TCP receiver and wait for it to terminate.
967 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
968 self.tcpserver.finished.wait(2.0)
969 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000970
Christian Heimes180510d2008-03-03 19:15:45 +0000971 def test_output(self):
972 # The log message sent to the SocketHandler is properly received.
973 logger = logging.getLogger("tcp")
974 logger.error("spam")
975 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000976 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000977
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000978
Christian Heimes180510d2008-03-03 19:15:45 +0000979class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000980
Christian Heimes180510d2008-03-03 19:15:45 +0000981 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000982
Christian Heimes180510d2008-03-03 19:15:45 +0000983 def setUp(self):
984 """Create a dict to remember potentially destroyed objects."""
985 BaseTest.setUp(self)
986 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000987
Christian Heimes180510d2008-03-03 19:15:45 +0000988 def _watch_for_survival(self, *args):
989 """Watch the given objects for survival, by creating weakrefs to
990 them."""
991 for obj in args:
992 key = id(obj), repr(obj)
993 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000994
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000995 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +0000996 """Assert that all objects watched for survival have survived."""
997 # Trigger cycle breaking.
998 gc.collect()
999 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001000 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001001 if ref() is None:
1002 dead.append(repr_)
1003 if dead:
1004 self.fail("%d objects should have survived "
1005 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001006
Christian Heimes180510d2008-03-03 19:15:45 +00001007 def test_persistent_loggers(self):
1008 # Logger objects are persistent and retain their configuration, even
1009 # if visible references are destroyed.
1010 self.root_logger.setLevel(logging.INFO)
1011 foo = logging.getLogger("foo")
1012 self._watch_for_survival(foo)
1013 foo.setLevel(logging.DEBUG)
1014 self.root_logger.debug(self.next_message())
1015 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001016 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001017 ('foo', 'DEBUG', '2'),
1018 ])
1019 del foo
1020 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001021 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001022 # foo has retained its settings.
1023 bar = logging.getLogger("foo")
1024 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001025 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001026 ('foo', 'DEBUG', '2'),
1027 ('foo', 'DEBUG', '3'),
1028 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001029
Benjamin Petersonf91df042009-02-13 02:50:59 +00001030
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001031class EncodingTest(BaseTest):
1032 def test_encoding_plain_file(self):
1033 # In Python 2.x, a plain file object is treated as having no encoding.
1034 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001035 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1036 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001037 # the non-ascii data we write to the log.
1038 data = "foo\x80"
1039 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001040 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001041 log.addHandler(handler)
1042 try:
1043 # write non-ascii data to the log.
1044 log.warning(data)
1045 finally:
1046 log.removeHandler(handler)
1047 handler.close()
1048 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001049 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001050 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001051 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001052 finally:
1053 f.close()
1054 finally:
1055 if os.path.isfile(fn):
1056 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001057
Benjamin Petersonf91df042009-02-13 02:50:59 +00001058 def test_encoding_cyrillic_unicode(self):
1059 log = logging.getLogger("test")
1060 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1061 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1062 #Ensure it's written in a Cyrillic encoding
1063 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001064 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001065 stream = io.BytesIO()
1066 writer = writer_class(stream, 'strict')
1067 handler = logging.StreamHandler(writer)
1068 log.addHandler(handler)
1069 try:
1070 log.warning(message)
1071 finally:
1072 log.removeHandler(handler)
1073 handler.close()
1074 # check we wrote exactly those bytes, ignoring trailing \n etc
1075 s = stream.getvalue()
1076 #Compare against what the data should be when encoded in CP-1251
1077 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1078
1079
Georg Brandlf9734072008-12-07 15:30:06 +00001080class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001081
Georg Brandlf9734072008-12-07 15:30:06 +00001082 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001083 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001084 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001085 self.addCleanup(lambda: logging.captureWarnings(False))
1086 warnings.filterwarnings("always", category=UserWarning)
1087 stream = io.StringIO()
1088 h = logging.StreamHandler(stream)
1089 logger = logging.getLogger("py.warnings")
1090 logger.addHandler(h)
1091 warnings.warn("I'm warning you...")
1092 logger.removeHandler(h)
1093 s = stream.getvalue()
1094 h.close()
1095 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001096
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001097 #See if an explicit file uses the original implementation
1098 a_file = io.StringIO()
1099 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1100 a_file, "Dummy line")
1101 s = a_file.getvalue()
1102 a_file.close()
1103 self.assertEqual(s,
1104 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1105
1106 def test_warnings_no_handlers(self):
1107 with warnings.catch_warnings():
1108 logging.captureWarnings(True)
1109 self.addCleanup(lambda: logging.captureWarnings(False))
1110
1111 # confirm our assumption: no loggers are set
1112 logger = logging.getLogger("py.warnings")
1113 assert logger.handlers == []
1114
1115 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1116 self.assertTrue(len(logger.handlers) == 1)
1117 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001118
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001119
1120def formatFunc(format, datefmt=None):
1121 return logging.Formatter(format, datefmt)
1122
1123def handlerFunc():
1124 return logging.StreamHandler()
1125
1126class CustomHandler(logging.StreamHandler):
1127 pass
1128
1129class ConfigDictTest(BaseTest):
1130
1131 """Reading logging config from a dictionary."""
1132
1133 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1134
1135 # config0 is a standard configuration.
1136 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001137 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001138 'formatters': {
1139 'form1' : {
1140 'format' : '%(levelname)s ++ %(message)s',
1141 },
1142 },
1143 'handlers' : {
1144 'hand1' : {
1145 'class' : 'logging.StreamHandler',
1146 'formatter' : 'form1',
1147 'level' : 'NOTSET',
1148 'stream' : 'ext://sys.stdout',
1149 },
1150 },
1151 'root' : {
1152 'level' : 'WARNING',
1153 'handlers' : ['hand1'],
1154 },
1155 }
1156
1157 # config1 adds a little to the standard configuration.
1158 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001159 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001160 'formatters': {
1161 'form1' : {
1162 'format' : '%(levelname)s ++ %(message)s',
1163 },
1164 },
1165 'handlers' : {
1166 'hand1' : {
1167 'class' : 'logging.StreamHandler',
1168 'formatter' : 'form1',
1169 'level' : 'NOTSET',
1170 'stream' : 'ext://sys.stdout',
1171 },
1172 },
1173 'loggers' : {
1174 'compiler.parser' : {
1175 'level' : 'DEBUG',
1176 'handlers' : ['hand1'],
1177 },
1178 },
1179 'root' : {
1180 'level' : 'WARNING',
1181 },
1182 }
1183
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001184 # config1a moves the handler to the root. Used with config8a
1185 config1a = {
1186 'version': 1,
1187 'formatters': {
1188 'form1' : {
1189 'format' : '%(levelname)s ++ %(message)s',
1190 },
1191 },
1192 'handlers' : {
1193 'hand1' : {
1194 'class' : 'logging.StreamHandler',
1195 'formatter' : 'form1',
1196 'level' : 'NOTSET',
1197 'stream' : 'ext://sys.stdout',
1198 },
1199 },
1200 'loggers' : {
1201 'compiler.parser' : {
1202 'level' : 'DEBUG',
1203 },
1204 },
1205 'root' : {
1206 'level' : 'WARNING',
1207 'handlers' : ['hand1'],
1208 },
1209 }
1210
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001211 # config2 has a subtle configuration error that should be reported
1212 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001213 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001214 'formatters': {
1215 'form1' : {
1216 'format' : '%(levelname)s ++ %(message)s',
1217 },
1218 },
1219 'handlers' : {
1220 'hand1' : {
1221 'class' : 'logging.StreamHandler',
1222 'formatter' : 'form1',
1223 'level' : 'NOTSET',
1224 'stream' : 'ext://sys.stdbout',
1225 },
1226 },
1227 'loggers' : {
1228 'compiler.parser' : {
1229 'level' : 'DEBUG',
1230 'handlers' : ['hand1'],
1231 },
1232 },
1233 'root' : {
1234 'level' : 'WARNING',
1235 },
1236 }
1237
1238 #As config1 but with a misspelt level on a handler
1239 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001240 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001241 'formatters': {
1242 'form1' : {
1243 'format' : '%(levelname)s ++ %(message)s',
1244 },
1245 },
1246 'handlers' : {
1247 'hand1' : {
1248 'class' : 'logging.StreamHandler',
1249 'formatter' : 'form1',
1250 'level' : 'NTOSET',
1251 'stream' : 'ext://sys.stdout',
1252 },
1253 },
1254 'loggers' : {
1255 'compiler.parser' : {
1256 'level' : 'DEBUG',
1257 'handlers' : ['hand1'],
1258 },
1259 },
1260 'root' : {
1261 'level' : 'WARNING',
1262 },
1263 }
1264
1265
1266 #As config1 but with a misspelt level on a logger
1267 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001268 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001269 'formatters': {
1270 'form1' : {
1271 'format' : '%(levelname)s ++ %(message)s',
1272 },
1273 },
1274 'handlers' : {
1275 'hand1' : {
1276 'class' : 'logging.StreamHandler',
1277 'formatter' : 'form1',
1278 'level' : 'NOTSET',
1279 'stream' : 'ext://sys.stdout',
1280 },
1281 },
1282 'loggers' : {
1283 'compiler.parser' : {
1284 'level' : 'DEBUG',
1285 'handlers' : ['hand1'],
1286 },
1287 },
1288 'root' : {
1289 'level' : 'WRANING',
1290 },
1291 }
1292
1293 # config3 has a less subtle configuration error
1294 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001295 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001296 'formatters': {
1297 'form1' : {
1298 'format' : '%(levelname)s ++ %(message)s',
1299 },
1300 },
1301 'handlers' : {
1302 'hand1' : {
1303 'class' : 'logging.StreamHandler',
1304 'formatter' : 'misspelled_name',
1305 'level' : 'NOTSET',
1306 'stream' : 'ext://sys.stdout',
1307 },
1308 },
1309 'loggers' : {
1310 'compiler.parser' : {
1311 'level' : 'DEBUG',
1312 'handlers' : ['hand1'],
1313 },
1314 },
1315 'root' : {
1316 'level' : 'WARNING',
1317 },
1318 }
1319
1320 # config4 specifies a custom formatter class to be loaded
1321 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001322 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001323 'formatters': {
1324 'form1' : {
1325 '()' : __name__ + '.ExceptionFormatter',
1326 'format' : '%(levelname)s:%(name)s:%(message)s',
1327 },
1328 },
1329 'handlers' : {
1330 'hand1' : {
1331 'class' : 'logging.StreamHandler',
1332 'formatter' : 'form1',
1333 'level' : 'NOTSET',
1334 'stream' : 'ext://sys.stdout',
1335 },
1336 },
1337 'root' : {
1338 'level' : 'NOTSET',
1339 'handlers' : ['hand1'],
1340 },
1341 }
1342
1343 # As config4 but using an actual callable rather than a string
1344 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001345 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001346 'formatters': {
1347 'form1' : {
1348 '()' : ExceptionFormatter,
1349 'format' : '%(levelname)s:%(name)s:%(message)s',
1350 },
1351 'form2' : {
1352 '()' : __name__ + '.formatFunc',
1353 'format' : '%(levelname)s:%(name)s:%(message)s',
1354 },
1355 'form3' : {
1356 '()' : formatFunc,
1357 'format' : '%(levelname)s:%(name)s:%(message)s',
1358 },
1359 },
1360 'handlers' : {
1361 'hand1' : {
1362 'class' : 'logging.StreamHandler',
1363 'formatter' : 'form1',
1364 'level' : 'NOTSET',
1365 'stream' : 'ext://sys.stdout',
1366 },
1367 'hand2' : {
1368 '()' : handlerFunc,
1369 },
1370 },
1371 'root' : {
1372 'level' : 'NOTSET',
1373 'handlers' : ['hand1'],
1374 },
1375 }
1376
1377 # config5 specifies a custom handler class to be loaded
1378 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001379 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001380 'formatters': {
1381 'form1' : {
1382 'format' : '%(levelname)s ++ %(message)s',
1383 },
1384 },
1385 'handlers' : {
1386 'hand1' : {
1387 'class' : __name__ + '.CustomHandler',
1388 'formatter' : 'form1',
1389 'level' : 'NOTSET',
1390 'stream' : 'ext://sys.stdout',
1391 },
1392 },
1393 'loggers' : {
1394 'compiler.parser' : {
1395 'level' : 'DEBUG',
1396 'handlers' : ['hand1'],
1397 },
1398 },
1399 'root' : {
1400 'level' : 'WARNING',
1401 },
1402 }
1403
1404 # config6 specifies a custom handler class to be loaded
1405 # but has bad arguments
1406 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001407 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001408 'formatters': {
1409 'form1' : {
1410 'format' : '%(levelname)s ++ %(message)s',
1411 },
1412 },
1413 'handlers' : {
1414 'hand1' : {
1415 'class' : __name__ + '.CustomHandler',
1416 'formatter' : 'form1',
1417 'level' : 'NOTSET',
1418 'stream' : 'ext://sys.stdout',
1419 '9' : 'invalid parameter name',
1420 },
1421 },
1422 'loggers' : {
1423 'compiler.parser' : {
1424 'level' : 'DEBUG',
1425 'handlers' : ['hand1'],
1426 },
1427 },
1428 'root' : {
1429 'level' : 'WARNING',
1430 },
1431 }
1432
1433 #config 7 does not define compiler.parser but defines compiler.lexer
1434 #so compiler.parser should be disabled after applying it
1435 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001436 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001437 'formatters': {
1438 'form1' : {
1439 'format' : '%(levelname)s ++ %(message)s',
1440 },
1441 },
1442 'handlers' : {
1443 'hand1' : {
1444 'class' : 'logging.StreamHandler',
1445 'formatter' : 'form1',
1446 'level' : 'NOTSET',
1447 'stream' : 'ext://sys.stdout',
1448 },
1449 },
1450 'loggers' : {
1451 'compiler.lexer' : {
1452 'level' : 'DEBUG',
1453 'handlers' : ['hand1'],
1454 },
1455 },
1456 'root' : {
1457 'level' : 'WARNING',
1458 },
1459 }
1460
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001461 # config8 defines both compiler and compiler.lexer
1462 # so compiler.parser should not be disabled (since
1463 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001464 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001465 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001466 'disable_existing_loggers' : False,
1467 'formatters': {
1468 'form1' : {
1469 'format' : '%(levelname)s ++ %(message)s',
1470 },
1471 },
1472 'handlers' : {
1473 'hand1' : {
1474 'class' : 'logging.StreamHandler',
1475 'formatter' : 'form1',
1476 'level' : 'NOTSET',
1477 'stream' : 'ext://sys.stdout',
1478 },
1479 },
1480 'loggers' : {
1481 'compiler' : {
1482 'level' : 'DEBUG',
1483 'handlers' : ['hand1'],
1484 },
1485 'compiler.lexer' : {
1486 },
1487 },
1488 'root' : {
1489 'level' : 'WARNING',
1490 },
1491 }
1492
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001493 # config8a disables existing loggers
1494 config8a = {
1495 'version': 1,
1496 'disable_existing_loggers' : True,
1497 'formatters': {
1498 'form1' : {
1499 'format' : '%(levelname)s ++ %(message)s',
1500 },
1501 },
1502 'handlers' : {
1503 'hand1' : {
1504 'class' : 'logging.StreamHandler',
1505 'formatter' : 'form1',
1506 'level' : 'NOTSET',
1507 'stream' : 'ext://sys.stdout',
1508 },
1509 },
1510 'loggers' : {
1511 'compiler' : {
1512 'level' : 'DEBUG',
1513 'handlers' : ['hand1'],
1514 },
1515 'compiler.lexer' : {
1516 },
1517 },
1518 'root' : {
1519 'level' : 'WARNING',
1520 },
1521 }
1522
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001523 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001524 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001525 'formatters': {
1526 'form1' : {
1527 'format' : '%(levelname)s ++ %(message)s',
1528 },
1529 },
1530 'handlers' : {
1531 'hand1' : {
1532 'class' : 'logging.StreamHandler',
1533 'formatter' : 'form1',
1534 'level' : 'WARNING',
1535 'stream' : 'ext://sys.stdout',
1536 },
1537 },
1538 'loggers' : {
1539 'compiler.parser' : {
1540 'level' : 'WARNING',
1541 'handlers' : ['hand1'],
1542 },
1543 },
1544 'root' : {
1545 'level' : 'NOTSET',
1546 },
1547 }
1548
1549 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001550 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001551 'incremental' : True,
1552 'handlers' : {
1553 'hand1' : {
1554 'level' : 'WARNING',
1555 },
1556 },
1557 'loggers' : {
1558 'compiler.parser' : {
1559 'level' : 'INFO',
1560 },
1561 },
1562 }
1563
1564 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001565 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001566 'incremental' : True,
1567 'handlers' : {
1568 'hand1' : {
1569 'level' : 'INFO',
1570 },
1571 },
1572 'loggers' : {
1573 'compiler.parser' : {
1574 'level' : 'INFO',
1575 },
1576 },
1577 }
1578
1579 #As config1 but with a filter added
1580 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001581 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001582 'formatters': {
1583 'form1' : {
1584 'format' : '%(levelname)s ++ %(message)s',
1585 },
1586 },
1587 'filters' : {
1588 'filt1' : {
1589 'name' : 'compiler.parser',
1590 },
1591 },
1592 'handlers' : {
1593 'hand1' : {
1594 'class' : 'logging.StreamHandler',
1595 'formatter' : 'form1',
1596 'level' : 'NOTSET',
1597 'stream' : 'ext://sys.stdout',
1598 'filters' : ['filt1'],
1599 },
1600 },
1601 'loggers' : {
1602 'compiler.parser' : {
1603 'level' : 'DEBUG',
1604 'filters' : ['filt1'],
1605 },
1606 },
1607 'root' : {
1608 'level' : 'WARNING',
1609 'handlers' : ['hand1'],
1610 },
1611 }
1612
1613 #As config1 but using cfg:// references
1614 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001615 'version': 1,
1616 'true_formatters': {
1617 'form1' : {
1618 'format' : '%(levelname)s ++ %(message)s',
1619 },
1620 },
1621 'handler_configs': {
1622 'hand1' : {
1623 'class' : 'logging.StreamHandler',
1624 'formatter' : 'form1',
1625 'level' : 'NOTSET',
1626 'stream' : 'ext://sys.stdout',
1627 },
1628 },
1629 'formatters' : 'cfg://true_formatters',
1630 'handlers' : {
1631 'hand1' : 'cfg://handler_configs[hand1]',
1632 },
1633 'loggers' : {
1634 'compiler.parser' : {
1635 'level' : 'DEBUG',
1636 'handlers' : ['hand1'],
1637 },
1638 },
1639 'root' : {
1640 'level' : 'WARNING',
1641 },
1642 }
1643
1644 #As config11 but missing the version key
1645 config12 = {
1646 'true_formatters': {
1647 'form1' : {
1648 'format' : '%(levelname)s ++ %(message)s',
1649 },
1650 },
1651 'handler_configs': {
1652 'hand1' : {
1653 'class' : 'logging.StreamHandler',
1654 'formatter' : 'form1',
1655 'level' : 'NOTSET',
1656 'stream' : 'ext://sys.stdout',
1657 },
1658 },
1659 'formatters' : 'cfg://true_formatters',
1660 'handlers' : {
1661 'hand1' : 'cfg://handler_configs[hand1]',
1662 },
1663 'loggers' : {
1664 'compiler.parser' : {
1665 'level' : 'DEBUG',
1666 'handlers' : ['hand1'],
1667 },
1668 },
1669 'root' : {
1670 'level' : 'WARNING',
1671 },
1672 }
1673
1674 #As config11 but using an unsupported version
1675 config13 = {
1676 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001677 'true_formatters': {
1678 'form1' : {
1679 'format' : '%(levelname)s ++ %(message)s',
1680 },
1681 },
1682 'handler_configs': {
1683 'hand1' : {
1684 'class' : 'logging.StreamHandler',
1685 'formatter' : 'form1',
1686 'level' : 'NOTSET',
1687 'stream' : 'ext://sys.stdout',
1688 },
1689 },
1690 'formatters' : 'cfg://true_formatters',
1691 'handlers' : {
1692 'hand1' : 'cfg://handler_configs[hand1]',
1693 },
1694 'loggers' : {
1695 'compiler.parser' : {
1696 'level' : 'DEBUG',
1697 'handlers' : ['hand1'],
1698 },
1699 },
1700 'root' : {
1701 'level' : 'WARNING',
1702 },
1703 }
1704
1705 def apply_config(self, conf):
1706 logging.config.dictConfig(conf)
1707
1708 def test_config0_ok(self):
1709 # A simple config which overrides the default settings.
1710 with captured_stdout() as output:
1711 self.apply_config(self.config0)
1712 logger = logging.getLogger()
1713 # Won't output anything
1714 logger.info(self.next_message())
1715 # Outputs a message
1716 logger.error(self.next_message())
1717 self.assert_log_lines([
1718 ('ERROR', '2'),
1719 ], stream=output)
1720 # Original logger output is empty.
1721 self.assert_log_lines([])
1722
1723 def test_config1_ok(self, config=config1):
1724 # A config defining a sub-parser as well.
1725 with captured_stdout() as output:
1726 self.apply_config(config)
1727 logger = logging.getLogger("compiler.parser")
1728 # Both will output a message
1729 logger.info(self.next_message())
1730 logger.error(self.next_message())
1731 self.assert_log_lines([
1732 ('INFO', '1'),
1733 ('ERROR', '2'),
1734 ], stream=output)
1735 # Original logger output is empty.
1736 self.assert_log_lines([])
1737
1738 def test_config2_failure(self):
1739 # A simple config which overrides the default settings.
1740 self.assertRaises(Exception, self.apply_config, self.config2)
1741
1742 def test_config2a_failure(self):
1743 # A simple config which overrides the default settings.
1744 self.assertRaises(Exception, self.apply_config, self.config2a)
1745
1746 def test_config2b_failure(self):
1747 # A simple config which overrides the default settings.
1748 self.assertRaises(Exception, self.apply_config, self.config2b)
1749
1750 def test_config3_failure(self):
1751 # A simple config which overrides the default settings.
1752 self.assertRaises(Exception, self.apply_config, self.config3)
1753
1754 def test_config4_ok(self):
1755 # A config specifying a custom formatter class.
1756 with captured_stdout() as output:
1757 self.apply_config(self.config4)
1758 #logger = logging.getLogger()
1759 try:
1760 raise RuntimeError()
1761 except RuntimeError:
1762 logging.exception("just testing")
1763 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001764 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001765 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1766 # Original logger output is empty
1767 self.assert_log_lines([])
1768
1769 def test_config4a_ok(self):
1770 # A config specifying a custom formatter class.
1771 with captured_stdout() as output:
1772 self.apply_config(self.config4a)
1773 #logger = logging.getLogger()
1774 try:
1775 raise RuntimeError()
1776 except RuntimeError:
1777 logging.exception("just testing")
1778 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001779 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001780 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1781 # Original logger output is empty
1782 self.assert_log_lines([])
1783
1784 def test_config5_ok(self):
1785 self.test_config1_ok(config=self.config5)
1786
1787 def test_config6_failure(self):
1788 self.assertRaises(Exception, self.apply_config, self.config6)
1789
1790 def test_config7_ok(self):
1791 with captured_stdout() as output:
1792 self.apply_config(self.config1)
1793 logger = logging.getLogger("compiler.parser")
1794 # Both will output a message
1795 logger.info(self.next_message())
1796 logger.error(self.next_message())
1797 self.assert_log_lines([
1798 ('INFO', '1'),
1799 ('ERROR', '2'),
1800 ], stream=output)
1801 # Original logger output is empty.
1802 self.assert_log_lines([])
1803 with captured_stdout() as output:
1804 self.apply_config(self.config7)
1805 logger = logging.getLogger("compiler.parser")
1806 self.assertTrue(logger.disabled)
1807 logger = logging.getLogger("compiler.lexer")
1808 # Both will output a message
1809 logger.info(self.next_message())
1810 logger.error(self.next_message())
1811 self.assert_log_lines([
1812 ('INFO', '3'),
1813 ('ERROR', '4'),
1814 ], stream=output)
1815 # Original logger output is empty.
1816 self.assert_log_lines([])
1817
1818 #Same as test_config_7_ok but don't disable old loggers.
1819 def test_config_8_ok(self):
1820 with captured_stdout() as output:
1821 self.apply_config(self.config1)
1822 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001823 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001824 logger.info(self.next_message())
1825 logger.error(self.next_message())
1826 self.assert_log_lines([
1827 ('INFO', '1'),
1828 ('ERROR', '2'),
1829 ], stream=output)
1830 # Original logger output is empty.
1831 self.assert_log_lines([])
1832 with captured_stdout() as output:
1833 self.apply_config(self.config8)
1834 logger = logging.getLogger("compiler.parser")
1835 self.assertFalse(logger.disabled)
1836 # Both will output a message
1837 logger.info(self.next_message())
1838 logger.error(self.next_message())
1839 logger = logging.getLogger("compiler.lexer")
1840 # Both will output a message
1841 logger.info(self.next_message())
1842 logger.error(self.next_message())
1843 self.assert_log_lines([
1844 ('INFO', '3'),
1845 ('ERROR', '4'),
1846 ('INFO', '5'),
1847 ('ERROR', '6'),
1848 ], stream=output)
1849 # Original logger output is empty.
1850 self.assert_log_lines([])
1851
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001852 def test_config_8a_ok(self):
1853 with captured_stdout() as output:
1854 self.apply_config(self.config1a)
1855 logger = logging.getLogger("compiler.parser")
1856 # See issue #11424. compiler-hyphenated sorts
1857 # between compiler and compiler.xyz and this
1858 # was preventing compiler.xyz from being included
1859 # in the child loggers of compiler because of an
1860 # overzealous loop termination condition.
1861 hyphenated = logging.getLogger('compiler-hyphenated')
1862 # All will output a message
1863 logger.info(self.next_message())
1864 logger.error(self.next_message())
1865 hyphenated.critical(self.next_message())
1866 self.assert_log_lines([
1867 ('INFO', '1'),
1868 ('ERROR', '2'),
1869 ('CRITICAL', '3'),
1870 ], stream=output)
1871 # Original logger output is empty.
1872 self.assert_log_lines([])
1873 with captured_stdout() as output:
1874 self.apply_config(self.config8a)
1875 logger = logging.getLogger("compiler.parser")
1876 self.assertFalse(logger.disabled)
1877 # Both will output a message
1878 logger.info(self.next_message())
1879 logger.error(self.next_message())
1880 logger = logging.getLogger("compiler.lexer")
1881 # Both will output a message
1882 logger.info(self.next_message())
1883 logger.error(self.next_message())
1884 # Will not appear
1885 hyphenated.critical(self.next_message())
1886 self.assert_log_lines([
1887 ('INFO', '4'),
1888 ('ERROR', '5'),
1889 ('INFO', '6'),
1890 ('ERROR', '7'),
1891 ], stream=output)
1892 # Original logger output is empty.
1893 self.assert_log_lines([])
1894
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001895 def test_config_9_ok(self):
1896 with captured_stdout() as output:
1897 self.apply_config(self.config9)
1898 logger = logging.getLogger("compiler.parser")
1899 #Nothing will be output since both handler and logger are set to WARNING
1900 logger.info(self.next_message())
1901 self.assert_log_lines([], stream=output)
1902 self.apply_config(self.config9a)
1903 #Nothing will be output since both handler is still set to WARNING
1904 logger.info(self.next_message())
1905 self.assert_log_lines([], stream=output)
1906 self.apply_config(self.config9b)
1907 #Message should now be output
1908 logger.info(self.next_message())
1909 self.assert_log_lines([
1910 ('INFO', '3'),
1911 ], stream=output)
1912
1913 def test_config_10_ok(self):
1914 with captured_stdout() as output:
1915 self.apply_config(self.config10)
1916 logger = logging.getLogger("compiler.parser")
1917 logger.warning(self.next_message())
1918 logger = logging.getLogger('compiler')
1919 #Not output, because filtered
1920 logger.warning(self.next_message())
1921 logger = logging.getLogger('compiler.lexer')
1922 #Not output, because filtered
1923 logger.warning(self.next_message())
1924 logger = logging.getLogger("compiler.parser.codegen")
1925 #Output, as not filtered
1926 logger.error(self.next_message())
1927 self.assert_log_lines([
1928 ('WARNING', '1'),
1929 ('ERROR', '4'),
1930 ], stream=output)
1931
1932 def test_config11_ok(self):
1933 self.test_config1_ok(self.config11)
1934
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001935 def test_config12_failure(self):
1936 self.assertRaises(Exception, self.apply_config, self.config12)
1937
1938 def test_config13_failure(self):
1939 self.assertRaises(Exception, self.apply_config, self.config13)
1940
Victor Stinner45df8202010-04-28 22:31:17 +00001941 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001942 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001943 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001944 # Ask for a randomly assigned port (by using port 0)
1945 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001946 t.start()
1947 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001948 # Now get the port allocated
1949 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001950 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001951 try:
1952 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1953 sock.settimeout(2.0)
1954 sock.connect(('localhost', port))
1955
1956 slen = struct.pack('>L', len(text))
1957 s = slen + text
1958 sentsofar = 0
1959 left = len(s)
1960 while left > 0:
1961 sent = sock.send(s[sentsofar:])
1962 sentsofar += sent
1963 left -= sent
1964 sock.close()
1965 finally:
1966 t.ready.wait(2.0)
1967 logging.config.stopListening()
1968 t.join(2.0)
1969
1970 def test_listen_config_10_ok(self):
1971 with captured_stdout() as output:
1972 self.setup_via_listener(json.dumps(self.config10))
1973 logger = logging.getLogger("compiler.parser")
1974 logger.warning(self.next_message())
1975 logger = logging.getLogger('compiler')
1976 #Not output, because filtered
1977 logger.warning(self.next_message())
1978 logger = logging.getLogger('compiler.lexer')
1979 #Not output, because filtered
1980 logger.warning(self.next_message())
1981 logger = logging.getLogger("compiler.parser.codegen")
1982 #Output, as not filtered
1983 logger.error(self.next_message())
1984 self.assert_log_lines([
1985 ('WARNING', '1'),
1986 ('ERROR', '4'),
1987 ], stream=output)
1988
1989 def test_listen_config_1_ok(self):
1990 with captured_stdout() as output:
1991 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1992 logger = logging.getLogger("compiler.parser")
1993 # Both will output a message
1994 logger.info(self.next_message())
1995 logger.error(self.next_message())
1996 self.assert_log_lines([
1997 ('INFO', '1'),
1998 ('ERROR', '2'),
1999 ], stream=output)
2000 # Original logger output is empty.
2001 self.assert_log_lines([])
2002
2003
2004class ManagerTest(BaseTest):
2005 def test_manager_loggerclass(self):
2006 logged = []
2007
2008 class MyLogger(logging.Logger):
2009 def _log(self, level, msg, args, exc_info=None, extra=None):
2010 logged.append(msg)
2011
2012 man = logging.Manager(None)
2013 self.assertRaises(TypeError, man.setLoggerClass, int)
2014 man.setLoggerClass(MyLogger)
2015 logger = man.getLogger('test')
2016 logger.warning('should appear in logged')
2017 logging.warning('should not appear in logged')
2018
2019 self.assertEqual(logged, ['should appear in logged'])
2020
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002021 def test_set_log_record_factory(self):
2022 man = logging.Manager(None)
2023 expected = object()
2024 man.setLogRecordFactory(expected)
2025 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002026
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002027class ChildLoggerTest(BaseTest):
2028 def test_child_loggers(self):
2029 r = logging.getLogger()
2030 l1 = logging.getLogger('abc')
2031 l2 = logging.getLogger('def.ghi')
2032 c1 = r.getChild('xyz')
2033 c2 = r.getChild('uvw.xyz')
2034 self.assertTrue(c1 is logging.getLogger('xyz'))
2035 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2036 c1 = l1.getChild('def')
2037 c2 = c1.getChild('ghi')
2038 c3 = l1.getChild('def.ghi')
2039 self.assertTrue(c1 is logging.getLogger('abc.def'))
2040 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2041 self.assertTrue(c2 is c3)
2042
2043
Vinay Sajip6fac8172010-10-19 20:44:14 +00002044class DerivedLogRecord(logging.LogRecord):
2045 pass
2046
Vinay Sajip61561522010-12-03 11:50:38 +00002047class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002048
2049 def setUp(self):
2050 class CheckingFilter(logging.Filter):
2051 def __init__(self, cls):
2052 self.cls = cls
2053
2054 def filter(self, record):
2055 t = type(record)
2056 if t is not self.cls:
2057 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2058 self.cls)
2059 raise TypeError(msg)
2060 return True
2061
2062 BaseTest.setUp(self)
2063 self.filter = CheckingFilter(DerivedLogRecord)
2064 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002065 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002066
2067 def tearDown(self):
2068 self.root_logger.removeFilter(self.filter)
2069 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002070 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002071
2072 def test_logrecord_class(self):
2073 self.assertRaises(TypeError, self.root_logger.warning,
2074 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002075 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002076 self.root_logger.error(self.next_message())
2077 self.assert_log_lines([
2078 ('root', 'ERROR', '2'),
2079 ])
2080
2081
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002082class QueueHandlerTest(BaseTest):
2083 # Do not bother with a logger name group.
2084 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2085
2086 def setUp(self):
2087 BaseTest.setUp(self)
2088 self.queue = queue.Queue(-1)
2089 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2090 self.que_logger = logging.getLogger('que')
2091 self.que_logger.propagate = False
2092 self.que_logger.setLevel(logging.WARNING)
2093 self.que_logger.addHandler(self.que_hdlr)
2094
2095 def tearDown(self):
2096 self.que_hdlr.close()
2097 BaseTest.tearDown(self)
2098
2099 def test_queue_handler(self):
2100 self.que_logger.debug(self.next_message())
2101 self.assertRaises(queue.Empty, self.queue.get_nowait)
2102 self.que_logger.info(self.next_message())
2103 self.assertRaises(queue.Empty, self.queue.get_nowait)
2104 msg = self.next_message()
2105 self.que_logger.warning(msg)
2106 data = self.queue.get_nowait()
2107 self.assertTrue(isinstance(data, logging.LogRecord))
2108 self.assertEqual(data.name, self.que_logger.name)
2109 self.assertEqual((data.msg, data.args), (msg, None))
2110
Vinay Sajipa39c5712010-10-25 13:57:39 +00002111class FormatterTest(unittest.TestCase):
2112 def setUp(self):
2113 self.common = {
2114 'name': 'formatter.test',
2115 'level': logging.DEBUG,
2116 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2117 'lineno': 42,
2118 'exc_info': None,
2119 'func': None,
2120 'msg': 'Message with %d %s',
2121 'args': (2, 'placeholders'),
2122 }
2123 self.variants = {
2124 }
2125
2126 def get_record(self, name=None):
2127 result = dict(self.common)
2128 if name is not None:
2129 result.update(self.variants[name])
2130 return logging.makeLogRecord(result)
2131
2132 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002133 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002134 r = self.get_record()
2135 f = logging.Formatter('${%(message)s}')
2136 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2137 f = logging.Formatter('%(random)s')
2138 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002139 self.assertFalse(f.usesTime())
2140 f = logging.Formatter('%(asctime)s')
2141 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002142 f = logging.Formatter('%(asctime)-15s')
2143 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002144 f = logging.Formatter('asctime')
2145 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002146
2147 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002148 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002149 r = self.get_record()
2150 f = logging.Formatter('$%{message}%$', style='{')
2151 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2152 f = logging.Formatter('{random}', style='{')
2153 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002154 self.assertFalse(f.usesTime())
2155 f = logging.Formatter('{asctime}', style='{')
2156 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002157 f = logging.Formatter('{asctime!s:15}', style='{')
2158 self.assertTrue(f.usesTime())
2159 f = logging.Formatter('{asctime:15}', style='{')
2160 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002161 f = logging.Formatter('asctime', style='{')
2162 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002163
2164 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002165 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002166 r = self.get_record()
2167 f = logging.Formatter('$message', style='$')
2168 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2169 f = logging.Formatter('$$%${message}%$$', style='$')
2170 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2171 f = logging.Formatter('${random}', style='$')
2172 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002173 self.assertFalse(f.usesTime())
2174 f = logging.Formatter('${asctime}', style='$')
2175 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002176 f = logging.Formatter('${asctime', style='$')
2177 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002178 f = logging.Formatter('$asctime', style='$')
2179 self.assertTrue(f.usesTime())
2180 f = logging.Formatter('asctime', style='$')
2181 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002182
Vinay Sajip5a27d402010-12-10 11:42:57 +00002183class LastResortTest(BaseTest):
2184 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002185 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002186 root = self.root_logger
2187 root.removeHandler(self.root_hdlr)
2188 old_stderr = sys.stderr
2189 old_lastresort = logging.lastResort
2190 old_raise_exceptions = logging.raiseExceptions
2191 try:
2192 sys.stderr = sio = io.StringIO()
2193 root.warning('This is your final chance!')
2194 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2195 #No handlers and no last resort, so 'No handlers' message
2196 logging.lastResort = None
2197 sys.stderr = sio = io.StringIO()
2198 root.warning('This is your final chance!')
2199 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2200 # 'No handlers' message only printed once
2201 sys.stderr = sio = io.StringIO()
2202 root.warning('This is your final chance!')
2203 self.assertEqual(sio.getvalue(), '')
2204 root.manager.emittedNoHandlerWarning = False
2205 #If raiseExceptions is False, no message is printed
2206 logging.raiseExceptions = False
2207 sys.stderr = sio = io.StringIO()
2208 root.warning('This is your final chance!')
2209 self.assertEqual(sio.getvalue(), '')
2210 finally:
2211 sys.stderr = old_stderr
2212 root.addHandler(self.root_hdlr)
2213 logging.lastResort = old_lastresort
2214 logging.raiseExceptions = old_raise_exceptions
2215
2216
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002217class FakeHandler:
2218
2219 def __init__(self, identifier, called):
2220 for method in ('acquire', 'flush', 'close', 'release'):
2221 setattr(self, method, self.record_call(identifier, method, called))
2222
2223 def record_call(self, identifier, method_name, called):
2224 def inner():
2225 called.append('{} - {}'.format(identifier, method_name))
2226 return inner
2227
2228
2229class RecordingHandler(logging.NullHandler):
2230
2231 def __init__(self, *args, **kwargs):
2232 super(RecordingHandler, self).__init__(*args, **kwargs)
2233 self.records = []
2234
2235 def handle(self, record):
2236 """Keep track of all the emitted records."""
2237 self.records.append(record)
2238
2239
2240class ShutdownTest(BaseTest):
2241
2242 """Tets suite for the shutdown method."""
2243
2244 def setUp(self):
2245 super(ShutdownTest, self).setUp()
2246 self.called = []
2247
2248 raise_exceptions = logging.raiseExceptions
2249 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2250
2251 def raise_error(self, error):
2252 def inner():
2253 raise error()
2254 return inner
2255
2256 def test_no_failure(self):
2257 # create some fake handlers
2258 handler0 = FakeHandler(0, self.called)
2259 handler1 = FakeHandler(1, self.called)
2260 handler2 = FakeHandler(2, self.called)
2261
2262 # create live weakref to those handlers
2263 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2264
2265 logging.shutdown(handlerList=list(handlers))
2266
2267 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2268 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2269 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2270 self.assertEqual(expected, self.called)
2271
2272 def _test_with_failure_in_method(self, method, error):
2273 handler = FakeHandler(0, self.called)
2274 setattr(handler, method, self.raise_error(error))
2275 handlers = [logging.weakref.ref(handler)]
2276
2277 logging.shutdown(handlerList=list(handlers))
2278
2279 self.assertEqual('0 - release', self.called[-1])
2280
2281 def test_with_ioerror_in_acquire(self):
2282 self._test_with_failure_in_method('acquire', IOError)
2283
2284 def test_with_ioerror_in_flush(self):
2285 self._test_with_failure_in_method('flush', IOError)
2286
2287 def test_with_ioerror_in_close(self):
2288 self._test_with_failure_in_method('close', IOError)
2289
2290 def test_with_valueerror_in_acquire(self):
2291 self._test_with_failure_in_method('acquire', ValueError)
2292
2293 def test_with_valueerror_in_flush(self):
2294 self._test_with_failure_in_method('flush', ValueError)
2295
2296 def test_with_valueerror_in_close(self):
2297 self._test_with_failure_in_method('close', ValueError)
2298
2299 def test_with_other_error_in_acquire_without_raise(self):
2300 logging.raiseExceptions = False
2301 self._test_with_failure_in_method('acquire', IndexError)
2302
2303 def test_with_other_error_in_flush_without_raise(self):
2304 logging.raiseExceptions = False
2305 self._test_with_failure_in_method('flush', IndexError)
2306
2307 def test_with_other_error_in_close_without_raise(self):
2308 logging.raiseExceptions = False
2309 self._test_with_failure_in_method('close', IndexError)
2310
2311 def test_with_other_error_in_acquire_with_raise(self):
2312 logging.raiseExceptions = True
2313 self.assertRaises(IndexError, self._test_with_failure_in_method,
2314 'acquire', IndexError)
2315
2316 def test_with_other_error_in_flush_with_raise(self):
2317 logging.raiseExceptions = True
2318 self.assertRaises(IndexError, self._test_with_failure_in_method,
2319 'flush', IndexError)
2320
2321 def test_with_other_error_in_close_with_raise(self):
2322 logging.raiseExceptions = True
2323 self.assertRaises(IndexError, self._test_with_failure_in_method,
2324 'close', IndexError)
2325
2326
2327class ModuleLevelMiscTest(BaseTest):
2328
2329 """Tets suite for some module level methods."""
2330
2331 def test_disable(self):
2332 old_disable = logging.root.manager.disable
2333 # confirm our assumptions are correct
2334 assert old_disable == 0
2335 self.addCleanup(lambda: logging.disable(old_disable))
2336
2337 logging.disable(83)
2338 self.assertEqual(logging.root.manager.disable, 83)
2339
2340 def _test_log(self, method, level=None):
2341 called = []
2342 patch(self, logging, 'basicConfig',
2343 lambda *a, **kw: called.append(a, kw))
2344
2345 recording = RecordingHandler()
2346 logging.root.addHandler(recording)
2347
2348 log_method = getattr(logging, method)
2349 if level is not None:
2350 log_method(level, "test me: %r", recording)
2351 else:
2352 log_method("test me: %r", recording)
2353
2354 self.assertEqual(len(recording.records), 1)
2355 record = recording.records[0]
2356 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2357
2358 expected_level = level if level is not None else getattr(logging, method.upper())
2359 self.assertEqual(record.levelno, expected_level)
2360
2361 # basicConfig was not called!
2362 self.assertEqual(called, [])
2363
2364 def test_log(self):
2365 self._test_log('log', logging.ERROR)
2366
2367 def test_debug(self):
2368 self._test_log('debug')
2369
2370 def test_info(self):
2371 self._test_log('info')
2372
2373 def test_warning(self):
2374 self._test_log('warning')
2375
2376 def test_error(self):
2377 self._test_log('error')
2378
2379 def test_critical(self):
2380 self._test_log('critical')
2381
2382 def test_set_logger_class(self):
2383 self.assertRaises(TypeError, logging.setLoggerClass, object)
2384
2385 class MyLogger(logging.Logger):
2386 pass
2387
2388 logging.setLoggerClass(MyLogger)
2389 self.assertEqual(logging.getLoggerClass(), MyLogger)
2390
2391 logging.setLoggerClass(logging.Logger)
2392 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2393
2394
2395class BasicConfigTest(unittest.TestCase):
2396
2397 """Tets suite for logging.basicConfig."""
2398
2399 def setUp(self):
2400 super(BasicConfigTest, self).setUp()
2401 handlers = logging.root.handlers
2402 self.addCleanup(lambda: setattr(logging.root, 'handlers', handlers))
2403 logging.root.handlers = []
2404
2405 def tearDown(self):
2406 logging.shutdown()
2407 super(BasicConfigTest, self).tearDown()
2408
2409 def test_no_kwargs(self):
2410 logging.basicConfig()
2411
2412 # handler defaults to a StreamHandler to sys.stderr
2413 self.assertEqual(len(logging.root.handlers), 1)
2414 handler = logging.root.handlers[0]
2415 self.assertIsInstance(handler, logging.StreamHandler)
2416 self.assertEqual(handler.stream, sys.stderr)
2417
2418 formatter = handler.formatter
2419 # format defaults to logging.BASIC_FORMAT
2420 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2421 # datefmt defaults to None
2422 self.assertIsNone(formatter.datefmt)
2423 # style defaults to %
2424 self.assertIsInstance(formatter._style, logging.PercentStyle)
2425
2426 # level is not explicitely set
2427 self.assertEqual(logging.root.level, logging.WARNING)
2428
2429 def test_filename(self):
2430 logging.basicConfig(filename='test.log')
2431
2432 self.assertEqual(len(logging.root.handlers), 1)
2433 handler = logging.root.handlers[0]
2434 self.assertIsInstance(handler, logging.FileHandler)
2435
2436 expected = logging.FileHandler('test.log', 'a')
2437 self.addCleanup(expected.close)
2438 self.assertEqual(handler.stream.mode, expected.stream.mode)
2439 self.assertEqual(handler.stream.name, expected.stream.name)
2440
2441 def test_filemode(self):
2442 logging.basicConfig(filename='test.log', filemode='wb')
2443
2444 handler = logging.root.handlers[0]
2445 expected = logging.FileHandler('test.log', 'wb')
2446 self.addCleanup(expected.close)
2447 self.assertEqual(handler.stream.mode, expected.stream.mode)
2448
2449 def test_stream(self):
2450 stream = io.StringIO()
2451 self.addCleanup(stream.close)
2452 logging.basicConfig(stream=stream)
2453
2454 self.assertEqual(len(logging.root.handlers), 1)
2455 handler = logging.root.handlers[0]
2456 self.assertIsInstance(handler, logging.StreamHandler)
2457 self.assertEqual(handler.stream, stream)
2458
2459 def test_format(self):
2460 logging.basicConfig(format='foo')
2461
2462 formatter = logging.root.handlers[0].formatter
2463 self.assertEqual(formatter._style._fmt, 'foo')
2464
2465 def test_datefmt(self):
2466 logging.basicConfig(datefmt='bar')
2467
2468 formatter = logging.root.handlers[0].formatter
2469 self.assertEqual(formatter.datefmt, 'bar')
2470
2471 def test_style(self):
2472 logging.basicConfig(style='$')
2473
2474 formatter = logging.root.handlers[0].formatter
2475 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2476
2477 def test_level(self):
2478 old_level = logging.root.level
2479 self.addCleanup(lambda: logging.root.setLevel(old_level))
2480
2481 logging.basicConfig(level=57)
2482 self.assertEqual(logging.root.level, 57)
2483
2484 def _test_log(self, method, level=None):
2485 # logging.root has no handlers so basicConfig should be called
2486 called = []
2487
2488 old_basic_config = logging.basicConfig
2489 def my_basic_config(*a, **kw):
2490 old_basic_config()
2491 old_level = logging.root.level
2492 logging.root.setLevel(100) # avoid having messages in stderr
2493 self.addCleanup(lambda: logging.root.setLevel(old_level))
2494 called.append((a, kw))
2495
2496 patch(self, logging, 'basicConfig', my_basic_config)
2497
2498 log_method = getattr(logging, method)
2499 if level is not None:
2500 log_method(level, "test me")
2501 else:
2502 log_method("test me")
2503
2504 # basicConfig was called with no arguments
2505 self.assertEqual(called, [((), {})])
2506
2507 def test_log(self):
2508 self._test_log('log', logging.WARNING)
2509
2510 def test_debug(self):
2511 self._test_log('debug')
2512
2513 def test_info(self):
2514 self._test_log('info')
2515
2516 def test_warning(self):
2517 self._test_log('warning')
2518
2519 def test_error(self):
2520 self._test_log('error')
2521
2522 def test_critical(self):
2523 self._test_log('critical')
2524
2525
2526class LoggerAdapterTest(unittest.TestCase):
2527
2528 def setUp(self):
2529 super(LoggerAdapterTest, self).setUp()
2530 old_handler_list = logging._handlerList[:]
2531
2532 self.recording = RecordingHandler()
2533 self.logger = logging.root
2534 self.logger.addHandler(self.recording)
2535 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2536 self.addCleanup(self.recording.close)
2537
2538 def cleanup():
2539 logging._handlerList[:] = old_handler_list
2540
2541 self.addCleanup(cleanup)
2542 self.addCleanup(logging.shutdown)
2543 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2544
2545 def test_exception(self):
2546 msg = 'testing exception: %r'
2547 exc = None
2548 try:
2549 assert False
2550 except AssertionError as e:
2551 exc = e
2552 self.adapter.exception(msg, self.recording)
2553
2554 self.assertEqual(len(self.recording.records), 1)
2555 record = self.recording.records[0]
2556 self.assertEqual(record.levelno, logging.ERROR)
2557 self.assertEqual(record.msg, msg)
2558 self.assertEqual(record.args, (self.recording,))
2559 self.assertEqual(record.exc_info,
2560 (exc.__class__, exc, exc.__traceback__))
2561
2562 def test_critical(self):
2563 msg = 'critical test! %r'
2564 self.adapter.critical(msg, self.recording)
2565
2566 self.assertEqual(len(self.recording.records), 1)
2567 record = self.recording.records[0]
2568 self.assertEqual(record.levelno, logging.CRITICAL)
2569 self.assertEqual(record.msg, msg)
2570 self.assertEqual(record.args, (self.recording,))
2571
2572 def test_is_enabled_for(self):
2573 old_disable = self.adapter.logger.manager.disable
2574 self.adapter.logger.manager.disable = 33
2575 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2576 'disable', old_disable))
2577 self.assertFalse(self.adapter.isEnabledFor(32))
2578
2579 def test_has_handlers(self):
2580 self.assertTrue(self.adapter.hasHandlers())
2581
2582 for handler in self.logger.handlers:
2583 self.logger.removeHandler(handler)
2584 assert not self.logger.hasHandlers()
2585
2586 self.assertFalse(self.adapter.hasHandlers())
2587
2588
2589class LoggerTest(BaseTest):
2590
2591 def setUp(self):
2592 super(LoggerTest, self).setUp()
2593 self.recording = RecordingHandler()
2594 self.logger = logging.Logger(name='blah')
2595 self.logger.addHandler(self.recording)
2596 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2597 self.addCleanup(self.recording.close)
2598 self.addCleanup(logging.shutdown)
2599
2600 def test_set_invalid_level(self):
2601 self.assertRaises(TypeError, self.logger.setLevel, object())
2602
2603 def test_exception(self):
2604 msg = 'testing exception: %r'
2605 exc = None
2606 try:
2607 assert False
2608 except AssertionError as e:
2609 exc = e
2610 self.logger.exception(msg, self.recording)
2611
2612 self.assertEqual(len(self.recording.records), 1)
2613 record = self.recording.records[0]
2614 self.assertEqual(record.levelno, logging.ERROR)
2615 self.assertEqual(record.msg, msg)
2616 self.assertEqual(record.args, (self.recording,))
2617 self.assertEqual(record.exc_info,
2618 (exc.__class__, exc, exc.__traceback__))
2619
2620 def test_log_invalid_level_with_raise(self):
2621 old_raise = logging.raiseExceptions
2622 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2623
2624 logging.raiseExceptions = True
2625 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2626
2627 def test_log_invalid_level_no_raise(self):
2628 old_raise = logging.raiseExceptions
2629 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2630
2631 logging.raiseExceptions = False
2632 self.logger.log('10', 'test message') # no exception happens
2633
2634 def test_find_caller_with_stack_info(self):
2635 called = []
2636 patch(self, logging.traceback, 'print_stack',
2637 lambda f, file: called.append(file.getvalue()))
2638
2639 self.logger.findCaller(stack_info=True)
2640
2641 self.assertEqual(len(called), 1)
2642 self.assertEqual('Stack (most recent call last):\n', called[0])
2643
2644 def test_make_record_with_extra_overwrite(self):
2645 name = 'my record'
2646 level = 13
2647 fn = lno = msg = args = exc_info = func = sinfo = None
2648 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2649 exc_info, func, sinfo)
2650
2651 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2652 extra = {key: 'some value'}
2653 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2654 fn, lno, msg, args, exc_info,
2655 extra=extra, sinfo=sinfo)
2656
2657 def test_make_record_with_extra_no_overwrite(self):
2658 name = 'my record'
2659 level = 13
2660 fn = lno = msg = args = exc_info = func = sinfo = None
2661 extra = {'valid_key': 'some value'}
2662 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2663 exc_info, extra=extra, sinfo=sinfo)
2664 self.assertIn('valid_key', result.__dict__)
2665
2666 def test_has_handlers(self):
2667 self.assertTrue(self.logger.hasHandlers())
2668
2669 for handler in self.logger.handlers:
2670 self.logger.removeHandler(handler)
2671 assert not self.logger.hasHandlers()
2672
2673 self.assertFalse(self.logger.hasHandlers())
2674
2675 def test_has_handlers_no_propagate(self):
2676 child_logger = logging.getLogger('blah.child')
2677 child_logger.propagate = False
2678 assert child_logger.handlers == []
2679
2680 self.assertFalse(child_logger.hasHandlers())
2681
2682 def test_is_enabled_for(self):
2683 old_disable = self.logger.manager.disable
2684 self.logger.manager.disable = 23
2685 self.addCleanup(lambda: setattr(self.logger.manager,
2686 'disable', old_disable))
2687 self.assertFalse(self.logger.isEnabledFor(22))
2688
2689
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002690class BaseFileTest(BaseTest):
2691 "Base class for handler tests that write log files"
2692
2693 def setUp(self):
2694 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002695 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2696 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002697 self.rmfiles = []
2698
2699 def tearDown(self):
2700 for fn in self.rmfiles:
2701 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002702 if os.path.exists(self.fn):
2703 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002704 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002705
2706 def assertLogFile(self, filename):
2707 "Assert a log file is there and register it for deletion"
2708 self.assertTrue(os.path.exists(filename),
2709 msg="Log file %r does not exist")
2710 self.rmfiles.append(filename)
2711
2712
2713class RotatingFileHandlerTest(BaseFileTest):
2714 def next_rec(self):
2715 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2716 self.next_message(), None, None, None)
2717
2718 def test_should_not_rollover(self):
2719 # If maxbytes is zero rollover never occurs
2720 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2721 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002722 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002723
2724 def test_should_rollover(self):
2725 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2726 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002727 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002728
2729 def test_file_created(self):
2730 # checks that the file is created and assumes it was created
2731 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002732 rh = logging.handlers.RotatingFileHandler(self.fn)
2733 rh.emit(self.next_rec())
2734 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002735 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002736
2737 def test_rollover_filenames(self):
2738 rh = logging.handlers.RotatingFileHandler(
2739 self.fn, backupCount=2, maxBytes=1)
2740 rh.emit(self.next_rec())
2741 self.assertLogFile(self.fn)
2742 rh.emit(self.next_rec())
2743 self.assertLogFile(self.fn + ".1")
2744 rh.emit(self.next_rec())
2745 self.assertLogFile(self.fn + ".2")
2746 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002747 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002748
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002749class TimedRotatingFileHandlerTest(BaseFileTest):
2750 # test methods added below
2751 pass
2752
2753def secs(**kw):
2754 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2755
2756for when, exp in (('S', 1),
2757 ('M', 60),
2758 ('H', 60 * 60),
2759 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002760 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002761 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002762 ('W0', secs(days=4, hours=24)),
2763 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002764 def test_compute_rollover(self, when=when, exp=exp):
2765 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002766 self.fn, when=when, interval=1, backupCount=0, utc=True)
2767 currentTime = 0.0
2768 actual = rh.computeRollover(currentTime)
2769 if exp != actual:
2770 # Failures occur on some systems for MIDNIGHT and W0.
2771 # Print detailed calculation for MIDNIGHT so we can try to see
2772 # what's going on
2773 import time
2774 if when == 'MIDNIGHT':
2775 try:
2776 if rh.utc:
2777 t = time.gmtime(currentTime)
2778 else:
2779 t = time.localtime(currentTime)
2780 currentHour = t[3]
2781 currentMinute = t[4]
2782 currentSecond = t[5]
2783 # r is the number of seconds left between now and midnight
2784 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2785 currentMinute) * 60 +
2786 currentSecond)
2787 result = currentTime + r
2788 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2789 print('currentHour: %s' % currentHour, file=sys.stderr)
2790 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2791 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2792 print('r: %s' % r, file=sys.stderr)
2793 print('result: %s' % result, file=sys.stderr)
2794 except Exception:
2795 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2796 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002797 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002798 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2799
Christian Heimes180510d2008-03-03 19:15:45 +00002800# Set the locale to the platform-dependent default. I have no idea
2801# why the test does this, but in any case we save the current locale
2802# first and restore it at the end.
2803@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002804def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002805 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002806 CustomLevelsAndFiltersTest, MemoryHandlerTest,
2807 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002808 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajipa39c5712010-10-25 13:57:39 +00002809 FormatterTest,
Vinay Sajip61561522010-12-03 11:50:38 +00002810 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002811 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
2812 LoggerAdapterTest, LoggerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002813 RotatingFileHandlerTest,
Vinay Sajip5a27d402010-12-10 11:42:57 +00002814 LastResortTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00002815 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00002816 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00002817
Christian Heimes180510d2008-03-03 19:15:45 +00002818if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002819 test_main()