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