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