blob: 6623a0f532257626f91c538a2280689e6e5f99cf [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
Florent Xiclunadc692742010-08-15 20:16:27 +000043from test.support import captured_stdout, run_with_locale, run_unittest
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)
Brett Cannondf8709d2009-04-01 20:01:47 +00001086 try:
Brett Cannon5b9082a2009-04-05 18:57:32 +00001087 warnings.filterwarnings("always", category=UserWarning)
Brett Cannondf8709d2009-04-01 20:01:47 +00001088 file = io.StringIO()
1089 h = logging.StreamHandler(file)
1090 logger = logging.getLogger("py.warnings")
1091 logger.addHandler(h)
1092 warnings.warn("I'm warning you...")
1093 logger.removeHandler(h)
1094 s = file.getvalue()
1095 h.close()
1096 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001097
Brett Cannondf8709d2009-04-01 20:01:47 +00001098 #See if an explicit file uses the original implementation
1099 file = io.StringIO()
1100 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1101 file, "Dummy line")
1102 s = file.getvalue()
1103 file.close()
1104 self.assertEqual(s,
1105 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1106 finally:
1107 logging.captureWarnings(False)
Georg Brandlf9734072008-12-07 15:30:06 +00001108
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001109
1110def formatFunc(format, datefmt=None):
1111 return logging.Formatter(format, datefmt)
1112
1113def handlerFunc():
1114 return logging.StreamHandler()
1115
1116class CustomHandler(logging.StreamHandler):
1117 pass
1118
1119class ConfigDictTest(BaseTest):
1120
1121 """Reading logging config from a dictionary."""
1122
1123 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1124
1125 # config0 is a standard configuration.
1126 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001127 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001128 'formatters': {
1129 'form1' : {
1130 'format' : '%(levelname)s ++ %(message)s',
1131 },
1132 },
1133 'handlers' : {
1134 'hand1' : {
1135 'class' : 'logging.StreamHandler',
1136 'formatter' : 'form1',
1137 'level' : 'NOTSET',
1138 'stream' : 'ext://sys.stdout',
1139 },
1140 },
1141 'root' : {
1142 'level' : 'WARNING',
1143 'handlers' : ['hand1'],
1144 },
1145 }
1146
1147 # config1 adds a little to the standard configuration.
1148 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001149 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001150 'formatters': {
1151 'form1' : {
1152 'format' : '%(levelname)s ++ %(message)s',
1153 },
1154 },
1155 'handlers' : {
1156 'hand1' : {
1157 'class' : 'logging.StreamHandler',
1158 'formatter' : 'form1',
1159 'level' : 'NOTSET',
1160 'stream' : 'ext://sys.stdout',
1161 },
1162 },
1163 'loggers' : {
1164 'compiler.parser' : {
1165 'level' : 'DEBUG',
1166 'handlers' : ['hand1'],
1167 },
1168 },
1169 'root' : {
1170 'level' : 'WARNING',
1171 },
1172 }
1173
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001174 # config1a moves the handler to the root. Used with config8a
1175 config1a = {
1176 'version': 1,
1177 'formatters': {
1178 'form1' : {
1179 'format' : '%(levelname)s ++ %(message)s',
1180 },
1181 },
1182 'handlers' : {
1183 'hand1' : {
1184 'class' : 'logging.StreamHandler',
1185 'formatter' : 'form1',
1186 'level' : 'NOTSET',
1187 'stream' : 'ext://sys.stdout',
1188 },
1189 },
1190 'loggers' : {
1191 'compiler.parser' : {
1192 'level' : 'DEBUG',
1193 },
1194 },
1195 'root' : {
1196 'level' : 'WARNING',
1197 'handlers' : ['hand1'],
1198 },
1199 }
1200
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001201 # config2 has a subtle configuration error that should be reported
1202 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001203 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001204 'formatters': {
1205 'form1' : {
1206 'format' : '%(levelname)s ++ %(message)s',
1207 },
1208 },
1209 'handlers' : {
1210 'hand1' : {
1211 'class' : 'logging.StreamHandler',
1212 'formatter' : 'form1',
1213 'level' : 'NOTSET',
1214 'stream' : 'ext://sys.stdbout',
1215 },
1216 },
1217 'loggers' : {
1218 'compiler.parser' : {
1219 'level' : 'DEBUG',
1220 'handlers' : ['hand1'],
1221 },
1222 },
1223 'root' : {
1224 'level' : 'WARNING',
1225 },
1226 }
1227
1228 #As config1 but with a misspelt level on a handler
1229 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001230 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001231 'formatters': {
1232 'form1' : {
1233 'format' : '%(levelname)s ++ %(message)s',
1234 },
1235 },
1236 'handlers' : {
1237 'hand1' : {
1238 'class' : 'logging.StreamHandler',
1239 'formatter' : 'form1',
1240 'level' : 'NTOSET',
1241 'stream' : 'ext://sys.stdout',
1242 },
1243 },
1244 'loggers' : {
1245 'compiler.parser' : {
1246 'level' : 'DEBUG',
1247 'handlers' : ['hand1'],
1248 },
1249 },
1250 'root' : {
1251 'level' : 'WARNING',
1252 },
1253 }
1254
1255
1256 #As config1 but with a misspelt level on a logger
1257 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001258 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001259 'formatters': {
1260 'form1' : {
1261 'format' : '%(levelname)s ++ %(message)s',
1262 },
1263 },
1264 'handlers' : {
1265 'hand1' : {
1266 'class' : 'logging.StreamHandler',
1267 'formatter' : 'form1',
1268 'level' : 'NOTSET',
1269 'stream' : 'ext://sys.stdout',
1270 },
1271 },
1272 'loggers' : {
1273 'compiler.parser' : {
1274 'level' : 'DEBUG',
1275 'handlers' : ['hand1'],
1276 },
1277 },
1278 'root' : {
1279 'level' : 'WRANING',
1280 },
1281 }
1282
1283 # config3 has a less subtle configuration error
1284 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001285 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001286 'formatters': {
1287 'form1' : {
1288 'format' : '%(levelname)s ++ %(message)s',
1289 },
1290 },
1291 'handlers' : {
1292 'hand1' : {
1293 'class' : 'logging.StreamHandler',
1294 'formatter' : 'misspelled_name',
1295 'level' : 'NOTSET',
1296 'stream' : 'ext://sys.stdout',
1297 },
1298 },
1299 'loggers' : {
1300 'compiler.parser' : {
1301 'level' : 'DEBUG',
1302 'handlers' : ['hand1'],
1303 },
1304 },
1305 'root' : {
1306 'level' : 'WARNING',
1307 },
1308 }
1309
1310 # config4 specifies a custom formatter class to be loaded
1311 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001312 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001313 'formatters': {
1314 'form1' : {
1315 '()' : __name__ + '.ExceptionFormatter',
1316 'format' : '%(levelname)s:%(name)s:%(message)s',
1317 },
1318 },
1319 'handlers' : {
1320 'hand1' : {
1321 'class' : 'logging.StreamHandler',
1322 'formatter' : 'form1',
1323 'level' : 'NOTSET',
1324 'stream' : 'ext://sys.stdout',
1325 },
1326 },
1327 'root' : {
1328 'level' : 'NOTSET',
1329 'handlers' : ['hand1'],
1330 },
1331 }
1332
1333 # As config4 but using an actual callable rather than a string
1334 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001335 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001336 'formatters': {
1337 'form1' : {
1338 '()' : ExceptionFormatter,
1339 'format' : '%(levelname)s:%(name)s:%(message)s',
1340 },
1341 'form2' : {
1342 '()' : __name__ + '.formatFunc',
1343 'format' : '%(levelname)s:%(name)s:%(message)s',
1344 },
1345 'form3' : {
1346 '()' : formatFunc,
1347 'format' : '%(levelname)s:%(name)s:%(message)s',
1348 },
1349 },
1350 'handlers' : {
1351 'hand1' : {
1352 'class' : 'logging.StreamHandler',
1353 'formatter' : 'form1',
1354 'level' : 'NOTSET',
1355 'stream' : 'ext://sys.stdout',
1356 },
1357 'hand2' : {
1358 '()' : handlerFunc,
1359 },
1360 },
1361 'root' : {
1362 'level' : 'NOTSET',
1363 'handlers' : ['hand1'],
1364 },
1365 }
1366
1367 # config5 specifies a custom handler class to be loaded
1368 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001369 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001370 'formatters': {
1371 'form1' : {
1372 'format' : '%(levelname)s ++ %(message)s',
1373 },
1374 },
1375 'handlers' : {
1376 'hand1' : {
1377 'class' : __name__ + '.CustomHandler',
1378 'formatter' : 'form1',
1379 'level' : 'NOTSET',
1380 'stream' : 'ext://sys.stdout',
1381 },
1382 },
1383 'loggers' : {
1384 'compiler.parser' : {
1385 'level' : 'DEBUG',
1386 'handlers' : ['hand1'],
1387 },
1388 },
1389 'root' : {
1390 'level' : 'WARNING',
1391 },
1392 }
1393
1394 # config6 specifies a custom handler class to be loaded
1395 # but has bad arguments
1396 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001397 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001398 'formatters': {
1399 'form1' : {
1400 'format' : '%(levelname)s ++ %(message)s',
1401 },
1402 },
1403 'handlers' : {
1404 'hand1' : {
1405 'class' : __name__ + '.CustomHandler',
1406 'formatter' : 'form1',
1407 'level' : 'NOTSET',
1408 'stream' : 'ext://sys.stdout',
1409 '9' : 'invalid parameter name',
1410 },
1411 },
1412 'loggers' : {
1413 'compiler.parser' : {
1414 'level' : 'DEBUG',
1415 'handlers' : ['hand1'],
1416 },
1417 },
1418 'root' : {
1419 'level' : 'WARNING',
1420 },
1421 }
1422
1423 #config 7 does not define compiler.parser but defines compiler.lexer
1424 #so compiler.parser should be disabled after applying it
1425 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001426 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001427 'formatters': {
1428 'form1' : {
1429 'format' : '%(levelname)s ++ %(message)s',
1430 },
1431 },
1432 'handlers' : {
1433 'hand1' : {
1434 'class' : 'logging.StreamHandler',
1435 'formatter' : 'form1',
1436 'level' : 'NOTSET',
1437 'stream' : 'ext://sys.stdout',
1438 },
1439 },
1440 'loggers' : {
1441 'compiler.lexer' : {
1442 'level' : 'DEBUG',
1443 'handlers' : ['hand1'],
1444 },
1445 },
1446 'root' : {
1447 'level' : 'WARNING',
1448 },
1449 }
1450
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001451 # config8 defines both compiler and compiler.lexer
1452 # so compiler.parser should not be disabled (since
1453 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001454 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001455 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001456 'disable_existing_loggers' : False,
1457 'formatters': {
1458 'form1' : {
1459 'format' : '%(levelname)s ++ %(message)s',
1460 },
1461 },
1462 'handlers' : {
1463 'hand1' : {
1464 'class' : 'logging.StreamHandler',
1465 'formatter' : 'form1',
1466 'level' : 'NOTSET',
1467 'stream' : 'ext://sys.stdout',
1468 },
1469 },
1470 'loggers' : {
1471 'compiler' : {
1472 'level' : 'DEBUG',
1473 'handlers' : ['hand1'],
1474 },
1475 'compiler.lexer' : {
1476 },
1477 },
1478 'root' : {
1479 'level' : 'WARNING',
1480 },
1481 }
1482
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001483 # config8a disables existing loggers
1484 config8a = {
1485 'version': 1,
1486 'disable_existing_loggers' : True,
1487 'formatters': {
1488 'form1' : {
1489 'format' : '%(levelname)s ++ %(message)s',
1490 },
1491 },
1492 'handlers' : {
1493 'hand1' : {
1494 'class' : 'logging.StreamHandler',
1495 'formatter' : 'form1',
1496 'level' : 'NOTSET',
1497 'stream' : 'ext://sys.stdout',
1498 },
1499 },
1500 'loggers' : {
1501 'compiler' : {
1502 'level' : 'DEBUG',
1503 'handlers' : ['hand1'],
1504 },
1505 'compiler.lexer' : {
1506 },
1507 },
1508 'root' : {
1509 'level' : 'WARNING',
1510 },
1511 }
1512
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001513 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001514 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001515 'formatters': {
1516 'form1' : {
1517 'format' : '%(levelname)s ++ %(message)s',
1518 },
1519 },
1520 'handlers' : {
1521 'hand1' : {
1522 'class' : 'logging.StreamHandler',
1523 'formatter' : 'form1',
1524 'level' : 'WARNING',
1525 'stream' : 'ext://sys.stdout',
1526 },
1527 },
1528 'loggers' : {
1529 'compiler.parser' : {
1530 'level' : 'WARNING',
1531 'handlers' : ['hand1'],
1532 },
1533 },
1534 'root' : {
1535 'level' : 'NOTSET',
1536 },
1537 }
1538
1539 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001540 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001541 'incremental' : True,
1542 'handlers' : {
1543 'hand1' : {
1544 'level' : 'WARNING',
1545 },
1546 },
1547 'loggers' : {
1548 'compiler.parser' : {
1549 'level' : 'INFO',
1550 },
1551 },
1552 }
1553
1554 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001555 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001556 'incremental' : True,
1557 'handlers' : {
1558 'hand1' : {
1559 'level' : 'INFO',
1560 },
1561 },
1562 'loggers' : {
1563 'compiler.parser' : {
1564 'level' : 'INFO',
1565 },
1566 },
1567 }
1568
1569 #As config1 but with a filter added
1570 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001571 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001572 'formatters': {
1573 'form1' : {
1574 'format' : '%(levelname)s ++ %(message)s',
1575 },
1576 },
1577 'filters' : {
1578 'filt1' : {
1579 'name' : 'compiler.parser',
1580 },
1581 },
1582 'handlers' : {
1583 'hand1' : {
1584 'class' : 'logging.StreamHandler',
1585 'formatter' : 'form1',
1586 'level' : 'NOTSET',
1587 'stream' : 'ext://sys.stdout',
1588 'filters' : ['filt1'],
1589 },
1590 },
1591 'loggers' : {
1592 'compiler.parser' : {
1593 'level' : 'DEBUG',
1594 'filters' : ['filt1'],
1595 },
1596 },
1597 'root' : {
1598 'level' : 'WARNING',
1599 'handlers' : ['hand1'],
1600 },
1601 }
1602
1603 #As config1 but using cfg:// references
1604 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001605 'version': 1,
1606 'true_formatters': {
1607 'form1' : {
1608 'format' : '%(levelname)s ++ %(message)s',
1609 },
1610 },
1611 'handler_configs': {
1612 'hand1' : {
1613 'class' : 'logging.StreamHandler',
1614 'formatter' : 'form1',
1615 'level' : 'NOTSET',
1616 'stream' : 'ext://sys.stdout',
1617 },
1618 },
1619 'formatters' : 'cfg://true_formatters',
1620 'handlers' : {
1621 'hand1' : 'cfg://handler_configs[hand1]',
1622 },
1623 'loggers' : {
1624 'compiler.parser' : {
1625 'level' : 'DEBUG',
1626 'handlers' : ['hand1'],
1627 },
1628 },
1629 'root' : {
1630 'level' : 'WARNING',
1631 },
1632 }
1633
1634 #As config11 but missing the version key
1635 config12 = {
1636 'true_formatters': {
1637 'form1' : {
1638 'format' : '%(levelname)s ++ %(message)s',
1639 },
1640 },
1641 'handler_configs': {
1642 'hand1' : {
1643 'class' : 'logging.StreamHandler',
1644 'formatter' : 'form1',
1645 'level' : 'NOTSET',
1646 'stream' : 'ext://sys.stdout',
1647 },
1648 },
1649 'formatters' : 'cfg://true_formatters',
1650 'handlers' : {
1651 'hand1' : 'cfg://handler_configs[hand1]',
1652 },
1653 'loggers' : {
1654 'compiler.parser' : {
1655 'level' : 'DEBUG',
1656 'handlers' : ['hand1'],
1657 },
1658 },
1659 'root' : {
1660 'level' : 'WARNING',
1661 },
1662 }
1663
1664 #As config11 but using an unsupported version
1665 config13 = {
1666 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001667 'true_formatters': {
1668 'form1' : {
1669 'format' : '%(levelname)s ++ %(message)s',
1670 },
1671 },
1672 'handler_configs': {
1673 'hand1' : {
1674 'class' : 'logging.StreamHandler',
1675 'formatter' : 'form1',
1676 'level' : 'NOTSET',
1677 'stream' : 'ext://sys.stdout',
1678 },
1679 },
1680 'formatters' : 'cfg://true_formatters',
1681 'handlers' : {
1682 'hand1' : 'cfg://handler_configs[hand1]',
1683 },
1684 'loggers' : {
1685 'compiler.parser' : {
1686 'level' : 'DEBUG',
1687 'handlers' : ['hand1'],
1688 },
1689 },
1690 'root' : {
1691 'level' : 'WARNING',
1692 },
1693 }
1694
1695 def apply_config(self, conf):
1696 logging.config.dictConfig(conf)
1697
1698 def test_config0_ok(self):
1699 # A simple config which overrides the default settings.
1700 with captured_stdout() as output:
1701 self.apply_config(self.config0)
1702 logger = logging.getLogger()
1703 # Won't output anything
1704 logger.info(self.next_message())
1705 # Outputs a message
1706 logger.error(self.next_message())
1707 self.assert_log_lines([
1708 ('ERROR', '2'),
1709 ], stream=output)
1710 # Original logger output is empty.
1711 self.assert_log_lines([])
1712
1713 def test_config1_ok(self, config=config1):
1714 # A config defining a sub-parser as well.
1715 with captured_stdout() as output:
1716 self.apply_config(config)
1717 logger = logging.getLogger("compiler.parser")
1718 # Both will output a message
1719 logger.info(self.next_message())
1720 logger.error(self.next_message())
1721 self.assert_log_lines([
1722 ('INFO', '1'),
1723 ('ERROR', '2'),
1724 ], stream=output)
1725 # Original logger output is empty.
1726 self.assert_log_lines([])
1727
1728 def test_config2_failure(self):
1729 # A simple config which overrides the default settings.
1730 self.assertRaises(Exception, self.apply_config, self.config2)
1731
1732 def test_config2a_failure(self):
1733 # A simple config which overrides the default settings.
1734 self.assertRaises(Exception, self.apply_config, self.config2a)
1735
1736 def test_config2b_failure(self):
1737 # A simple config which overrides the default settings.
1738 self.assertRaises(Exception, self.apply_config, self.config2b)
1739
1740 def test_config3_failure(self):
1741 # A simple config which overrides the default settings.
1742 self.assertRaises(Exception, self.apply_config, self.config3)
1743
1744 def test_config4_ok(self):
1745 # A config specifying a custom formatter class.
1746 with captured_stdout() as output:
1747 self.apply_config(self.config4)
1748 #logger = logging.getLogger()
1749 try:
1750 raise RuntimeError()
1751 except RuntimeError:
1752 logging.exception("just testing")
1753 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001754 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001755 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1756 # Original logger output is empty
1757 self.assert_log_lines([])
1758
1759 def test_config4a_ok(self):
1760 # A config specifying a custom formatter class.
1761 with captured_stdout() as output:
1762 self.apply_config(self.config4a)
1763 #logger = logging.getLogger()
1764 try:
1765 raise RuntimeError()
1766 except RuntimeError:
1767 logging.exception("just testing")
1768 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001769 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001770 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1771 # Original logger output is empty
1772 self.assert_log_lines([])
1773
1774 def test_config5_ok(self):
1775 self.test_config1_ok(config=self.config5)
1776
1777 def test_config6_failure(self):
1778 self.assertRaises(Exception, self.apply_config, self.config6)
1779
1780 def test_config7_ok(self):
1781 with captured_stdout() as output:
1782 self.apply_config(self.config1)
1783 logger = logging.getLogger("compiler.parser")
1784 # Both will output a message
1785 logger.info(self.next_message())
1786 logger.error(self.next_message())
1787 self.assert_log_lines([
1788 ('INFO', '1'),
1789 ('ERROR', '2'),
1790 ], stream=output)
1791 # Original logger output is empty.
1792 self.assert_log_lines([])
1793 with captured_stdout() as output:
1794 self.apply_config(self.config7)
1795 logger = logging.getLogger("compiler.parser")
1796 self.assertTrue(logger.disabled)
1797 logger = logging.getLogger("compiler.lexer")
1798 # Both will output a message
1799 logger.info(self.next_message())
1800 logger.error(self.next_message())
1801 self.assert_log_lines([
1802 ('INFO', '3'),
1803 ('ERROR', '4'),
1804 ], stream=output)
1805 # Original logger output is empty.
1806 self.assert_log_lines([])
1807
1808 #Same as test_config_7_ok but don't disable old loggers.
1809 def test_config_8_ok(self):
1810 with captured_stdout() as output:
1811 self.apply_config(self.config1)
1812 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001813 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001814 logger.info(self.next_message())
1815 logger.error(self.next_message())
1816 self.assert_log_lines([
1817 ('INFO', '1'),
1818 ('ERROR', '2'),
1819 ], stream=output)
1820 # Original logger output is empty.
1821 self.assert_log_lines([])
1822 with captured_stdout() as output:
1823 self.apply_config(self.config8)
1824 logger = logging.getLogger("compiler.parser")
1825 self.assertFalse(logger.disabled)
1826 # Both will output a message
1827 logger.info(self.next_message())
1828 logger.error(self.next_message())
1829 logger = logging.getLogger("compiler.lexer")
1830 # Both will output a message
1831 logger.info(self.next_message())
1832 logger.error(self.next_message())
1833 self.assert_log_lines([
1834 ('INFO', '3'),
1835 ('ERROR', '4'),
1836 ('INFO', '5'),
1837 ('ERROR', '6'),
1838 ], stream=output)
1839 # Original logger output is empty.
1840 self.assert_log_lines([])
1841
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001842 def test_config_8a_ok(self):
1843 with captured_stdout() as output:
1844 self.apply_config(self.config1a)
1845 logger = logging.getLogger("compiler.parser")
1846 # See issue #11424. compiler-hyphenated sorts
1847 # between compiler and compiler.xyz and this
1848 # was preventing compiler.xyz from being included
1849 # in the child loggers of compiler because of an
1850 # overzealous loop termination condition.
1851 hyphenated = logging.getLogger('compiler-hyphenated')
1852 # All will output a message
1853 logger.info(self.next_message())
1854 logger.error(self.next_message())
1855 hyphenated.critical(self.next_message())
1856 self.assert_log_lines([
1857 ('INFO', '1'),
1858 ('ERROR', '2'),
1859 ('CRITICAL', '3'),
1860 ], stream=output)
1861 # Original logger output is empty.
1862 self.assert_log_lines([])
1863 with captured_stdout() as output:
1864 self.apply_config(self.config8a)
1865 logger = logging.getLogger("compiler.parser")
1866 self.assertFalse(logger.disabled)
1867 # Both will output a message
1868 logger.info(self.next_message())
1869 logger.error(self.next_message())
1870 logger = logging.getLogger("compiler.lexer")
1871 # Both will output a message
1872 logger.info(self.next_message())
1873 logger.error(self.next_message())
1874 # Will not appear
1875 hyphenated.critical(self.next_message())
1876 self.assert_log_lines([
1877 ('INFO', '4'),
1878 ('ERROR', '5'),
1879 ('INFO', '6'),
1880 ('ERROR', '7'),
1881 ], stream=output)
1882 # Original logger output is empty.
1883 self.assert_log_lines([])
1884
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001885 def test_config_9_ok(self):
1886 with captured_stdout() as output:
1887 self.apply_config(self.config9)
1888 logger = logging.getLogger("compiler.parser")
1889 #Nothing will be output since both handler and logger are set to WARNING
1890 logger.info(self.next_message())
1891 self.assert_log_lines([], stream=output)
1892 self.apply_config(self.config9a)
1893 #Nothing will be output since both handler is still set to WARNING
1894 logger.info(self.next_message())
1895 self.assert_log_lines([], stream=output)
1896 self.apply_config(self.config9b)
1897 #Message should now be output
1898 logger.info(self.next_message())
1899 self.assert_log_lines([
1900 ('INFO', '3'),
1901 ], stream=output)
1902
1903 def test_config_10_ok(self):
1904 with captured_stdout() as output:
1905 self.apply_config(self.config10)
1906 logger = logging.getLogger("compiler.parser")
1907 logger.warning(self.next_message())
1908 logger = logging.getLogger('compiler')
1909 #Not output, because filtered
1910 logger.warning(self.next_message())
1911 logger = logging.getLogger('compiler.lexer')
1912 #Not output, because filtered
1913 logger.warning(self.next_message())
1914 logger = logging.getLogger("compiler.parser.codegen")
1915 #Output, as not filtered
1916 logger.error(self.next_message())
1917 self.assert_log_lines([
1918 ('WARNING', '1'),
1919 ('ERROR', '4'),
1920 ], stream=output)
1921
1922 def test_config11_ok(self):
1923 self.test_config1_ok(self.config11)
1924
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001925 def test_config12_failure(self):
1926 self.assertRaises(Exception, self.apply_config, self.config12)
1927
1928 def test_config13_failure(self):
1929 self.assertRaises(Exception, self.apply_config, self.config13)
1930
Victor Stinner45df8202010-04-28 22:31:17 +00001931 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001932 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001933 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001934 # Ask for a randomly assigned port (by using port 0)
1935 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001936 t.start()
1937 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001938 # Now get the port allocated
1939 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001940 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001941 try:
1942 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1943 sock.settimeout(2.0)
1944 sock.connect(('localhost', port))
1945
1946 slen = struct.pack('>L', len(text))
1947 s = slen + text
1948 sentsofar = 0
1949 left = len(s)
1950 while left > 0:
1951 sent = sock.send(s[sentsofar:])
1952 sentsofar += sent
1953 left -= sent
1954 sock.close()
1955 finally:
1956 t.ready.wait(2.0)
1957 logging.config.stopListening()
1958 t.join(2.0)
1959
1960 def test_listen_config_10_ok(self):
1961 with captured_stdout() as output:
1962 self.setup_via_listener(json.dumps(self.config10))
1963 logger = logging.getLogger("compiler.parser")
1964 logger.warning(self.next_message())
1965 logger = logging.getLogger('compiler')
1966 #Not output, because filtered
1967 logger.warning(self.next_message())
1968 logger = logging.getLogger('compiler.lexer')
1969 #Not output, because filtered
1970 logger.warning(self.next_message())
1971 logger = logging.getLogger("compiler.parser.codegen")
1972 #Output, as not filtered
1973 logger.error(self.next_message())
1974 self.assert_log_lines([
1975 ('WARNING', '1'),
1976 ('ERROR', '4'),
1977 ], stream=output)
1978
1979 def test_listen_config_1_ok(self):
1980 with captured_stdout() as output:
1981 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1982 logger = logging.getLogger("compiler.parser")
1983 # Both will output a message
1984 logger.info(self.next_message())
1985 logger.error(self.next_message())
1986 self.assert_log_lines([
1987 ('INFO', '1'),
1988 ('ERROR', '2'),
1989 ], stream=output)
1990 # Original logger output is empty.
1991 self.assert_log_lines([])
1992
1993
1994class ManagerTest(BaseTest):
1995 def test_manager_loggerclass(self):
1996 logged = []
1997
1998 class MyLogger(logging.Logger):
1999 def _log(self, level, msg, args, exc_info=None, extra=None):
2000 logged.append(msg)
2001
2002 man = logging.Manager(None)
2003 self.assertRaises(TypeError, man.setLoggerClass, int)
2004 man.setLoggerClass(MyLogger)
2005 logger = man.getLogger('test')
2006 logger.warning('should appear in logged')
2007 logging.warning('should not appear in logged')
2008
2009 self.assertEqual(logged, ['should appear in logged'])
2010
2011
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002012class ChildLoggerTest(BaseTest):
2013 def test_child_loggers(self):
2014 r = logging.getLogger()
2015 l1 = logging.getLogger('abc')
2016 l2 = logging.getLogger('def.ghi')
2017 c1 = r.getChild('xyz')
2018 c2 = r.getChild('uvw.xyz')
2019 self.assertTrue(c1 is logging.getLogger('xyz'))
2020 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2021 c1 = l1.getChild('def')
2022 c2 = c1.getChild('ghi')
2023 c3 = l1.getChild('def.ghi')
2024 self.assertTrue(c1 is logging.getLogger('abc.def'))
2025 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2026 self.assertTrue(c2 is c3)
2027
2028
Vinay Sajip6fac8172010-10-19 20:44:14 +00002029class DerivedLogRecord(logging.LogRecord):
2030 pass
2031
Vinay Sajip61561522010-12-03 11:50:38 +00002032class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002033
2034 def setUp(self):
2035 class CheckingFilter(logging.Filter):
2036 def __init__(self, cls):
2037 self.cls = cls
2038
2039 def filter(self, record):
2040 t = type(record)
2041 if t is not self.cls:
2042 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2043 self.cls)
2044 raise TypeError(msg)
2045 return True
2046
2047 BaseTest.setUp(self)
2048 self.filter = CheckingFilter(DerivedLogRecord)
2049 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002050 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002051
2052 def tearDown(self):
2053 self.root_logger.removeFilter(self.filter)
2054 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002055 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002056
2057 def test_logrecord_class(self):
2058 self.assertRaises(TypeError, self.root_logger.warning,
2059 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002060 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002061 self.root_logger.error(self.next_message())
2062 self.assert_log_lines([
2063 ('root', 'ERROR', '2'),
2064 ])
2065
2066
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002067class QueueHandlerTest(BaseTest):
2068 # Do not bother with a logger name group.
2069 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2070
2071 def setUp(self):
2072 BaseTest.setUp(self)
2073 self.queue = queue.Queue(-1)
2074 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2075 self.que_logger = logging.getLogger('que')
2076 self.que_logger.propagate = False
2077 self.que_logger.setLevel(logging.WARNING)
2078 self.que_logger.addHandler(self.que_hdlr)
2079
2080 def tearDown(self):
2081 self.que_hdlr.close()
2082 BaseTest.tearDown(self)
2083
2084 def test_queue_handler(self):
2085 self.que_logger.debug(self.next_message())
2086 self.assertRaises(queue.Empty, self.queue.get_nowait)
2087 self.que_logger.info(self.next_message())
2088 self.assertRaises(queue.Empty, self.queue.get_nowait)
2089 msg = self.next_message()
2090 self.que_logger.warning(msg)
2091 data = self.queue.get_nowait()
2092 self.assertTrue(isinstance(data, logging.LogRecord))
2093 self.assertEqual(data.name, self.que_logger.name)
2094 self.assertEqual((data.msg, data.args), (msg, None))
2095
Vinay Sajipe723e962011-04-15 22:27:17 +01002096 def test_queue_listener(self):
2097 handler = TestHandler(Matcher())
2098 listener = logging.handlers.QueueListener(self.queue, handler)
2099 listener.start()
2100 try:
2101 self.que_logger.warning(self.next_message())
2102 self.que_logger.error(self.next_message())
2103 self.que_logger.critical(self.next_message())
2104 finally:
2105 listener.stop()
2106 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2107 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2108 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2109
2110
Vinay Sajipa39c5712010-10-25 13:57:39 +00002111class FormatterTest(unittest.TestCase):
2112 def setUp(self):
2113 self.common = {
2114 'name': 'formatter.test',
2115 'level': logging.DEBUG,
2116 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2117 'lineno': 42,
2118 'exc_info': None,
2119 'func': None,
2120 'msg': 'Message with %d %s',
2121 'args': (2, 'placeholders'),
2122 }
2123 self.variants = {
2124 }
2125
2126 def get_record(self, name=None):
2127 result = dict(self.common)
2128 if name is not None:
2129 result.update(self.variants[name])
2130 return logging.makeLogRecord(result)
2131
2132 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002133 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002134 r = self.get_record()
2135 f = logging.Formatter('${%(message)s}')
2136 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2137 f = logging.Formatter('%(random)s')
2138 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002139 self.assertFalse(f.usesTime())
2140 f = logging.Formatter('%(asctime)s')
2141 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002142 f = logging.Formatter('%(asctime)-15s')
2143 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002144 f = logging.Formatter('asctime')
2145 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002146
2147 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002148 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002149 r = self.get_record()
2150 f = logging.Formatter('$%{message}%$', style='{')
2151 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2152 f = logging.Formatter('{random}', style='{')
2153 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002154 self.assertFalse(f.usesTime())
2155 f = logging.Formatter('{asctime}', style='{')
2156 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002157 f = logging.Formatter('{asctime!s:15}', style='{')
2158 self.assertTrue(f.usesTime())
2159 f = logging.Formatter('{asctime:15}', style='{')
2160 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002161 f = logging.Formatter('asctime', style='{')
2162 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002163
2164 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002165 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002166 r = self.get_record()
2167 f = logging.Formatter('$message', style='$')
2168 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2169 f = logging.Formatter('$$%${message}%$$', style='$')
2170 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2171 f = logging.Formatter('${random}', style='$')
2172 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002173 self.assertFalse(f.usesTime())
2174 f = logging.Formatter('${asctime}', style='$')
2175 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002176 f = logging.Formatter('${asctime', style='$')
2177 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002178 f = logging.Formatter('$asctime', style='$')
2179 self.assertTrue(f.usesTime())
2180 f = logging.Formatter('asctime', style='$')
2181 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002182
Vinay Sajip5a27d402010-12-10 11:42:57 +00002183class LastResortTest(BaseTest):
2184 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002185 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002186 root = self.root_logger
2187 root.removeHandler(self.root_hdlr)
2188 old_stderr = sys.stderr
2189 old_lastresort = logging.lastResort
2190 old_raise_exceptions = logging.raiseExceptions
2191 try:
2192 sys.stderr = sio = io.StringIO()
2193 root.warning('This is your final chance!')
2194 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2195 #No handlers and no last resort, so 'No handlers' message
2196 logging.lastResort = None
2197 sys.stderr = sio = io.StringIO()
2198 root.warning('This is your final chance!')
2199 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2200 # 'No handlers' message only printed once
2201 sys.stderr = sio = io.StringIO()
2202 root.warning('This is your final chance!')
2203 self.assertEqual(sio.getvalue(), '')
2204 root.manager.emittedNoHandlerWarning = False
2205 #If raiseExceptions is False, no message is printed
2206 logging.raiseExceptions = False
2207 sys.stderr = sio = io.StringIO()
2208 root.warning('This is your final chance!')
2209 self.assertEqual(sio.getvalue(), '')
2210 finally:
2211 sys.stderr = old_stderr
2212 root.addHandler(self.root_hdlr)
2213 logging.lastResort = old_lastresort
2214 logging.raiseExceptions = old_raise_exceptions
2215
2216
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002217class BaseFileTest(BaseTest):
2218 "Base class for handler tests that write log files"
2219
2220 def setUp(self):
2221 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002222 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2223 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002224 self.rmfiles = []
2225
2226 def tearDown(self):
2227 for fn in self.rmfiles:
2228 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002229 if os.path.exists(self.fn):
2230 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002231 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002232
2233 def assertLogFile(self, filename):
2234 "Assert a log file is there and register it for deletion"
2235 self.assertTrue(os.path.exists(filename),
2236 msg="Log file %r does not exist")
2237 self.rmfiles.append(filename)
2238
2239
2240class RotatingFileHandlerTest(BaseFileTest):
2241 def next_rec(self):
2242 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2243 self.next_message(), None, None, None)
2244
2245 def test_should_not_rollover(self):
2246 # If maxbytes is zero rollover never occurs
2247 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2248 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002249 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002250
2251 def test_should_rollover(self):
2252 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2253 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002254 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002255
2256 def test_file_created(self):
2257 # checks that the file is created and assumes it was created
2258 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002259 rh = logging.handlers.RotatingFileHandler(self.fn)
2260 rh.emit(self.next_rec())
2261 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002262 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002263
2264 def test_rollover_filenames(self):
2265 rh = logging.handlers.RotatingFileHandler(
2266 self.fn, backupCount=2, maxBytes=1)
2267 rh.emit(self.next_rec())
2268 self.assertLogFile(self.fn)
2269 rh.emit(self.next_rec())
2270 self.assertLogFile(self.fn + ".1")
2271 rh.emit(self.next_rec())
2272 self.assertLogFile(self.fn + ".2")
2273 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002274 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002275
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002276class TimedRotatingFileHandlerTest(BaseFileTest):
2277 # test methods added below
2278 pass
2279
2280def secs(**kw):
2281 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2282
2283for when, exp in (('S', 1),
2284 ('M', 60),
2285 ('H', 60 * 60),
2286 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002287 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002288 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002289 ('W0', secs(days=4, hours=24)),
2290 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002291 def test_compute_rollover(self, when=when, exp=exp):
2292 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002293 self.fn, when=when, interval=1, backupCount=0, utc=True)
2294 currentTime = 0.0
2295 actual = rh.computeRollover(currentTime)
2296 if exp != actual:
2297 # Failures occur on some systems for MIDNIGHT and W0.
2298 # Print detailed calculation for MIDNIGHT so we can try to see
2299 # what's going on
2300 import time
2301 if when == 'MIDNIGHT':
2302 try:
2303 if rh.utc:
2304 t = time.gmtime(currentTime)
2305 else:
2306 t = time.localtime(currentTime)
2307 currentHour = t[3]
2308 currentMinute = t[4]
2309 currentSecond = t[5]
2310 # r is the number of seconds left between now and midnight
2311 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2312 currentMinute) * 60 +
2313 currentSecond)
2314 result = currentTime + r
2315 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2316 print('currentHour: %s' % currentHour, file=sys.stderr)
2317 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2318 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2319 print('r: %s' % r, file=sys.stderr)
2320 print('result: %s' % result, file=sys.stderr)
2321 except Exception:
2322 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2323 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002324 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002325 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2326
Christian Heimes180510d2008-03-03 19:15:45 +00002327# Set the locale to the platform-dependent default. I have no idea
2328# why the test does this, but in any case we save the current locale
2329# first and restore it at the end.
2330@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002331def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002332 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002333 CustomLevelsAndFiltersTest, MemoryHandlerTest,
2334 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002335 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajipa39c5712010-10-25 13:57:39 +00002336 FormatterTest,
Vinay Sajip61561522010-12-03 11:50:38 +00002337 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002338 RotatingFileHandlerTest,
Vinay Sajip5a27d402010-12-10 11:42:57 +00002339 LastResortTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00002340 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00002341 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00002342
Christian Heimes180510d2008-03-03 19:15:45 +00002343if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002344 test_main()