blob: 88f0ebc42eee970c4fa23f52a59d540f40211771 [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
Victor Stinner3c7c3552011-03-31 03:22:15 +02002409 @unittest.skipIf(True, "test disabled, issue #11557")
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002410 def test_no_kwargs(self):
2411 logging.basicConfig()
2412
2413 # handler defaults to a StreamHandler to sys.stderr
2414 self.assertEqual(len(logging.root.handlers), 1)
2415 handler = logging.root.handlers[0]
2416 self.assertIsInstance(handler, logging.StreamHandler)
2417 self.assertEqual(handler.stream, sys.stderr)
2418
2419 formatter = handler.formatter
2420 # format defaults to logging.BASIC_FORMAT
2421 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2422 # datefmt defaults to None
2423 self.assertIsNone(formatter.datefmt)
2424 # style defaults to %
2425 self.assertIsInstance(formatter._style, logging.PercentStyle)
2426
2427 # level is not explicitely set
2428 self.assertEqual(logging.root.level, logging.WARNING)
2429
2430 def test_filename(self):
2431 logging.basicConfig(filename='test.log')
2432
2433 self.assertEqual(len(logging.root.handlers), 1)
2434 handler = logging.root.handlers[0]
2435 self.assertIsInstance(handler, logging.FileHandler)
2436
2437 expected = logging.FileHandler('test.log', 'a')
2438 self.addCleanup(expected.close)
2439 self.assertEqual(handler.stream.mode, expected.stream.mode)
2440 self.assertEqual(handler.stream.name, expected.stream.name)
2441
2442 def test_filemode(self):
2443 logging.basicConfig(filename='test.log', filemode='wb')
2444
2445 handler = logging.root.handlers[0]
2446 expected = logging.FileHandler('test.log', 'wb')
2447 self.addCleanup(expected.close)
2448 self.assertEqual(handler.stream.mode, expected.stream.mode)
2449
2450 def test_stream(self):
2451 stream = io.StringIO()
2452 self.addCleanup(stream.close)
2453 logging.basicConfig(stream=stream)
2454
2455 self.assertEqual(len(logging.root.handlers), 1)
2456 handler = logging.root.handlers[0]
2457 self.assertIsInstance(handler, logging.StreamHandler)
2458 self.assertEqual(handler.stream, stream)
2459
2460 def test_format(self):
2461 logging.basicConfig(format='foo')
2462
2463 formatter = logging.root.handlers[0].formatter
2464 self.assertEqual(formatter._style._fmt, 'foo')
2465
2466 def test_datefmt(self):
2467 logging.basicConfig(datefmt='bar')
2468
2469 formatter = logging.root.handlers[0].formatter
2470 self.assertEqual(formatter.datefmt, 'bar')
2471
2472 def test_style(self):
2473 logging.basicConfig(style='$')
2474
2475 formatter = logging.root.handlers[0].formatter
2476 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2477
2478 def test_level(self):
2479 old_level = logging.root.level
2480 self.addCleanup(lambda: logging.root.setLevel(old_level))
2481
2482 logging.basicConfig(level=57)
2483 self.assertEqual(logging.root.level, 57)
2484
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002485 def test_incompatible(self):
2486 assertRaises = self.assertRaises
2487 handlers = [logging.StreamHandler()]
2488 stream = sys.stderr
2489 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2490 stream=stream)
2491 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2492 handlers=handlers)
2493 assertRaises(ValueError, logging.basicConfig, stream=stream,
2494 handlers=handlers)
2495
2496 def test_handlers(self):
2497 handlers = [logging.StreamHandler(), logging.StreamHandler(sys.stdout)]
2498 logging.basicConfig(handlers=handlers)
2499 self.assertIs(handlers[0], logging.root.handlers[0])
2500 self.assertIs(handlers[1], logging.root.handlers[1])
2501 self.assertIsNotNone(handlers[0].formatter)
2502 self.assertIsNotNone(handlers[1].formatter)
2503 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2504
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002505 def _test_log(self, method, level=None):
2506 # logging.root has no handlers so basicConfig should be called
2507 called = []
2508
2509 old_basic_config = logging.basicConfig
2510 def my_basic_config(*a, **kw):
2511 old_basic_config()
2512 old_level = logging.root.level
2513 logging.root.setLevel(100) # avoid having messages in stderr
2514 self.addCleanup(lambda: logging.root.setLevel(old_level))
2515 called.append((a, kw))
2516
2517 patch(self, logging, 'basicConfig', my_basic_config)
2518
2519 log_method = getattr(logging, method)
2520 if level is not None:
2521 log_method(level, "test me")
2522 else:
2523 log_method("test me")
2524
2525 # basicConfig was called with no arguments
2526 self.assertEqual(called, [((), {})])
2527
2528 def test_log(self):
2529 self._test_log('log', logging.WARNING)
2530
2531 def test_debug(self):
2532 self._test_log('debug')
2533
2534 def test_info(self):
2535 self._test_log('info')
2536
2537 def test_warning(self):
2538 self._test_log('warning')
2539
2540 def test_error(self):
2541 self._test_log('error')
2542
2543 def test_critical(self):
2544 self._test_log('critical')
2545
2546
2547class LoggerAdapterTest(unittest.TestCase):
2548
2549 def setUp(self):
2550 super(LoggerAdapterTest, self).setUp()
2551 old_handler_list = logging._handlerList[:]
2552
2553 self.recording = RecordingHandler()
2554 self.logger = logging.root
2555 self.logger.addHandler(self.recording)
2556 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2557 self.addCleanup(self.recording.close)
2558
2559 def cleanup():
2560 logging._handlerList[:] = old_handler_list
2561
2562 self.addCleanup(cleanup)
2563 self.addCleanup(logging.shutdown)
2564 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2565
2566 def test_exception(self):
2567 msg = 'testing exception: %r'
2568 exc = None
2569 try:
2570 assert False
2571 except AssertionError as e:
2572 exc = e
2573 self.adapter.exception(msg, self.recording)
2574
2575 self.assertEqual(len(self.recording.records), 1)
2576 record = self.recording.records[0]
2577 self.assertEqual(record.levelno, logging.ERROR)
2578 self.assertEqual(record.msg, msg)
2579 self.assertEqual(record.args, (self.recording,))
2580 self.assertEqual(record.exc_info,
2581 (exc.__class__, exc, exc.__traceback__))
2582
2583 def test_critical(self):
2584 msg = 'critical test! %r'
2585 self.adapter.critical(msg, self.recording)
2586
2587 self.assertEqual(len(self.recording.records), 1)
2588 record = self.recording.records[0]
2589 self.assertEqual(record.levelno, logging.CRITICAL)
2590 self.assertEqual(record.msg, msg)
2591 self.assertEqual(record.args, (self.recording,))
2592
2593 def test_is_enabled_for(self):
2594 old_disable = self.adapter.logger.manager.disable
2595 self.adapter.logger.manager.disable = 33
2596 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2597 'disable', old_disable))
2598 self.assertFalse(self.adapter.isEnabledFor(32))
2599
2600 def test_has_handlers(self):
2601 self.assertTrue(self.adapter.hasHandlers())
2602
2603 for handler in self.logger.handlers:
2604 self.logger.removeHandler(handler)
2605 assert not self.logger.hasHandlers()
2606
2607 self.assertFalse(self.adapter.hasHandlers())
2608
2609
2610class LoggerTest(BaseTest):
2611
2612 def setUp(self):
2613 super(LoggerTest, self).setUp()
2614 self.recording = RecordingHandler()
2615 self.logger = logging.Logger(name='blah')
2616 self.logger.addHandler(self.recording)
2617 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2618 self.addCleanup(self.recording.close)
2619 self.addCleanup(logging.shutdown)
2620
2621 def test_set_invalid_level(self):
2622 self.assertRaises(TypeError, self.logger.setLevel, object())
2623
2624 def test_exception(self):
2625 msg = 'testing exception: %r'
2626 exc = None
2627 try:
2628 assert False
2629 except AssertionError as e:
2630 exc = e
2631 self.logger.exception(msg, self.recording)
2632
2633 self.assertEqual(len(self.recording.records), 1)
2634 record = self.recording.records[0]
2635 self.assertEqual(record.levelno, logging.ERROR)
2636 self.assertEqual(record.msg, msg)
2637 self.assertEqual(record.args, (self.recording,))
2638 self.assertEqual(record.exc_info,
2639 (exc.__class__, exc, exc.__traceback__))
2640
2641 def test_log_invalid_level_with_raise(self):
2642 old_raise = logging.raiseExceptions
2643 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2644
2645 logging.raiseExceptions = True
2646 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2647
2648 def test_log_invalid_level_no_raise(self):
2649 old_raise = logging.raiseExceptions
2650 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2651
2652 logging.raiseExceptions = False
2653 self.logger.log('10', 'test message') # no exception happens
2654
2655 def test_find_caller_with_stack_info(self):
2656 called = []
2657 patch(self, logging.traceback, 'print_stack',
2658 lambda f, file: called.append(file.getvalue()))
2659
2660 self.logger.findCaller(stack_info=True)
2661
2662 self.assertEqual(len(called), 1)
2663 self.assertEqual('Stack (most recent call last):\n', called[0])
2664
2665 def test_make_record_with_extra_overwrite(self):
2666 name = 'my record'
2667 level = 13
2668 fn = lno = msg = args = exc_info = func = sinfo = None
2669 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2670 exc_info, func, sinfo)
2671
2672 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2673 extra = {key: 'some value'}
2674 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2675 fn, lno, msg, args, exc_info,
2676 extra=extra, sinfo=sinfo)
2677
2678 def test_make_record_with_extra_no_overwrite(self):
2679 name = 'my record'
2680 level = 13
2681 fn = lno = msg = args = exc_info = func = sinfo = None
2682 extra = {'valid_key': 'some value'}
2683 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2684 exc_info, extra=extra, sinfo=sinfo)
2685 self.assertIn('valid_key', result.__dict__)
2686
2687 def test_has_handlers(self):
2688 self.assertTrue(self.logger.hasHandlers())
2689
2690 for handler in self.logger.handlers:
2691 self.logger.removeHandler(handler)
2692 assert not self.logger.hasHandlers()
2693
2694 self.assertFalse(self.logger.hasHandlers())
2695
2696 def test_has_handlers_no_propagate(self):
2697 child_logger = logging.getLogger('blah.child')
2698 child_logger.propagate = False
2699 assert child_logger.handlers == []
2700
2701 self.assertFalse(child_logger.hasHandlers())
2702
2703 def test_is_enabled_for(self):
2704 old_disable = self.logger.manager.disable
2705 self.logger.manager.disable = 23
2706 self.addCleanup(lambda: setattr(self.logger.manager,
2707 'disable', old_disable))
2708 self.assertFalse(self.logger.isEnabledFor(22))
2709
2710
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002711class BaseFileTest(BaseTest):
2712 "Base class for handler tests that write log files"
2713
2714 def setUp(self):
2715 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002716 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2717 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002718 self.rmfiles = []
2719
2720 def tearDown(self):
2721 for fn in self.rmfiles:
2722 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002723 if os.path.exists(self.fn):
2724 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002725 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002726
2727 def assertLogFile(self, filename):
2728 "Assert a log file is there and register it for deletion"
2729 self.assertTrue(os.path.exists(filename),
2730 msg="Log file %r does not exist")
2731 self.rmfiles.append(filename)
2732
2733
2734class RotatingFileHandlerTest(BaseFileTest):
2735 def next_rec(self):
2736 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2737 self.next_message(), None, None, None)
2738
2739 def test_should_not_rollover(self):
2740 # If maxbytes is zero rollover never occurs
2741 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2742 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002743 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002744
2745 def test_should_rollover(self):
2746 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2747 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002748 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002749
2750 def test_file_created(self):
2751 # checks that the file is created and assumes it was created
2752 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002753 rh = logging.handlers.RotatingFileHandler(self.fn)
2754 rh.emit(self.next_rec())
2755 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002756 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002757
2758 def test_rollover_filenames(self):
2759 rh = logging.handlers.RotatingFileHandler(
2760 self.fn, backupCount=2, maxBytes=1)
2761 rh.emit(self.next_rec())
2762 self.assertLogFile(self.fn)
2763 rh.emit(self.next_rec())
2764 self.assertLogFile(self.fn + ".1")
2765 rh.emit(self.next_rec())
2766 self.assertLogFile(self.fn + ".2")
2767 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002768 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002769
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002770class TimedRotatingFileHandlerTest(BaseFileTest):
2771 # test methods added below
2772 pass
2773
2774def secs(**kw):
2775 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2776
2777for when, exp in (('S', 1),
2778 ('M', 60),
2779 ('H', 60 * 60),
2780 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002781 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002782 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002783 ('W0', secs(days=4, hours=24)),
2784 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002785 def test_compute_rollover(self, when=when, exp=exp):
2786 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002787 self.fn, when=when, interval=1, backupCount=0, utc=True)
2788 currentTime = 0.0
2789 actual = rh.computeRollover(currentTime)
2790 if exp != actual:
2791 # Failures occur on some systems for MIDNIGHT and W0.
2792 # Print detailed calculation for MIDNIGHT so we can try to see
2793 # what's going on
2794 import time
2795 if when == 'MIDNIGHT':
2796 try:
2797 if rh.utc:
2798 t = time.gmtime(currentTime)
2799 else:
2800 t = time.localtime(currentTime)
2801 currentHour = t[3]
2802 currentMinute = t[4]
2803 currentSecond = t[5]
2804 # r is the number of seconds left between now and midnight
2805 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2806 currentMinute) * 60 +
2807 currentSecond)
2808 result = currentTime + r
2809 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2810 print('currentHour: %s' % currentHour, file=sys.stderr)
2811 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2812 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2813 print('r: %s' % r, file=sys.stderr)
2814 print('result: %s' % result, file=sys.stderr)
2815 except Exception:
2816 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2817 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002818 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002819 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2820
Christian Heimes180510d2008-03-03 19:15:45 +00002821# Set the locale to the platform-dependent default. I have no idea
2822# why the test does this, but in any case we save the current locale
2823# first and restore it at the end.
2824@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002825def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002826 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002827 CustomLevelsAndFiltersTest, MemoryHandlerTest,
2828 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002829 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajipa39c5712010-10-25 13:57:39 +00002830 FormatterTest,
Vinay Sajip61561522010-12-03 11:50:38 +00002831 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002832 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
2833 LoggerAdapterTest, LoggerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002834 RotatingFileHandlerTest,
Vinay Sajip5a27d402010-12-10 11:42:57 +00002835 LastResortTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00002836 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00002837 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00002838
Christian Heimes180510d2008-03-03 19:15:45 +00002839if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002840 test_main()