blob: ab66596d083c65f84b1a401e9e3e61b90d3af9b7 [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
Vinay Sajip61b787e2011-11-07 08:53:03 +0000296 def test_invalid_name(self):
Vinay Sajip3bd56382011-11-07 10:15:08 +0000297 self.assertRaises(TypeError, logging.getLogger, any)
Christian Heimes180510d2008-03-03 19:15:45 +0000298
299class BasicFilterTest(BaseTest):
300
301 """Test the bundled Filter class."""
302
303 def test_filter(self):
304 # Only messages satisfying the specified criteria pass through the
305 # filter.
306 filter_ = logging.Filter("spam.eggs")
307 handler = self.root_logger.handlers[0]
308 try:
309 handler.addFilter(filter_)
310 spam = logging.getLogger("spam")
311 spam_eggs = logging.getLogger("spam.eggs")
312 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
313 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
314
315 spam.info(self.next_message())
316 spam_eggs.info(self.next_message()) # Good.
317 spam_eggs_fish.info(self.next_message()) # Good.
318 spam_bakedbeans.info(self.next_message())
319
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000320 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000321 ('spam.eggs', 'INFO', '2'),
322 ('spam.eggs.fish', 'INFO', '3'),
323 ])
324 finally:
325 handler.removeFilter(filter_)
326
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000327 def test_callable_filter(self):
328 # Only messages satisfying the specified criteria pass through the
329 # filter.
330
331 def filterfunc(record):
332 parts = record.name.split('.')
333 prefix = '.'.join(parts[:2])
334 return prefix == 'spam.eggs'
335
336 handler = self.root_logger.handlers[0]
337 try:
338 handler.addFilter(filterfunc)
339 spam = logging.getLogger("spam")
340 spam_eggs = logging.getLogger("spam.eggs")
341 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
342 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
343
344 spam.info(self.next_message())
345 spam_eggs.info(self.next_message()) # Good.
346 spam_eggs_fish.info(self.next_message()) # Good.
347 spam_bakedbeans.info(self.next_message())
348
349 self.assert_log_lines([
350 ('spam.eggs', 'INFO', '2'),
351 ('spam.eggs.fish', 'INFO', '3'),
352 ])
353 finally:
354 handler.removeFilter(filterfunc)
355
Christian Heimes180510d2008-03-03 19:15:45 +0000356
357#
358# First, we define our levels. There can be as many as you want - the only
359# limitations are that they should be integers, the lowest should be > 0 and
360# larger values mean less information being logged. If you need specific
361# level values which do not fit into these limitations, you can use a
362# mapping dictionary to convert between your application levels and the
363# logging system.
364#
365SILENT = 120
366TACITURN = 119
367TERSE = 118
368EFFUSIVE = 117
369SOCIABLE = 116
370VERBOSE = 115
371TALKATIVE = 114
372GARRULOUS = 113
373CHATTERBOX = 112
374BORING = 111
375
376LEVEL_RANGE = range(BORING, SILENT + 1)
377
378#
379# Next, we define names for our levels. You don't need to do this - in which
380# case the system will use "Level n" to denote the text for the level.
381#
382my_logging_levels = {
383 SILENT : 'Silent',
384 TACITURN : 'Taciturn',
385 TERSE : 'Terse',
386 EFFUSIVE : 'Effusive',
387 SOCIABLE : 'Sociable',
388 VERBOSE : 'Verbose',
389 TALKATIVE : 'Talkative',
390 GARRULOUS : 'Garrulous',
391 CHATTERBOX : 'Chatterbox',
392 BORING : 'Boring',
393}
394
395class GarrulousFilter(logging.Filter):
396
397 """A filter which blocks garrulous messages."""
398
399 def filter(self, record):
400 return record.levelno != GARRULOUS
401
402class VerySpecificFilter(logging.Filter):
403
404 """A filter which blocks sociable and taciturn messages."""
405
406 def filter(self, record):
407 return record.levelno not in [SOCIABLE, TACITURN]
408
409
410class CustomLevelsAndFiltersTest(BaseTest):
411
412 """Test various filtering possibilities with custom logging levels."""
413
414 # Skip the logger name group.
415 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
416
417 def setUp(self):
418 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000419 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000420 logging.addLevelName(k, v)
421
422 def log_at_all_levels(self, logger):
423 for lvl in LEVEL_RANGE:
424 logger.log(lvl, self.next_message())
425
426 def test_logger_filter(self):
427 # Filter at logger level.
428 self.root_logger.setLevel(VERBOSE)
429 # Levels >= 'Verbose' are good.
430 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000431 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000432 ('Verbose', '5'),
433 ('Sociable', '6'),
434 ('Effusive', '7'),
435 ('Terse', '8'),
436 ('Taciturn', '9'),
437 ('Silent', '10'),
438 ])
439
440 def test_handler_filter(self):
441 # Filter at handler level.
442 self.root_logger.handlers[0].setLevel(SOCIABLE)
443 try:
444 # Levels >= 'Sociable' are good.
445 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000446 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000447 ('Sociable', '6'),
448 ('Effusive', '7'),
449 ('Terse', '8'),
450 ('Taciturn', '9'),
451 ('Silent', '10'),
452 ])
453 finally:
454 self.root_logger.handlers[0].setLevel(logging.NOTSET)
455
456 def test_specific_filters(self):
457 # Set a specific filter object on the handler, and then add another
458 # filter object on the logger itself.
459 handler = self.root_logger.handlers[0]
460 specific_filter = None
461 garr = GarrulousFilter()
462 handler.addFilter(garr)
463 try:
464 self.log_at_all_levels(self.root_logger)
465 first_lines = [
466 # Notice how 'Garrulous' is missing
467 ('Boring', '1'),
468 ('Chatterbox', '2'),
469 ('Talkative', '4'),
470 ('Verbose', '5'),
471 ('Sociable', '6'),
472 ('Effusive', '7'),
473 ('Terse', '8'),
474 ('Taciturn', '9'),
475 ('Silent', '10'),
476 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000477 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000478
479 specific_filter = VerySpecificFilter()
480 self.root_logger.addFilter(specific_filter)
481 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000482 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000483 # Not only 'Garrulous' is still missing, but also 'Sociable'
484 # and 'Taciturn'
485 ('Boring', '11'),
486 ('Chatterbox', '12'),
487 ('Talkative', '14'),
488 ('Verbose', '15'),
489 ('Effusive', '17'),
490 ('Terse', '18'),
491 ('Silent', '20'),
492 ])
493 finally:
494 if specific_filter:
495 self.root_logger.removeFilter(specific_filter)
496 handler.removeFilter(garr)
497
498
499class MemoryHandlerTest(BaseTest):
500
501 """Tests for the MemoryHandler."""
502
503 # Do not bother with a logger name group.
504 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
505
506 def setUp(self):
507 BaseTest.setUp(self)
508 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
509 self.root_hdlr)
510 self.mem_logger = logging.getLogger('mem')
511 self.mem_logger.propagate = 0
512 self.mem_logger.addHandler(self.mem_hdlr)
513
514 def tearDown(self):
515 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000516 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000517
518 def test_flush(self):
519 # The memory handler flushes to its target handler based on specific
520 # criteria (message count and message level).
521 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000522 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000523 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000524 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000525 # This will flush because the level is >= logging.WARNING
526 self.mem_logger.warn(self.next_message())
527 lines = [
528 ('DEBUG', '1'),
529 ('INFO', '2'),
530 ('WARNING', '3'),
531 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000532 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000533 for n in (4, 14):
534 for i in range(9):
535 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000536 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000537 # This will flush because it's the 10th message since the last
538 # flush.
539 self.mem_logger.debug(self.next_message())
540 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000541 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000542
543 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000544 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000545
546
547class ExceptionFormatter(logging.Formatter):
548 """A special exception formatter."""
549 def formatException(self, ei):
550 return "Got a [%s]" % ei[0].__name__
551
552
553class ConfigFileTest(BaseTest):
554
555 """Reading logging config from a .ini-style config file."""
556
557 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
558
559 # config0 is a standard configuration.
560 config0 = """
561 [loggers]
562 keys=root
563
564 [handlers]
565 keys=hand1
566
567 [formatters]
568 keys=form1
569
570 [logger_root]
571 level=WARNING
572 handlers=hand1
573
574 [handler_hand1]
575 class=StreamHandler
576 level=NOTSET
577 formatter=form1
578 args=(sys.stdout,)
579
580 [formatter_form1]
581 format=%(levelname)s ++ %(message)s
582 datefmt=
583 """
584
585 # config1 adds a little to the standard configuration.
586 config1 = """
587 [loggers]
588 keys=root,parser
589
590 [handlers]
591 keys=hand1
592
593 [formatters]
594 keys=form1
595
596 [logger_root]
597 level=WARNING
598 handlers=
599
600 [logger_parser]
601 level=DEBUG
602 handlers=hand1
603 propagate=1
604 qualname=compiler.parser
605
606 [handler_hand1]
607 class=StreamHandler
608 level=NOTSET
609 formatter=form1
610 args=(sys.stdout,)
611
612 [formatter_form1]
613 format=%(levelname)s ++ %(message)s
614 datefmt=
615 """
616
Vinay Sajip3f84b072011-03-07 17:49:33 +0000617 # config1a moves the handler to the root.
618 config1a = """
619 [loggers]
620 keys=root,parser
621
622 [handlers]
623 keys=hand1
624
625 [formatters]
626 keys=form1
627
628 [logger_root]
629 level=WARNING
630 handlers=hand1
631
632 [logger_parser]
633 level=DEBUG
634 handlers=
635 propagate=1
636 qualname=compiler.parser
637
638 [handler_hand1]
639 class=StreamHandler
640 level=NOTSET
641 formatter=form1
642 args=(sys.stdout,)
643
644 [formatter_form1]
645 format=%(levelname)s ++ %(message)s
646 datefmt=
647 """
648
Christian Heimes180510d2008-03-03 19:15:45 +0000649 # config2 has a subtle configuration error that should be reported
650 config2 = config1.replace("sys.stdout", "sys.stbout")
651
652 # config3 has a less subtle configuration error
653 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
654
655 # config4 specifies a custom formatter class to be loaded
656 config4 = """
657 [loggers]
658 keys=root
659
660 [handlers]
661 keys=hand1
662
663 [formatters]
664 keys=form1
665
666 [logger_root]
667 level=NOTSET
668 handlers=hand1
669
670 [handler_hand1]
671 class=StreamHandler
672 level=NOTSET
673 formatter=form1
674 args=(sys.stdout,)
675
676 [formatter_form1]
677 class=""" + __name__ + """.ExceptionFormatter
678 format=%(levelname)s:%(name)s:%(message)s
679 datefmt=
680 """
681
Georg Brandl3dbca812008-07-23 16:10:53 +0000682 # config5 specifies a custom handler class to be loaded
683 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
684
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000685 # config6 uses ', ' delimiters in the handlers and formatters sections
686 config6 = """
687 [loggers]
688 keys=root,parser
689
690 [handlers]
691 keys=hand1, hand2
692
693 [formatters]
694 keys=form1, form2
695
696 [logger_root]
697 level=WARNING
698 handlers=
699
700 [logger_parser]
701 level=DEBUG
702 handlers=hand1
703 propagate=1
704 qualname=compiler.parser
705
706 [handler_hand1]
707 class=StreamHandler
708 level=NOTSET
709 formatter=form1
710 args=(sys.stdout,)
711
712 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000713 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000714 level=NOTSET
715 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000716 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000717
718 [formatter_form1]
719 format=%(levelname)s ++ %(message)s
720 datefmt=
721
722 [formatter_form2]
723 format=%(message)s
724 datefmt=
725 """
726
Vinay Sajip3f84b072011-03-07 17:49:33 +0000727 # config7 adds a compiler logger.
728 config7 = """
729 [loggers]
730 keys=root,parser,compiler
731
732 [handlers]
733 keys=hand1
734
735 [formatters]
736 keys=form1
737
738 [logger_root]
739 level=WARNING
740 handlers=hand1
741
742 [logger_compiler]
743 level=DEBUG
744 handlers=
745 propagate=1
746 qualname=compiler
747
748 [logger_parser]
749 level=DEBUG
750 handlers=
751 propagate=1
752 qualname=compiler.parser
753
754 [handler_hand1]
755 class=StreamHandler
756 level=NOTSET
757 formatter=form1
758 args=(sys.stdout,)
759
760 [formatter_form1]
761 format=%(levelname)s ++ %(message)s
762 datefmt=
763 """
764
Christian Heimes180510d2008-03-03 19:15:45 +0000765 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000766 file = io.StringIO(textwrap.dedent(conf))
767 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000768
769 def test_config0_ok(self):
770 # A simple config file which overrides the default settings.
771 with captured_stdout() as output:
772 self.apply_config(self.config0)
773 logger = logging.getLogger()
774 # Won't output anything
775 logger.info(self.next_message())
776 # Outputs a message
777 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000778 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000779 ('ERROR', '2'),
780 ], stream=output)
781 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000782 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000783
Georg Brandl3dbca812008-07-23 16:10:53 +0000784 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000785 # A config file defining a sub-parser as well.
786 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000787 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000788 logger = logging.getLogger("compiler.parser")
789 # Both will output a message
790 logger.info(self.next_message())
791 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000792 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000793 ('INFO', '1'),
794 ('ERROR', '2'),
795 ], stream=output)
796 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000797 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000798
799 def test_config2_failure(self):
800 # A simple config file which overrides the default settings.
801 self.assertRaises(Exception, self.apply_config, self.config2)
802
803 def test_config3_failure(self):
804 # A simple config file which overrides the default settings.
805 self.assertRaises(Exception, self.apply_config, self.config3)
806
807 def test_config4_ok(self):
808 # A config file specifying a custom formatter class.
809 with captured_stdout() as output:
810 self.apply_config(self.config4)
811 logger = logging.getLogger()
812 try:
813 raise RuntimeError()
814 except RuntimeError:
815 logging.exception("just testing")
816 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000817 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000818 "ERROR:root:just testing\nGot a [RuntimeError]\n")
819 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000820 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000821
Georg Brandl3dbca812008-07-23 16:10:53 +0000822 def test_config5_ok(self):
823 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000824
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000825 def test_config6_ok(self):
826 self.test_config1_ok(config=self.config6)
827
Vinay Sajip3f84b072011-03-07 17:49:33 +0000828 def test_config7_ok(self):
829 with captured_stdout() as output:
830 self.apply_config(self.config1a)
831 logger = logging.getLogger("compiler.parser")
832 # See issue #11424. compiler-hyphenated sorts
833 # between compiler and compiler.xyz and this
834 # was preventing compiler.xyz from being included
835 # in the child loggers of compiler because of an
836 # overzealous loop termination condition.
837 hyphenated = logging.getLogger('compiler-hyphenated')
838 # All will output a message
839 logger.info(self.next_message())
840 logger.error(self.next_message())
841 hyphenated.critical(self.next_message())
842 self.assert_log_lines([
843 ('INFO', '1'),
844 ('ERROR', '2'),
845 ('CRITICAL', '3'),
846 ], stream=output)
847 # Original logger output is empty.
848 self.assert_log_lines([])
849 with captured_stdout() as output:
850 self.apply_config(self.config7)
851 logger = logging.getLogger("compiler.parser")
852 self.assertFalse(logger.disabled)
853 # Both will output a message
854 logger.info(self.next_message())
855 logger.error(self.next_message())
856 logger = logging.getLogger("compiler.lexer")
857 # Both will output a message
858 logger.info(self.next_message())
859 logger.error(self.next_message())
860 # Will not appear
861 hyphenated.critical(self.next_message())
862 self.assert_log_lines([
863 ('INFO', '4'),
864 ('ERROR', '5'),
865 ('INFO', '6'),
866 ('ERROR', '7'),
867 ], stream=output)
868 # Original logger output is empty.
869 self.assert_log_lines([])
870
Christian Heimes180510d2008-03-03 19:15:45 +0000871class LogRecordStreamHandler(StreamRequestHandler):
872
873 """Handler for a streaming logging request. It saves the log message in the
874 TCP server's 'log_output' attribute."""
875
876 TCP_LOG_END = "!!!END!!!"
877
878 def handle(self):
879 """Handle multiple requests - each expected to be of 4-byte length,
880 followed by the LogRecord in pickle format. Logs the record
881 according to whatever policy is configured locally."""
882 while True:
883 chunk = self.connection.recv(4)
884 if len(chunk) < 4:
885 break
886 slen = struct.unpack(">L", chunk)[0]
887 chunk = self.connection.recv(slen)
888 while len(chunk) < slen:
889 chunk = chunk + self.connection.recv(slen - len(chunk))
890 obj = self.unpickle(chunk)
891 record = logging.makeLogRecord(obj)
892 self.handle_log_record(record)
893
894 def unpickle(self, data):
895 return pickle.loads(data)
896
897 def handle_log_record(self, record):
898 # If the end-of-messages sentinel is seen, tell the server to
899 # terminate.
900 if self.TCP_LOG_END in record.msg:
901 self.server.abort = 1
902 return
903 self.server.log_output += record.msg + "\n"
904
Guido van Rossum376e6362003-04-25 14:22:00 +0000905
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000906class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000907
908 """A simple-minded TCP socket-based logging receiver suitable for test
909 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000910
911 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000912 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000913
914 def __init__(self, host='localhost',
915 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
916 handler=LogRecordStreamHandler):
917 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +0000918 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +0000919 self.timeout = 0.1
920 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000921
922 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000923 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000924 rd, wr, ex = select.select([self.socket.fileno()], [], [],
925 self.timeout)
926 if rd:
927 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +0000928 # Notify the main thread that we're about to exit
929 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000930 # close the listen socket
931 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000932
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000933
Victor Stinner45df8202010-04-28 22:31:17 +0000934@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +0000935class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000936
Christian Heimes180510d2008-03-03 19:15:45 +0000937 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000938
Christian Heimes180510d2008-03-03 19:15:45 +0000939 def setUp(self):
940 """Set up a TCP server to receive log messages, and a SocketHandler
941 pointing to that server's address and port."""
942 BaseTest.setUp(self)
943 self.tcpserver = LogRecordSocketReceiver(port=0)
944 self.port = self.tcpserver.socket.getsockname()[1]
945 self.threads = [
946 threading.Thread(target=self.tcpserver.serve_until_stopped)]
947 for thread in self.threads:
948 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000949
Christian Heimes180510d2008-03-03 19:15:45 +0000950 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
951 self.sock_hdlr.setFormatter(self.root_formatter)
952 self.root_logger.removeHandler(self.root_logger.handlers[0])
953 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000954
Christian Heimes180510d2008-03-03 19:15:45 +0000955 def tearDown(self):
956 """Shutdown the TCP server."""
957 try:
958 self.tcpserver.abort = True
959 del self.tcpserver
960 self.root_logger.removeHandler(self.sock_hdlr)
961 self.sock_hdlr.close()
962 for thread in self.threads:
963 thread.join(2.0)
964 finally:
965 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000966
Christian Heimes180510d2008-03-03 19:15:45 +0000967 def get_output(self):
968 """Get the log output as received by the TCP server."""
969 # Signal the TCP receiver and wait for it to terminate.
970 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
971 self.tcpserver.finished.wait(2.0)
972 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000973
Christian Heimes180510d2008-03-03 19:15:45 +0000974 def test_output(self):
975 # The log message sent to the SocketHandler is properly received.
976 logger = logging.getLogger("tcp")
977 logger.error("spam")
978 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000979 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000980
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000981
Christian Heimes180510d2008-03-03 19:15:45 +0000982class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000983
Christian Heimes180510d2008-03-03 19:15:45 +0000984 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000985
Christian Heimes180510d2008-03-03 19:15:45 +0000986 def setUp(self):
987 """Create a dict to remember potentially destroyed objects."""
988 BaseTest.setUp(self)
989 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000990
Christian Heimes180510d2008-03-03 19:15:45 +0000991 def _watch_for_survival(self, *args):
992 """Watch the given objects for survival, by creating weakrefs to
993 them."""
994 for obj in args:
995 key = id(obj), repr(obj)
996 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000997
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000998 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +0000999 """Assert that all objects watched for survival have survived."""
1000 # Trigger cycle breaking.
1001 gc.collect()
1002 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001003 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001004 if ref() is None:
1005 dead.append(repr_)
1006 if dead:
1007 self.fail("%d objects should have survived "
1008 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001009
Christian Heimes180510d2008-03-03 19:15:45 +00001010 def test_persistent_loggers(self):
1011 # Logger objects are persistent and retain their configuration, even
1012 # if visible references are destroyed.
1013 self.root_logger.setLevel(logging.INFO)
1014 foo = logging.getLogger("foo")
1015 self._watch_for_survival(foo)
1016 foo.setLevel(logging.DEBUG)
1017 self.root_logger.debug(self.next_message())
1018 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001019 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001020 ('foo', 'DEBUG', '2'),
1021 ])
1022 del foo
1023 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001024 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001025 # foo has retained its settings.
1026 bar = logging.getLogger("foo")
1027 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001028 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001029 ('foo', 'DEBUG', '2'),
1030 ('foo', 'DEBUG', '3'),
1031 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001032
Benjamin Petersonf91df042009-02-13 02:50:59 +00001033
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001034class EncodingTest(BaseTest):
1035 def test_encoding_plain_file(self):
1036 # In Python 2.x, a plain file object is treated as having no encoding.
1037 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001038 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1039 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001040 # the non-ascii data we write to the log.
1041 data = "foo\x80"
1042 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001043 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001044 log.addHandler(handler)
1045 try:
1046 # write non-ascii data to the log.
1047 log.warning(data)
1048 finally:
1049 log.removeHandler(handler)
1050 handler.close()
1051 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001052 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001053 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001054 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001055 finally:
1056 f.close()
1057 finally:
1058 if os.path.isfile(fn):
1059 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001060
Benjamin Petersonf91df042009-02-13 02:50:59 +00001061 def test_encoding_cyrillic_unicode(self):
1062 log = logging.getLogger("test")
1063 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1064 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1065 #Ensure it's written in a Cyrillic encoding
1066 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001067 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001068 stream = io.BytesIO()
1069 writer = writer_class(stream, 'strict')
1070 handler = logging.StreamHandler(writer)
1071 log.addHandler(handler)
1072 try:
1073 log.warning(message)
1074 finally:
1075 log.removeHandler(handler)
1076 handler.close()
1077 # check we wrote exactly those bytes, ignoring trailing \n etc
1078 s = stream.getvalue()
1079 #Compare against what the data should be when encoded in CP-1251
1080 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1081
1082
Georg Brandlf9734072008-12-07 15:30:06 +00001083class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001084
Georg Brandlf9734072008-12-07 15:30:06 +00001085 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001086 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001087 logging.captureWarnings(True)
Brett Cannondf8709d2009-04-01 20:01:47 +00001088 try:
Brett Cannon5b9082a2009-04-05 18:57:32 +00001089 warnings.filterwarnings("always", category=UserWarning)
Brett Cannondf8709d2009-04-01 20:01:47 +00001090 file = io.StringIO()
1091 h = logging.StreamHandler(file)
1092 logger = logging.getLogger("py.warnings")
1093 logger.addHandler(h)
1094 warnings.warn("I'm warning you...")
1095 logger.removeHandler(h)
1096 s = file.getvalue()
1097 h.close()
1098 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001099
Brett Cannondf8709d2009-04-01 20:01:47 +00001100 #See if an explicit file uses the original implementation
1101 file = io.StringIO()
1102 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1103 file, "Dummy line")
1104 s = file.getvalue()
1105 file.close()
1106 self.assertEqual(s,
1107 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1108 finally:
1109 logging.captureWarnings(False)
Georg Brandlf9734072008-12-07 15:30:06 +00001110
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001111
1112def formatFunc(format, datefmt=None):
1113 return logging.Formatter(format, datefmt)
1114
1115def handlerFunc():
1116 return logging.StreamHandler()
1117
1118class CustomHandler(logging.StreamHandler):
1119 pass
1120
1121class ConfigDictTest(BaseTest):
1122
1123 """Reading logging config from a dictionary."""
1124
1125 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1126
1127 # config0 is a standard configuration.
1128 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001129 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001130 'formatters': {
1131 'form1' : {
1132 'format' : '%(levelname)s ++ %(message)s',
1133 },
1134 },
1135 'handlers' : {
1136 'hand1' : {
1137 'class' : 'logging.StreamHandler',
1138 'formatter' : 'form1',
1139 'level' : 'NOTSET',
1140 'stream' : 'ext://sys.stdout',
1141 },
1142 },
1143 'root' : {
1144 'level' : 'WARNING',
1145 'handlers' : ['hand1'],
1146 },
1147 }
1148
1149 # config1 adds a little to the standard configuration.
1150 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001151 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001152 'formatters': {
1153 'form1' : {
1154 'format' : '%(levelname)s ++ %(message)s',
1155 },
1156 },
1157 'handlers' : {
1158 'hand1' : {
1159 'class' : 'logging.StreamHandler',
1160 'formatter' : 'form1',
1161 'level' : 'NOTSET',
1162 'stream' : 'ext://sys.stdout',
1163 },
1164 },
1165 'loggers' : {
1166 'compiler.parser' : {
1167 'level' : 'DEBUG',
1168 'handlers' : ['hand1'],
1169 },
1170 },
1171 'root' : {
1172 'level' : 'WARNING',
1173 },
1174 }
1175
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001176 # config1a moves the handler to the root. Used with config8a
1177 config1a = {
1178 'version': 1,
1179 'formatters': {
1180 'form1' : {
1181 'format' : '%(levelname)s ++ %(message)s',
1182 },
1183 },
1184 'handlers' : {
1185 'hand1' : {
1186 'class' : 'logging.StreamHandler',
1187 'formatter' : 'form1',
1188 'level' : 'NOTSET',
1189 'stream' : 'ext://sys.stdout',
1190 },
1191 },
1192 'loggers' : {
1193 'compiler.parser' : {
1194 'level' : 'DEBUG',
1195 },
1196 },
1197 'root' : {
1198 'level' : 'WARNING',
1199 'handlers' : ['hand1'],
1200 },
1201 }
1202
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001203 # config2 has a subtle configuration error that should be reported
1204 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001205 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001206 'formatters': {
1207 'form1' : {
1208 'format' : '%(levelname)s ++ %(message)s',
1209 },
1210 },
1211 'handlers' : {
1212 'hand1' : {
1213 'class' : 'logging.StreamHandler',
1214 'formatter' : 'form1',
1215 'level' : 'NOTSET',
1216 'stream' : 'ext://sys.stdbout',
1217 },
1218 },
1219 'loggers' : {
1220 'compiler.parser' : {
1221 'level' : 'DEBUG',
1222 'handlers' : ['hand1'],
1223 },
1224 },
1225 'root' : {
1226 'level' : 'WARNING',
1227 },
1228 }
1229
1230 #As config1 but with a misspelt level on a handler
1231 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001232 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001233 'formatters': {
1234 'form1' : {
1235 'format' : '%(levelname)s ++ %(message)s',
1236 },
1237 },
1238 'handlers' : {
1239 'hand1' : {
1240 'class' : 'logging.StreamHandler',
1241 'formatter' : 'form1',
1242 'level' : 'NTOSET',
1243 'stream' : 'ext://sys.stdout',
1244 },
1245 },
1246 'loggers' : {
1247 'compiler.parser' : {
1248 'level' : 'DEBUG',
1249 'handlers' : ['hand1'],
1250 },
1251 },
1252 'root' : {
1253 'level' : 'WARNING',
1254 },
1255 }
1256
1257
1258 #As config1 but with a misspelt level on a logger
1259 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001260 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001261 'formatters': {
1262 'form1' : {
1263 'format' : '%(levelname)s ++ %(message)s',
1264 },
1265 },
1266 'handlers' : {
1267 'hand1' : {
1268 'class' : 'logging.StreamHandler',
1269 'formatter' : 'form1',
1270 'level' : 'NOTSET',
1271 'stream' : 'ext://sys.stdout',
1272 },
1273 },
1274 'loggers' : {
1275 'compiler.parser' : {
1276 'level' : 'DEBUG',
1277 'handlers' : ['hand1'],
1278 },
1279 },
1280 'root' : {
1281 'level' : 'WRANING',
1282 },
1283 }
1284
1285 # config3 has a less subtle configuration error
1286 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001287 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001288 'formatters': {
1289 'form1' : {
1290 'format' : '%(levelname)s ++ %(message)s',
1291 },
1292 },
1293 'handlers' : {
1294 'hand1' : {
1295 'class' : 'logging.StreamHandler',
1296 'formatter' : 'misspelled_name',
1297 'level' : 'NOTSET',
1298 'stream' : 'ext://sys.stdout',
1299 },
1300 },
1301 'loggers' : {
1302 'compiler.parser' : {
1303 'level' : 'DEBUG',
1304 'handlers' : ['hand1'],
1305 },
1306 },
1307 'root' : {
1308 'level' : 'WARNING',
1309 },
1310 }
1311
1312 # config4 specifies a custom formatter class to be loaded
1313 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001314 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001315 'formatters': {
1316 'form1' : {
1317 '()' : __name__ + '.ExceptionFormatter',
1318 'format' : '%(levelname)s:%(name)s:%(message)s',
1319 },
1320 },
1321 'handlers' : {
1322 'hand1' : {
1323 'class' : 'logging.StreamHandler',
1324 'formatter' : 'form1',
1325 'level' : 'NOTSET',
1326 'stream' : 'ext://sys.stdout',
1327 },
1328 },
1329 'root' : {
1330 'level' : 'NOTSET',
1331 'handlers' : ['hand1'],
1332 },
1333 }
1334
1335 # As config4 but using an actual callable rather than a string
1336 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001337 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001338 'formatters': {
1339 'form1' : {
1340 '()' : ExceptionFormatter,
1341 'format' : '%(levelname)s:%(name)s:%(message)s',
1342 },
1343 'form2' : {
1344 '()' : __name__ + '.formatFunc',
1345 'format' : '%(levelname)s:%(name)s:%(message)s',
1346 },
1347 'form3' : {
1348 '()' : formatFunc,
1349 'format' : '%(levelname)s:%(name)s:%(message)s',
1350 },
1351 },
1352 'handlers' : {
1353 'hand1' : {
1354 'class' : 'logging.StreamHandler',
1355 'formatter' : 'form1',
1356 'level' : 'NOTSET',
1357 'stream' : 'ext://sys.stdout',
1358 },
1359 'hand2' : {
1360 '()' : handlerFunc,
1361 },
1362 },
1363 'root' : {
1364 'level' : 'NOTSET',
1365 'handlers' : ['hand1'],
1366 },
1367 }
1368
1369 # config5 specifies a custom handler class to be loaded
1370 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001371 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001372 'formatters': {
1373 'form1' : {
1374 'format' : '%(levelname)s ++ %(message)s',
1375 },
1376 },
1377 'handlers' : {
1378 'hand1' : {
1379 'class' : __name__ + '.CustomHandler',
1380 'formatter' : 'form1',
1381 'level' : 'NOTSET',
1382 'stream' : 'ext://sys.stdout',
1383 },
1384 },
1385 'loggers' : {
1386 'compiler.parser' : {
1387 'level' : 'DEBUG',
1388 'handlers' : ['hand1'],
1389 },
1390 },
1391 'root' : {
1392 'level' : 'WARNING',
1393 },
1394 }
1395
1396 # config6 specifies a custom handler class to be loaded
1397 # but has bad arguments
1398 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001399 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001400 'formatters': {
1401 'form1' : {
1402 'format' : '%(levelname)s ++ %(message)s',
1403 },
1404 },
1405 'handlers' : {
1406 'hand1' : {
1407 'class' : __name__ + '.CustomHandler',
1408 'formatter' : 'form1',
1409 'level' : 'NOTSET',
1410 'stream' : 'ext://sys.stdout',
1411 '9' : 'invalid parameter name',
1412 },
1413 },
1414 'loggers' : {
1415 'compiler.parser' : {
1416 'level' : 'DEBUG',
1417 'handlers' : ['hand1'],
1418 },
1419 },
1420 'root' : {
1421 'level' : 'WARNING',
1422 },
1423 }
1424
1425 #config 7 does not define compiler.parser but defines compiler.lexer
1426 #so compiler.parser should be disabled after applying it
1427 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001428 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001429 'formatters': {
1430 'form1' : {
1431 'format' : '%(levelname)s ++ %(message)s',
1432 },
1433 },
1434 'handlers' : {
1435 'hand1' : {
1436 'class' : 'logging.StreamHandler',
1437 'formatter' : 'form1',
1438 'level' : 'NOTSET',
1439 'stream' : 'ext://sys.stdout',
1440 },
1441 },
1442 'loggers' : {
1443 'compiler.lexer' : {
1444 'level' : 'DEBUG',
1445 'handlers' : ['hand1'],
1446 },
1447 },
1448 'root' : {
1449 'level' : 'WARNING',
1450 },
1451 }
1452
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001453 # config8 defines both compiler and compiler.lexer
1454 # so compiler.parser should not be disabled (since
1455 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001456 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001457 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001458 'disable_existing_loggers' : False,
1459 'formatters': {
1460 'form1' : {
1461 'format' : '%(levelname)s ++ %(message)s',
1462 },
1463 },
1464 'handlers' : {
1465 'hand1' : {
1466 'class' : 'logging.StreamHandler',
1467 'formatter' : 'form1',
1468 'level' : 'NOTSET',
1469 'stream' : 'ext://sys.stdout',
1470 },
1471 },
1472 'loggers' : {
1473 'compiler' : {
1474 'level' : 'DEBUG',
1475 'handlers' : ['hand1'],
1476 },
1477 'compiler.lexer' : {
1478 },
1479 },
1480 'root' : {
1481 'level' : 'WARNING',
1482 },
1483 }
1484
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001485 # config8a disables existing loggers
1486 config8a = {
1487 'version': 1,
1488 'disable_existing_loggers' : True,
1489 'formatters': {
1490 'form1' : {
1491 'format' : '%(levelname)s ++ %(message)s',
1492 },
1493 },
1494 'handlers' : {
1495 'hand1' : {
1496 'class' : 'logging.StreamHandler',
1497 'formatter' : 'form1',
1498 'level' : 'NOTSET',
1499 'stream' : 'ext://sys.stdout',
1500 },
1501 },
1502 'loggers' : {
1503 'compiler' : {
1504 'level' : 'DEBUG',
1505 'handlers' : ['hand1'],
1506 },
1507 'compiler.lexer' : {
1508 },
1509 },
1510 'root' : {
1511 'level' : 'WARNING',
1512 },
1513 }
1514
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001515 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001516 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001517 'formatters': {
1518 'form1' : {
1519 'format' : '%(levelname)s ++ %(message)s',
1520 },
1521 },
1522 'handlers' : {
1523 'hand1' : {
1524 'class' : 'logging.StreamHandler',
1525 'formatter' : 'form1',
1526 'level' : 'WARNING',
1527 'stream' : 'ext://sys.stdout',
1528 },
1529 },
1530 'loggers' : {
1531 'compiler.parser' : {
1532 'level' : 'WARNING',
1533 'handlers' : ['hand1'],
1534 },
1535 },
1536 'root' : {
1537 'level' : 'NOTSET',
1538 },
1539 }
1540
1541 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001542 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001543 'incremental' : True,
1544 'handlers' : {
1545 'hand1' : {
1546 'level' : 'WARNING',
1547 },
1548 },
1549 'loggers' : {
1550 'compiler.parser' : {
1551 'level' : 'INFO',
1552 },
1553 },
1554 }
1555
1556 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001557 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001558 'incremental' : True,
1559 'handlers' : {
1560 'hand1' : {
1561 'level' : 'INFO',
1562 },
1563 },
1564 'loggers' : {
1565 'compiler.parser' : {
1566 'level' : 'INFO',
1567 },
1568 },
1569 }
1570
1571 #As config1 but with a filter added
1572 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001573 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001574 'formatters': {
1575 'form1' : {
1576 'format' : '%(levelname)s ++ %(message)s',
1577 },
1578 },
1579 'filters' : {
1580 'filt1' : {
1581 'name' : 'compiler.parser',
1582 },
1583 },
1584 'handlers' : {
1585 'hand1' : {
1586 'class' : 'logging.StreamHandler',
1587 'formatter' : 'form1',
1588 'level' : 'NOTSET',
1589 'stream' : 'ext://sys.stdout',
1590 'filters' : ['filt1'],
1591 },
1592 },
1593 'loggers' : {
1594 'compiler.parser' : {
1595 'level' : 'DEBUG',
1596 'filters' : ['filt1'],
1597 },
1598 },
1599 'root' : {
1600 'level' : 'WARNING',
1601 'handlers' : ['hand1'],
1602 },
1603 }
1604
1605 #As config1 but using cfg:// references
1606 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001607 'version': 1,
1608 'true_formatters': {
1609 'form1' : {
1610 'format' : '%(levelname)s ++ %(message)s',
1611 },
1612 },
1613 'handler_configs': {
1614 'hand1' : {
1615 'class' : 'logging.StreamHandler',
1616 'formatter' : 'form1',
1617 'level' : 'NOTSET',
1618 'stream' : 'ext://sys.stdout',
1619 },
1620 },
1621 'formatters' : 'cfg://true_formatters',
1622 'handlers' : {
1623 'hand1' : 'cfg://handler_configs[hand1]',
1624 },
1625 'loggers' : {
1626 'compiler.parser' : {
1627 'level' : 'DEBUG',
1628 'handlers' : ['hand1'],
1629 },
1630 },
1631 'root' : {
1632 'level' : 'WARNING',
1633 },
1634 }
1635
1636 #As config11 but missing the version key
1637 config12 = {
1638 'true_formatters': {
1639 'form1' : {
1640 'format' : '%(levelname)s ++ %(message)s',
1641 },
1642 },
1643 'handler_configs': {
1644 'hand1' : {
1645 'class' : 'logging.StreamHandler',
1646 'formatter' : 'form1',
1647 'level' : 'NOTSET',
1648 'stream' : 'ext://sys.stdout',
1649 },
1650 },
1651 'formatters' : 'cfg://true_formatters',
1652 'handlers' : {
1653 'hand1' : 'cfg://handler_configs[hand1]',
1654 },
1655 'loggers' : {
1656 'compiler.parser' : {
1657 'level' : 'DEBUG',
1658 'handlers' : ['hand1'],
1659 },
1660 },
1661 'root' : {
1662 'level' : 'WARNING',
1663 },
1664 }
1665
1666 #As config11 but using an unsupported version
1667 config13 = {
1668 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001669 'true_formatters': {
1670 'form1' : {
1671 'format' : '%(levelname)s ++ %(message)s',
1672 },
1673 },
1674 'handler_configs': {
1675 'hand1' : {
1676 'class' : 'logging.StreamHandler',
1677 'formatter' : 'form1',
1678 'level' : 'NOTSET',
1679 'stream' : 'ext://sys.stdout',
1680 },
1681 },
1682 'formatters' : 'cfg://true_formatters',
1683 'handlers' : {
1684 'hand1' : 'cfg://handler_configs[hand1]',
1685 },
1686 'loggers' : {
1687 'compiler.parser' : {
1688 'level' : 'DEBUG',
1689 'handlers' : ['hand1'],
1690 },
1691 },
1692 'root' : {
1693 'level' : 'WARNING',
1694 },
1695 }
1696
1697 def apply_config(self, conf):
1698 logging.config.dictConfig(conf)
1699
1700 def test_config0_ok(self):
1701 # A simple config which overrides the default settings.
1702 with captured_stdout() as output:
1703 self.apply_config(self.config0)
1704 logger = logging.getLogger()
1705 # Won't output anything
1706 logger.info(self.next_message())
1707 # Outputs a message
1708 logger.error(self.next_message())
1709 self.assert_log_lines([
1710 ('ERROR', '2'),
1711 ], stream=output)
1712 # Original logger output is empty.
1713 self.assert_log_lines([])
1714
1715 def test_config1_ok(self, config=config1):
1716 # A config defining a sub-parser as well.
1717 with captured_stdout() as output:
1718 self.apply_config(config)
1719 logger = logging.getLogger("compiler.parser")
1720 # Both will output a message
1721 logger.info(self.next_message())
1722 logger.error(self.next_message())
1723 self.assert_log_lines([
1724 ('INFO', '1'),
1725 ('ERROR', '2'),
1726 ], stream=output)
1727 # Original logger output is empty.
1728 self.assert_log_lines([])
1729
1730 def test_config2_failure(self):
1731 # A simple config which overrides the default settings.
1732 self.assertRaises(Exception, self.apply_config, self.config2)
1733
1734 def test_config2a_failure(self):
1735 # A simple config which overrides the default settings.
1736 self.assertRaises(Exception, self.apply_config, self.config2a)
1737
1738 def test_config2b_failure(self):
1739 # A simple config which overrides the default settings.
1740 self.assertRaises(Exception, self.apply_config, self.config2b)
1741
1742 def test_config3_failure(self):
1743 # A simple config which overrides the default settings.
1744 self.assertRaises(Exception, self.apply_config, self.config3)
1745
1746 def test_config4_ok(self):
1747 # A config specifying a custom formatter class.
1748 with captured_stdout() as output:
1749 self.apply_config(self.config4)
1750 #logger = logging.getLogger()
1751 try:
1752 raise RuntimeError()
1753 except RuntimeError:
1754 logging.exception("just testing")
1755 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001756 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001757 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1758 # Original logger output is empty
1759 self.assert_log_lines([])
1760
1761 def test_config4a_ok(self):
1762 # A config specifying a custom formatter class.
1763 with captured_stdout() as output:
1764 self.apply_config(self.config4a)
1765 #logger = logging.getLogger()
1766 try:
1767 raise RuntimeError()
1768 except RuntimeError:
1769 logging.exception("just testing")
1770 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001771 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001772 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1773 # Original logger output is empty
1774 self.assert_log_lines([])
1775
1776 def test_config5_ok(self):
1777 self.test_config1_ok(config=self.config5)
1778
1779 def test_config6_failure(self):
1780 self.assertRaises(Exception, self.apply_config, self.config6)
1781
1782 def test_config7_ok(self):
1783 with captured_stdout() as output:
1784 self.apply_config(self.config1)
1785 logger = logging.getLogger("compiler.parser")
1786 # Both will output a message
1787 logger.info(self.next_message())
1788 logger.error(self.next_message())
1789 self.assert_log_lines([
1790 ('INFO', '1'),
1791 ('ERROR', '2'),
1792 ], stream=output)
1793 # Original logger output is empty.
1794 self.assert_log_lines([])
1795 with captured_stdout() as output:
1796 self.apply_config(self.config7)
1797 logger = logging.getLogger("compiler.parser")
1798 self.assertTrue(logger.disabled)
1799 logger = logging.getLogger("compiler.lexer")
1800 # Both will output a message
1801 logger.info(self.next_message())
1802 logger.error(self.next_message())
1803 self.assert_log_lines([
1804 ('INFO', '3'),
1805 ('ERROR', '4'),
1806 ], stream=output)
1807 # Original logger output is empty.
1808 self.assert_log_lines([])
1809
1810 #Same as test_config_7_ok but don't disable old loggers.
1811 def test_config_8_ok(self):
1812 with captured_stdout() as output:
1813 self.apply_config(self.config1)
1814 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001815 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001816 logger.info(self.next_message())
1817 logger.error(self.next_message())
1818 self.assert_log_lines([
1819 ('INFO', '1'),
1820 ('ERROR', '2'),
1821 ], stream=output)
1822 # Original logger output is empty.
1823 self.assert_log_lines([])
1824 with captured_stdout() as output:
1825 self.apply_config(self.config8)
1826 logger = logging.getLogger("compiler.parser")
1827 self.assertFalse(logger.disabled)
1828 # Both will output a message
1829 logger.info(self.next_message())
1830 logger.error(self.next_message())
1831 logger = logging.getLogger("compiler.lexer")
1832 # Both will output a message
1833 logger.info(self.next_message())
1834 logger.error(self.next_message())
1835 self.assert_log_lines([
1836 ('INFO', '3'),
1837 ('ERROR', '4'),
1838 ('INFO', '5'),
1839 ('ERROR', '6'),
1840 ], stream=output)
1841 # Original logger output is empty.
1842 self.assert_log_lines([])
1843
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001844 def test_config_8a_ok(self):
1845 with captured_stdout() as output:
1846 self.apply_config(self.config1a)
1847 logger = logging.getLogger("compiler.parser")
1848 # See issue #11424. compiler-hyphenated sorts
1849 # between compiler and compiler.xyz and this
1850 # was preventing compiler.xyz from being included
1851 # in the child loggers of compiler because of an
1852 # overzealous loop termination condition.
1853 hyphenated = logging.getLogger('compiler-hyphenated')
1854 # All will output a message
1855 logger.info(self.next_message())
1856 logger.error(self.next_message())
1857 hyphenated.critical(self.next_message())
1858 self.assert_log_lines([
1859 ('INFO', '1'),
1860 ('ERROR', '2'),
1861 ('CRITICAL', '3'),
1862 ], stream=output)
1863 # Original logger output is empty.
1864 self.assert_log_lines([])
1865 with captured_stdout() as output:
1866 self.apply_config(self.config8a)
1867 logger = logging.getLogger("compiler.parser")
1868 self.assertFalse(logger.disabled)
1869 # Both will output a message
1870 logger.info(self.next_message())
1871 logger.error(self.next_message())
1872 logger = logging.getLogger("compiler.lexer")
1873 # Both will output a message
1874 logger.info(self.next_message())
1875 logger.error(self.next_message())
1876 # Will not appear
1877 hyphenated.critical(self.next_message())
1878 self.assert_log_lines([
1879 ('INFO', '4'),
1880 ('ERROR', '5'),
1881 ('INFO', '6'),
1882 ('ERROR', '7'),
1883 ], stream=output)
1884 # Original logger output is empty.
1885 self.assert_log_lines([])
1886
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001887 def test_config_9_ok(self):
1888 with captured_stdout() as output:
1889 self.apply_config(self.config9)
1890 logger = logging.getLogger("compiler.parser")
1891 #Nothing will be output since both handler and logger are set to WARNING
1892 logger.info(self.next_message())
1893 self.assert_log_lines([], stream=output)
1894 self.apply_config(self.config9a)
1895 #Nothing will be output since both handler is still set to WARNING
1896 logger.info(self.next_message())
1897 self.assert_log_lines([], stream=output)
1898 self.apply_config(self.config9b)
1899 #Message should now be output
1900 logger.info(self.next_message())
1901 self.assert_log_lines([
1902 ('INFO', '3'),
1903 ], stream=output)
1904
1905 def test_config_10_ok(self):
1906 with captured_stdout() as output:
1907 self.apply_config(self.config10)
1908 logger = logging.getLogger("compiler.parser")
1909 logger.warning(self.next_message())
1910 logger = logging.getLogger('compiler')
1911 #Not output, because filtered
1912 logger.warning(self.next_message())
1913 logger = logging.getLogger('compiler.lexer')
1914 #Not output, because filtered
1915 logger.warning(self.next_message())
1916 logger = logging.getLogger("compiler.parser.codegen")
1917 #Output, as not filtered
1918 logger.error(self.next_message())
1919 self.assert_log_lines([
1920 ('WARNING', '1'),
1921 ('ERROR', '4'),
1922 ], stream=output)
1923
1924 def test_config11_ok(self):
1925 self.test_config1_ok(self.config11)
1926
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001927 def test_config12_failure(self):
1928 self.assertRaises(Exception, self.apply_config, self.config12)
1929
1930 def test_config13_failure(self):
1931 self.assertRaises(Exception, self.apply_config, self.config13)
1932
Victor Stinner45df8202010-04-28 22:31:17 +00001933 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001934 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001935 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001936 # Ask for a randomly assigned port (by using port 0)
1937 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001938 t.start()
1939 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001940 # Now get the port allocated
1941 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001942 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001943 try:
1944 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
1945 sock.settimeout(2.0)
1946 sock.connect(('localhost', port))
1947
1948 slen = struct.pack('>L', len(text))
1949 s = slen + text
1950 sentsofar = 0
1951 left = len(s)
1952 while left > 0:
1953 sent = sock.send(s[sentsofar:])
1954 sentsofar += sent
1955 left -= sent
1956 sock.close()
1957 finally:
1958 t.ready.wait(2.0)
1959 logging.config.stopListening()
1960 t.join(2.0)
1961
1962 def test_listen_config_10_ok(self):
1963 with captured_stdout() as output:
1964 self.setup_via_listener(json.dumps(self.config10))
1965 logger = logging.getLogger("compiler.parser")
1966 logger.warning(self.next_message())
1967 logger = logging.getLogger('compiler')
1968 #Not output, because filtered
1969 logger.warning(self.next_message())
1970 logger = logging.getLogger('compiler.lexer')
1971 #Not output, because filtered
1972 logger.warning(self.next_message())
1973 logger = logging.getLogger("compiler.parser.codegen")
1974 #Output, as not filtered
1975 logger.error(self.next_message())
1976 self.assert_log_lines([
1977 ('WARNING', '1'),
1978 ('ERROR', '4'),
1979 ], stream=output)
1980
1981 def test_listen_config_1_ok(self):
1982 with captured_stdout() as output:
1983 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
1984 logger = logging.getLogger("compiler.parser")
1985 # Both will output a message
1986 logger.info(self.next_message())
1987 logger.error(self.next_message())
1988 self.assert_log_lines([
1989 ('INFO', '1'),
1990 ('ERROR', '2'),
1991 ], stream=output)
1992 # Original logger output is empty.
1993 self.assert_log_lines([])
1994
1995
1996class ManagerTest(BaseTest):
1997 def test_manager_loggerclass(self):
1998 logged = []
1999
2000 class MyLogger(logging.Logger):
2001 def _log(self, level, msg, args, exc_info=None, extra=None):
2002 logged.append(msg)
2003
2004 man = logging.Manager(None)
2005 self.assertRaises(TypeError, man.setLoggerClass, int)
2006 man.setLoggerClass(MyLogger)
2007 logger = man.getLogger('test')
2008 logger.warning('should appear in logged')
2009 logging.warning('should not appear in logged')
2010
2011 self.assertEqual(logged, ['should appear in logged'])
2012
2013
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002014class ChildLoggerTest(BaseTest):
2015 def test_child_loggers(self):
2016 r = logging.getLogger()
2017 l1 = logging.getLogger('abc')
2018 l2 = logging.getLogger('def.ghi')
2019 c1 = r.getChild('xyz')
2020 c2 = r.getChild('uvw.xyz')
2021 self.assertTrue(c1 is logging.getLogger('xyz'))
2022 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2023 c1 = l1.getChild('def')
2024 c2 = c1.getChild('ghi')
2025 c3 = l1.getChild('def.ghi')
2026 self.assertTrue(c1 is logging.getLogger('abc.def'))
2027 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2028 self.assertTrue(c2 is c3)
2029
2030
Vinay Sajip6fac8172010-10-19 20:44:14 +00002031class DerivedLogRecord(logging.LogRecord):
2032 pass
2033
Vinay Sajip61561522010-12-03 11:50:38 +00002034class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002035
2036 def setUp(self):
2037 class CheckingFilter(logging.Filter):
2038 def __init__(self, cls):
2039 self.cls = cls
2040
2041 def filter(self, record):
2042 t = type(record)
2043 if t is not self.cls:
2044 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2045 self.cls)
2046 raise TypeError(msg)
2047 return True
2048
2049 BaseTest.setUp(self)
2050 self.filter = CheckingFilter(DerivedLogRecord)
2051 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002052 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002053
2054 def tearDown(self):
2055 self.root_logger.removeFilter(self.filter)
2056 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002057 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002058
2059 def test_logrecord_class(self):
2060 self.assertRaises(TypeError, self.root_logger.warning,
2061 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002062 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002063 self.root_logger.error(self.next_message())
2064 self.assert_log_lines([
2065 ('root', 'ERROR', '2'),
2066 ])
2067
2068
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002069class QueueHandlerTest(BaseTest):
2070 # Do not bother with a logger name group.
2071 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2072
2073 def setUp(self):
2074 BaseTest.setUp(self)
2075 self.queue = queue.Queue(-1)
2076 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2077 self.que_logger = logging.getLogger('que')
2078 self.que_logger.propagate = False
2079 self.que_logger.setLevel(logging.WARNING)
2080 self.que_logger.addHandler(self.que_hdlr)
2081
2082 def tearDown(self):
2083 self.que_hdlr.close()
2084 BaseTest.tearDown(self)
2085
2086 def test_queue_handler(self):
2087 self.que_logger.debug(self.next_message())
2088 self.assertRaises(queue.Empty, self.queue.get_nowait)
2089 self.que_logger.info(self.next_message())
2090 self.assertRaises(queue.Empty, self.queue.get_nowait)
2091 msg = self.next_message()
2092 self.que_logger.warning(msg)
2093 data = self.queue.get_nowait()
2094 self.assertTrue(isinstance(data, logging.LogRecord))
2095 self.assertEqual(data.name, self.que_logger.name)
2096 self.assertEqual((data.msg, data.args), (msg, None))
2097
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002098 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2099 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002100 def test_queue_listener(self):
2101 handler = TestHandler(Matcher())
2102 listener = logging.handlers.QueueListener(self.queue, handler)
2103 listener.start()
2104 try:
2105 self.que_logger.warning(self.next_message())
2106 self.que_logger.error(self.next_message())
2107 self.que_logger.critical(self.next_message())
2108 finally:
2109 listener.stop()
2110 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2111 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2112 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2113
2114
Vinay Sajipa39c5712010-10-25 13:57:39 +00002115class FormatterTest(unittest.TestCase):
2116 def setUp(self):
2117 self.common = {
2118 'name': 'formatter.test',
2119 'level': logging.DEBUG,
2120 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2121 'lineno': 42,
2122 'exc_info': None,
2123 'func': None,
2124 'msg': 'Message with %d %s',
2125 'args': (2, 'placeholders'),
2126 }
2127 self.variants = {
2128 }
2129
2130 def get_record(self, name=None):
2131 result = dict(self.common)
2132 if name is not None:
2133 result.update(self.variants[name])
2134 return logging.makeLogRecord(result)
2135
2136 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002137 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002138 r = self.get_record()
2139 f = logging.Formatter('${%(message)s}')
2140 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2141 f = logging.Formatter('%(random)s')
2142 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002143 self.assertFalse(f.usesTime())
2144 f = logging.Formatter('%(asctime)s')
2145 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002146 f = logging.Formatter('%(asctime)-15s')
2147 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002148 f = logging.Formatter('asctime')
2149 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002150
2151 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002152 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002153 r = self.get_record()
2154 f = logging.Formatter('$%{message}%$', style='{')
2155 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2156 f = logging.Formatter('{random}', style='{')
2157 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002158 self.assertFalse(f.usesTime())
2159 f = logging.Formatter('{asctime}', style='{')
2160 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002161 f = logging.Formatter('{asctime!s:15}', style='{')
2162 self.assertTrue(f.usesTime())
2163 f = logging.Formatter('{asctime:15}', style='{')
2164 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002165 f = logging.Formatter('asctime', style='{')
2166 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002167
2168 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002169 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002170 r = self.get_record()
2171 f = logging.Formatter('$message', style='$')
2172 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2173 f = logging.Formatter('$$%${message}%$$', style='$')
2174 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2175 f = logging.Formatter('${random}', style='$')
2176 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002177 self.assertFalse(f.usesTime())
2178 f = logging.Formatter('${asctime}', style='$')
2179 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002180 f = logging.Formatter('${asctime', style='$')
2181 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002182 f = logging.Formatter('$asctime', style='$')
2183 self.assertTrue(f.usesTime())
2184 f = logging.Formatter('asctime', style='$')
2185 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002186
Vinay Sajip5a27d402010-12-10 11:42:57 +00002187class LastResortTest(BaseTest):
2188 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002189 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002190 root = self.root_logger
2191 root.removeHandler(self.root_hdlr)
2192 old_stderr = sys.stderr
2193 old_lastresort = logging.lastResort
2194 old_raise_exceptions = logging.raiseExceptions
2195 try:
2196 sys.stderr = sio = io.StringIO()
2197 root.warning('This is your final chance!')
2198 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2199 #No handlers and no last resort, so 'No handlers' message
2200 logging.lastResort = None
2201 sys.stderr = sio = io.StringIO()
2202 root.warning('This is your final chance!')
2203 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2204 # 'No handlers' message only printed once
2205 sys.stderr = sio = io.StringIO()
2206 root.warning('This is your final chance!')
2207 self.assertEqual(sio.getvalue(), '')
2208 root.manager.emittedNoHandlerWarning = False
2209 #If raiseExceptions is False, no message is printed
2210 logging.raiseExceptions = False
2211 sys.stderr = sio = io.StringIO()
2212 root.warning('This is your final chance!')
2213 self.assertEqual(sio.getvalue(), '')
2214 finally:
2215 sys.stderr = old_stderr
2216 root.addHandler(self.root_hdlr)
2217 logging.lastResort = old_lastresort
2218 logging.raiseExceptions = old_raise_exceptions
2219
2220
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002221class BaseFileTest(BaseTest):
2222 "Base class for handler tests that write log files"
2223
2224 def setUp(self):
2225 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002226 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2227 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002228 self.rmfiles = []
2229
2230 def tearDown(self):
2231 for fn in self.rmfiles:
2232 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002233 if os.path.exists(self.fn):
2234 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002235 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002236
2237 def assertLogFile(self, filename):
2238 "Assert a log file is there and register it for deletion"
2239 self.assertTrue(os.path.exists(filename),
2240 msg="Log file %r does not exist")
2241 self.rmfiles.append(filename)
2242
2243
2244class RotatingFileHandlerTest(BaseFileTest):
2245 def next_rec(self):
2246 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2247 self.next_message(), None, None, None)
2248
2249 def test_should_not_rollover(self):
2250 # If maxbytes is zero rollover never occurs
2251 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2252 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002253 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002254
2255 def test_should_rollover(self):
2256 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2257 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002258 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002259
2260 def test_file_created(self):
2261 # checks that the file is created and assumes it was created
2262 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002263 rh = logging.handlers.RotatingFileHandler(self.fn)
2264 rh.emit(self.next_rec())
2265 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002266 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002267
2268 def test_rollover_filenames(self):
2269 rh = logging.handlers.RotatingFileHandler(
2270 self.fn, backupCount=2, maxBytes=1)
2271 rh.emit(self.next_rec())
2272 self.assertLogFile(self.fn)
2273 rh.emit(self.next_rec())
2274 self.assertLogFile(self.fn + ".1")
2275 rh.emit(self.next_rec())
2276 self.assertLogFile(self.fn + ".2")
2277 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002278 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002279
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002280class TimedRotatingFileHandlerTest(BaseFileTest):
2281 # test methods added below
2282 pass
2283
2284def secs(**kw):
2285 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2286
2287for when, exp in (('S', 1),
2288 ('M', 60),
2289 ('H', 60 * 60),
2290 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002291 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002292 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002293 ('W0', secs(days=4, hours=24)),
2294 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002295 def test_compute_rollover(self, when=when, exp=exp):
2296 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002297 self.fn, when=when, interval=1, backupCount=0, utc=True)
2298 currentTime = 0.0
2299 actual = rh.computeRollover(currentTime)
2300 if exp != actual:
2301 # Failures occur on some systems for MIDNIGHT and W0.
2302 # Print detailed calculation for MIDNIGHT so we can try to see
2303 # what's going on
2304 import time
2305 if when == 'MIDNIGHT':
2306 try:
2307 if rh.utc:
2308 t = time.gmtime(currentTime)
2309 else:
2310 t = time.localtime(currentTime)
2311 currentHour = t[3]
2312 currentMinute = t[4]
2313 currentSecond = t[5]
2314 # r is the number of seconds left between now and midnight
2315 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2316 currentMinute) * 60 +
2317 currentSecond)
2318 result = currentTime + r
2319 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2320 print('currentHour: %s' % currentHour, file=sys.stderr)
2321 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2322 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2323 print('r: %s' % r, file=sys.stderr)
2324 print('result: %s' % result, file=sys.stderr)
2325 except Exception:
2326 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2327 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002328 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002329 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2330
Christian Heimes180510d2008-03-03 19:15:45 +00002331# Set the locale to the platform-dependent default. I have no idea
2332# why the test does this, but in any case we save the current locale
2333# first and restore it at the end.
2334@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002335def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002336 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002337 CustomLevelsAndFiltersTest, MemoryHandlerTest,
2338 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002339 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajipa39c5712010-10-25 13:57:39 +00002340 FormatterTest,
Vinay Sajip61561522010-12-03 11:50:38 +00002341 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipbc85d842010-09-17 23:35:29 +00002342 RotatingFileHandlerTest,
Vinay Sajip5a27d402010-12-10 11:42:57 +00002343 LastResortTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00002344 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00002345 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00002346
Christian Heimes180510d2008-03-03 19:15:45 +00002347if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00002348 test_main()