blob: 42757cb05bd56f9700fb45c8a385de52d4da4aa8 [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
Vinay Sajipa463d252011-04-30 21:52:48 +010028import asynchat
29import asyncore
Benjamin Petersonf91df042009-02-13 02:50:59 +000030import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000031import datetime
Vinay Sajipa463d252011-04-30 21:52:48 +010032import errno
Christian Heimes180510d2008-03-03 19:15:45 +000033import pickle
34import io
35import gc
Vinay Sajip7367d082011-05-02 13:17:27 +010036from http.server import HTTPServer, BaseHTTPRequestHandler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000037import json
Christian Heimes180510d2008-03-03 19:15:45 +000038import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000039import queue
Christian Heimes180510d2008-03-03 19:15:45 +000040import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000041import select
Vinay Sajipa463d252011-04-30 21:52:48 +010042import smtpd
Christian Heimes180510d2008-03-03 19:15:45 +000043import socket
Vinay Sajip7367d082011-05-02 13:17:27 +010044from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
45 ThreadingTCPServer, StreamRequestHandler)
Christian Heimes180510d2008-03-03 19:15:45 +000046import struct
47import sys
48import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010049from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010050from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000051import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010052import time
Christian Heimes180510d2008-03-03 19:15:45 +000053import unittest
Vinay Sajip7367d082011-05-02 13:17:27 +010054from urllib.parse import urlparse, parse_qs
Georg Brandlf9734072008-12-07 15:30:06 +000055import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000056import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000057try:
58 import threading
59except ImportError:
60 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000061
62
Christian Heimes180510d2008-03-03 19:15:45 +000063class BaseTest(unittest.TestCase):
64
65 """Base class for logging tests."""
66
67 log_format = "%(name)s -> %(levelname)s: %(message)s"
68 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
69 message_num = 0
70
71 def setUp(self):
72 """Setup the default logging stream to an internal StringIO instance,
73 so that we can examine log output as we want."""
74 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000075 logging._acquireLock()
76 try:
Christian Heimes180510d2008-03-03 19:15:45 +000077 self.saved_handlers = logging._handlers.copy()
78 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000079 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000080 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000081 self.logger_states = logger_states = {}
82 for name in saved_loggers:
83 logger_states[name] = getattr(saved_loggers[name],
84 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000085 finally:
86 logging._releaseLock()
87
Benjamin Peterson22005fc2010-04-11 16:25:06 +000088 # Set two unused loggers: one non-ASCII and one Unicode.
89 # This is to test correct operation when sorting existing
90 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000091 self.logger1 = logging.getLogger("\xab\xd7\xbb")
92 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000093
Christian Heimes180510d2008-03-03 19:15:45 +000094 self.root_logger = logging.getLogger("")
95 self.original_logging_level = self.root_logger.getEffectiveLevel()
96
97 self.stream = io.StringIO()
98 self.root_logger.setLevel(logging.DEBUG)
99 self.root_hdlr = logging.StreamHandler(self.stream)
100 self.root_formatter = logging.Formatter(self.log_format)
101 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000102 if self.logger1.hasHandlers():
103 hlist = self.logger1.handlers + self.root_logger.handlers
104 raise AssertionError('Unexpected handlers: %s' % hlist)
105 if self.logger2.hasHandlers():
106 hlist = self.logger2.handlers + self.root_logger.handlers
107 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000108 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000109 self.assertTrue(self.logger1.hasHandlers())
110 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000111
112 def tearDown(self):
113 """Remove our logging stream, and restore the original logging
114 level."""
115 self.stream.close()
116 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000117 while self.root_logger.handlers:
118 h = self.root_logger.handlers[0]
119 self.root_logger.removeHandler(h)
120 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000121 self.root_logger.setLevel(self.original_logging_level)
122 logging._acquireLock()
123 try:
124 logging._levelNames.clear()
125 logging._levelNames.update(self.saved_level_names)
126 logging._handlers.clear()
127 logging._handlers.update(self.saved_handlers)
128 logging._handlerList[:] = self.saved_handler_list
129 loggerDict = logging.getLogger().manager.loggerDict
130 loggerDict.clear()
131 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000132 logger_states = self.logger_states
133 for name in self.logger_states:
134 if logger_states[name] is not None:
135 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000136 finally:
137 logging._releaseLock()
138
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000139 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000140 """Match the collected log lines against the regular expression
141 self.expected_log_pat, and compare the extracted group values to
142 the expected_values list of tuples."""
143 stream = stream or self.stream
144 pat = re.compile(self.expected_log_pat)
145 try:
146 stream.reset()
147 actual_lines = stream.readlines()
148 except AttributeError:
149 # StringIO.StringIO lacks a reset() method.
150 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000151 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000152 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000153 for actual, expected in zip(actual_lines, expected_values):
154 match = pat.search(actual)
155 if not match:
156 self.fail("Log line does not match expected pattern:\n" +
157 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000158 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000159 s = stream.read()
160 if s:
161 self.fail("Remaining output at end of log stream:\n" + s)
162
163 def next_message(self):
164 """Generate a message consisting solely of an auto-incrementing
165 integer."""
166 self.message_num += 1
167 return "%d" % self.message_num
168
169
170class BuiltinLevelsTest(BaseTest):
171 """Test builtin levels and their inheritance."""
172
173 def test_flat(self):
174 #Logging levels in a flat logger namespace.
175 m = self.next_message
176
177 ERR = logging.getLogger("ERR")
178 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000179 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000180 INF.setLevel(logging.INFO)
181 DEB = logging.getLogger("DEB")
182 DEB.setLevel(logging.DEBUG)
183
184 # These should log.
185 ERR.log(logging.CRITICAL, m())
186 ERR.error(m())
187
188 INF.log(logging.CRITICAL, m())
189 INF.error(m())
190 INF.warn(m())
191 INF.info(m())
192
193 DEB.log(logging.CRITICAL, m())
194 DEB.error(m())
195 DEB.warn (m())
196 DEB.info (m())
197 DEB.debug(m())
198
199 # These should not log.
200 ERR.warn(m())
201 ERR.info(m())
202 ERR.debug(m())
203
204 INF.debug(m())
205
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000206 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000207 ('ERR', 'CRITICAL', '1'),
208 ('ERR', 'ERROR', '2'),
209 ('INF', 'CRITICAL', '3'),
210 ('INF', 'ERROR', '4'),
211 ('INF', 'WARNING', '5'),
212 ('INF', 'INFO', '6'),
213 ('DEB', 'CRITICAL', '7'),
214 ('DEB', 'ERROR', '8'),
215 ('DEB', 'WARNING', '9'),
216 ('DEB', 'INFO', '10'),
217 ('DEB', 'DEBUG', '11'),
218 ])
219
220 def test_nested_explicit(self):
221 # Logging levels in a nested namespace, all explicitly set.
222 m = self.next_message
223
224 INF = logging.getLogger("INF")
225 INF.setLevel(logging.INFO)
226 INF_ERR = logging.getLogger("INF.ERR")
227 INF_ERR.setLevel(logging.ERROR)
228
229 # These should log.
230 INF_ERR.log(logging.CRITICAL, m())
231 INF_ERR.error(m())
232
233 # These should not log.
234 INF_ERR.warn(m())
235 INF_ERR.info(m())
236 INF_ERR.debug(m())
237
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000238 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000239 ('INF.ERR', 'CRITICAL', '1'),
240 ('INF.ERR', 'ERROR', '2'),
241 ])
242
243 def test_nested_inherited(self):
244 #Logging levels in a nested namespace, inherited from parent loggers.
245 m = self.next_message
246
247 INF = logging.getLogger("INF")
248 INF.setLevel(logging.INFO)
249 INF_ERR = logging.getLogger("INF.ERR")
250 INF_ERR.setLevel(logging.ERROR)
251 INF_UNDEF = logging.getLogger("INF.UNDEF")
252 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
253 UNDEF = logging.getLogger("UNDEF")
254
255 # These should log.
256 INF_UNDEF.log(logging.CRITICAL, m())
257 INF_UNDEF.error(m())
258 INF_UNDEF.warn(m())
259 INF_UNDEF.info(m())
260 INF_ERR_UNDEF.log(logging.CRITICAL, m())
261 INF_ERR_UNDEF.error(m())
262
263 # These should not log.
264 INF_UNDEF.debug(m())
265 INF_ERR_UNDEF.warn(m())
266 INF_ERR_UNDEF.info(m())
267 INF_ERR_UNDEF.debug(m())
268
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000269 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000270 ('INF.UNDEF', 'CRITICAL', '1'),
271 ('INF.UNDEF', 'ERROR', '2'),
272 ('INF.UNDEF', 'WARNING', '3'),
273 ('INF.UNDEF', 'INFO', '4'),
274 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
275 ('INF.ERR.UNDEF', 'ERROR', '6'),
276 ])
277
278 def test_nested_with_virtual_parent(self):
279 # Logging levels when some parent does not exist yet.
280 m = self.next_message
281
282 INF = logging.getLogger("INF")
283 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
284 CHILD = logging.getLogger("INF.BADPARENT")
285 INF.setLevel(logging.INFO)
286
287 # These should log.
288 GRANDCHILD.log(logging.FATAL, m())
289 GRANDCHILD.info(m())
290 CHILD.log(logging.FATAL, m())
291 CHILD.info(m())
292
293 # These should not log.
294 GRANDCHILD.debug(m())
295 CHILD.debug(m())
296
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000297 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000298 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
299 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
300 ('INF.BADPARENT', 'CRITICAL', '3'),
301 ('INF.BADPARENT', 'INFO', '4'),
302 ])
303
304
305class BasicFilterTest(BaseTest):
306
307 """Test the bundled Filter class."""
308
309 def test_filter(self):
310 # Only messages satisfying the specified criteria pass through the
311 # filter.
312 filter_ = logging.Filter("spam.eggs")
313 handler = self.root_logger.handlers[0]
314 try:
315 handler.addFilter(filter_)
316 spam = logging.getLogger("spam")
317 spam_eggs = logging.getLogger("spam.eggs")
318 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
319 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
320
321 spam.info(self.next_message())
322 spam_eggs.info(self.next_message()) # Good.
323 spam_eggs_fish.info(self.next_message()) # Good.
324 spam_bakedbeans.info(self.next_message())
325
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000326 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000327 ('spam.eggs', 'INFO', '2'),
328 ('spam.eggs.fish', 'INFO', '3'),
329 ])
330 finally:
331 handler.removeFilter(filter_)
332
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000333 def test_callable_filter(self):
334 # Only messages satisfying the specified criteria pass through the
335 # filter.
336
337 def filterfunc(record):
338 parts = record.name.split('.')
339 prefix = '.'.join(parts[:2])
340 return prefix == 'spam.eggs'
341
342 handler = self.root_logger.handlers[0]
343 try:
344 handler.addFilter(filterfunc)
345 spam = logging.getLogger("spam")
346 spam_eggs = logging.getLogger("spam.eggs")
347 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
348 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
349
350 spam.info(self.next_message())
351 spam_eggs.info(self.next_message()) # Good.
352 spam_eggs_fish.info(self.next_message()) # Good.
353 spam_bakedbeans.info(self.next_message())
354
355 self.assert_log_lines([
356 ('spam.eggs', 'INFO', '2'),
357 ('spam.eggs.fish', 'INFO', '3'),
358 ])
359 finally:
360 handler.removeFilter(filterfunc)
361
Vinay Sajip985ef872011-04-26 19:34:04 +0100362 def test_empty_filter(self):
363 f = logging.Filter()
364 r = logging.makeLogRecord({'name': 'spam.eggs'})
365 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000366
367#
368# First, we define our levels. There can be as many as you want - the only
369# limitations are that they should be integers, the lowest should be > 0 and
370# larger values mean less information being logged. If you need specific
371# level values which do not fit into these limitations, you can use a
372# mapping dictionary to convert between your application levels and the
373# logging system.
374#
375SILENT = 120
376TACITURN = 119
377TERSE = 118
378EFFUSIVE = 117
379SOCIABLE = 116
380VERBOSE = 115
381TALKATIVE = 114
382GARRULOUS = 113
383CHATTERBOX = 112
384BORING = 111
385
386LEVEL_RANGE = range(BORING, SILENT + 1)
387
388#
389# Next, we define names for our levels. You don't need to do this - in which
390# case the system will use "Level n" to denote the text for the level.
391#
392my_logging_levels = {
393 SILENT : 'Silent',
394 TACITURN : 'Taciturn',
395 TERSE : 'Terse',
396 EFFUSIVE : 'Effusive',
397 SOCIABLE : 'Sociable',
398 VERBOSE : 'Verbose',
399 TALKATIVE : 'Talkative',
400 GARRULOUS : 'Garrulous',
401 CHATTERBOX : 'Chatterbox',
402 BORING : 'Boring',
403}
404
405class GarrulousFilter(logging.Filter):
406
407 """A filter which blocks garrulous messages."""
408
409 def filter(self, record):
410 return record.levelno != GARRULOUS
411
412class VerySpecificFilter(logging.Filter):
413
414 """A filter which blocks sociable and taciturn messages."""
415
416 def filter(self, record):
417 return record.levelno not in [SOCIABLE, TACITURN]
418
419
420class CustomLevelsAndFiltersTest(BaseTest):
421
422 """Test various filtering possibilities with custom logging levels."""
423
424 # Skip the logger name group.
425 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
426
427 def setUp(self):
428 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000429 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000430 logging.addLevelName(k, v)
431
432 def log_at_all_levels(self, logger):
433 for lvl in LEVEL_RANGE:
434 logger.log(lvl, self.next_message())
435
436 def test_logger_filter(self):
437 # Filter at logger level.
438 self.root_logger.setLevel(VERBOSE)
439 # Levels >= 'Verbose' are good.
440 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000441 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000442 ('Verbose', '5'),
443 ('Sociable', '6'),
444 ('Effusive', '7'),
445 ('Terse', '8'),
446 ('Taciturn', '9'),
447 ('Silent', '10'),
448 ])
449
450 def test_handler_filter(self):
451 # Filter at handler level.
452 self.root_logger.handlers[0].setLevel(SOCIABLE)
453 try:
454 # Levels >= 'Sociable' are good.
455 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000456 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000457 ('Sociable', '6'),
458 ('Effusive', '7'),
459 ('Terse', '8'),
460 ('Taciturn', '9'),
461 ('Silent', '10'),
462 ])
463 finally:
464 self.root_logger.handlers[0].setLevel(logging.NOTSET)
465
466 def test_specific_filters(self):
467 # Set a specific filter object on the handler, and then add another
468 # filter object on the logger itself.
469 handler = self.root_logger.handlers[0]
470 specific_filter = None
471 garr = GarrulousFilter()
472 handler.addFilter(garr)
473 try:
474 self.log_at_all_levels(self.root_logger)
475 first_lines = [
476 # Notice how 'Garrulous' is missing
477 ('Boring', '1'),
478 ('Chatterbox', '2'),
479 ('Talkative', '4'),
480 ('Verbose', '5'),
481 ('Sociable', '6'),
482 ('Effusive', '7'),
483 ('Terse', '8'),
484 ('Taciturn', '9'),
485 ('Silent', '10'),
486 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000487 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000488
489 specific_filter = VerySpecificFilter()
490 self.root_logger.addFilter(specific_filter)
491 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000492 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000493 # Not only 'Garrulous' is still missing, but also 'Sociable'
494 # and 'Taciturn'
495 ('Boring', '11'),
496 ('Chatterbox', '12'),
497 ('Talkative', '14'),
498 ('Verbose', '15'),
499 ('Effusive', '17'),
500 ('Terse', '18'),
501 ('Silent', '20'),
502 ])
503 finally:
504 if specific_filter:
505 self.root_logger.removeFilter(specific_filter)
506 handler.removeFilter(garr)
507
508
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100509class HandlerTest(BaseTest):
510 def test_name(self):
511 h = logging.Handler()
512 h.name = 'generic'
513 self.assertEqual(h.name, 'generic')
514 h.name = 'anothergeneric'
515 self.assertEqual(h.name, 'anothergeneric')
516 self.assertRaises(NotImplementedError, h.emit, None)
517
Vinay Sajip5a35b062011-04-27 11:31:14 +0100518 def test_builtin_handlers(self):
519 # We can't actually *use* too many handlers in the tests,
520 # but we can try instantiating them with various options
521 if sys.platform in ('linux2', 'darwin'):
522 for existing in (True, False):
523 fd, fn = tempfile.mkstemp()
524 os.close(fd)
525 if not existing:
526 os.unlink(fn)
527 h = logging.handlers.WatchedFileHandler(fn, delay=True)
528 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100529 dev, ino = h.dev, h.ino
530 self.assertNotEqual(dev, -1)
531 self.assertNotEqual(ino, -1)
532 r = logging.makeLogRecord({'msg': 'Test'})
533 h.handle(r)
534 # Now remove the file.
535 os.unlink(fn)
536 self.assertFalse(os.path.exists(fn))
537 # The next call should recreate the file.
538 h.handle(r)
539 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100540 else:
541 self.assertEqual(h.dev, -1)
542 self.assertEqual(h.ino, -1)
543 h.close()
544 if existing:
545 os.unlink(fn)
546 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100547 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100548 else:
549 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100550 try:
551 h = logging.handlers.SysLogHandler(sockname)
552 self.assertEqual(h.facility, h.LOG_USER)
553 self.assertTrue(h.unixsocket)
554 h.close()
555 except socket.error: # syslogd might not be available
556 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100557 for method in ('GET', 'POST', 'PUT'):
558 if method == 'PUT':
559 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
560 'localhost', '/log', method)
561 else:
562 h = logging.handlers.HTTPHandler('localhost', '/log', method)
563 h.close()
564 h = logging.handlers.BufferingHandler(0)
565 r = logging.makeLogRecord({})
566 self.assertTrue(h.shouldFlush(r))
567 h.close()
568 h = logging.handlers.BufferingHandler(1)
569 self.assertFalse(h.shouldFlush(r))
570 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100571
572class BadStream(object):
573 def write(self, data):
574 raise RuntimeError('deliberate mistake')
575
576class TestStreamHandler(logging.StreamHandler):
577 def handleError(self, record):
578 self.error_record = record
579
580class StreamHandlerTest(BaseTest):
581 def test_error_handling(self):
582 h = TestStreamHandler(BadStream())
583 r = logging.makeLogRecord({})
584 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100585 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100586 try:
587 h.handle(r)
588 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100589 h = logging.StreamHandler(BadStream())
590 sys.stderr = sio = io.StringIO()
591 h.handle(r)
592 self.assertTrue('\nRuntimeError: '
593 'deliberate mistake\n' in sio.getvalue())
594 logging.raiseExceptions = False
595 sys.stderr = sio = io.StringIO()
596 h.handle(r)
597 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100598 finally:
599 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100600 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100601
Vinay Sajip7367d082011-05-02 13:17:27 +0100602# -- The following section could be moved into a server_helper.py module
603# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100604
605class TestSMTPChannel(smtpd.SMTPChannel):
606 def __init__(self, server, conn, addr, sockmap):
607 asynchat.async_chat.__init__(self, conn, sockmap)
608 self.smtp_server = server
609 self.conn = conn
610 self.addr = addr
611 self.received_lines = []
612 self.smtp_state = self.COMMAND
613 self.seen_greeting = ''
614 self.mailfrom = None
615 self.rcpttos = []
616 self.received_data = ''
617 self.fqdn = socket.getfqdn()
618 self.num_bytes = 0
619 try:
620 self.peer = conn.getpeername()
621 except socket.error as err:
622 # a race condition may occur if the other end is closing
623 # before we can get the peername
624 self.close()
625 if err.args[0] != errno.ENOTCONN:
626 raise
627 return
628 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
629 self.set_terminator(b'\r\n')
630
631
632class TestSMTPServer(smtpd.SMTPServer):
633 channel_class = TestSMTPChannel
634
635 def __init__(self, addr, handler, poll_interval, sockmap):
636 self._localaddr = addr
637 self._remoteaddr = None
638 self.sockmap = sockmap
639 asyncore.dispatcher.__init__(self, map=sockmap)
640 try:
641 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
642 sock.setblocking(0)
643 self.set_socket(sock, map=sockmap)
644 # try to re-use a server port if possible
645 self.set_reuse_addr()
646 self.bind(addr)
Vinay Sajip7367d082011-05-02 13:17:27 +0100647 self.port = sock.getsockname()[1]
Vinay Sajipa463d252011-04-30 21:52:48 +0100648 self.listen(5)
649 except:
650 self.close()
651 raise
652 self._handler = handler
653 self._thread = None
654 self.poll_interval = poll_interval
655
656 def handle_accepted(self, conn, addr):
657 print('Incoming connection from %s' % repr(addr), file=smtpd.DEBUGSTREAM)
658 channel = self.channel_class(self, conn, addr, self.sockmap)
659
660 def process_message(self, peer, mailfrom, rcpttos, data):
661 self._handler(peer, mailfrom, rcpttos, data)
662
663 def start(self):
664 self._thread = t = threading.Thread(target=self.serve_forever,
665 args=(self.poll_interval,))
666 t.setDaemon(True)
667 t.start()
668
669 def serve_forever(self, poll_interval):
670 asyncore.loop(poll_interval, map=self.sockmap)
671
Vinay Sajip7367d082011-05-02 13:17:27 +0100672 def stop(self, timeout=None):
Vinay Sajipa463d252011-04-30 21:52:48 +0100673 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100674 self._thread.join(timeout)
Vinay Sajipa463d252011-04-30 21:52:48 +0100675 self._thread = None
676
Vinay Sajip7367d082011-05-02 13:17:27 +0100677class ControlMixin(object):
678 """
679 This mixin is used to start a server on a separate thread, and
680 shut it down programmatically. Request handling is simplified - instead
681 of needing to derive a suitable RequestHandler subclass, you just
682 provide a callable which will be passed each received request to be
683 processed.
684
685 :param handler: A handler callable which will be called with a
686 single parameter - the request - in order to
687 process the request. This handler is called on the
688 server thread, effectively meaning that requests are
689 processed serially. While not quite Web scale ;-),
690 this should be fine for testing applications.
691 :param poll_interval: The polling interval in seconds.
692 """
693 def __init__(self, handler, poll_interval):
694 self._thread = None
695 self.poll_interval = poll_interval
696 self._handler = handler
697 self.ready = threading.Event()
698
699 def start(self):
700 """
701 Create a daemon thread to run the server, and start it.
702 """
703 self._thread = t = threading.Thread(target=self.serve_forever,
704 args=(self.poll_interval,))
705 t.setDaemon(True)
706 t.start()
707
708 def serve_forever(self, poll_interval):
709 self.ready.set()
710 super(ControlMixin, self).serve_forever(poll_interval)
711
712 def stop(self, timeout=None):
713 """
714 Tell the server thread to stop, and wait for it to do so.
715 """
716 self.shutdown()
717 if self._thread is not None:
718 self._thread.join(timeout)
719 self._thread = None
720 self.server_close()
721 self.ready.clear()
722
723class TestHTTPServer(ControlMixin, HTTPServer):
724 """
725 An HTTP server which is controllable using :class:`ControlMixin`.
726
727 :param addr: A tuple with the IP address and port to listen on.
728 :param handler: A handler callable which will be called with a
729 single parameter - the request - in order to
730 process the request.
731 :param poll_interval: The polling interval in seconds.
732 """
733 def __init__(self, addr, handler, poll_interval=0.5, log=False):
734 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
735 def __getattr__(self, name, default=None):
736 if name.startswith('do_'):
737 return self.process_request
738 raise AttributeError(name)
739
740 def process_request(self):
741 self.server._handler(self)
742
743 def log_message(self, format, *args):
744 if log:
745 super(DelegatingHTTPRequestHandler,
746 self).log_message(format, *args)
747 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
748 ControlMixin.__init__(self, handler, poll_interval)
749
750class TestTCPServer(ControlMixin, ThreadingTCPServer):
751 """
752 A TCP server which is controllable using :class:`ControlMixin`.
753
754 :param addr: A tuple with the IP address and port to listen on.
755 :param handler: A handler callable which will be called with a single
756 parameter - the request - in order to process the request.
757 :param poll_interval: The polling interval in seconds.
758 :bind_and_activate: If True (the default), binds the server and starts it
759 listening. If False, you need to call
760 :meth:`server_bind` and :meth:`server_activate` at
761 some later time before calling :meth:`start`, so that
762 the server will set up the socket and listen on it.
763 """
764
765 allow_reuse_address = True
766
767 def __init__(self, addr, handler, poll_interval=0.5,
768 bind_and_activate=True):
769 class DelegatingTCPRequestHandler(StreamRequestHandler):
770
771 def handle(self):
772 self.server._handler(self)
773 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
774 bind_and_activate)
775 ControlMixin.__init__(self, handler, poll_interval)
776
777 def server_bind(self):
778 super(TestTCPServer, self).server_bind()
779 self.port = self.socket.getsockname()[1]
780
781class TestUDPServer(ControlMixin, ThreadingUDPServer):
782 """
783 A UDP server which is controllable using :class:`ControlMixin`.
784
785 :param addr: A tuple with the IP address and port to listen on.
786 :param handler: A handler callable which will be called with a
787 single parameter - the request - in order to
788 process the request.
789 :param poll_interval: The polling interval for shutdown requests,
790 in seconds.
791 :bind_and_activate: If True (the default), binds the server and
792 starts it listening. If False, you need to
793 call :meth:`server_bind` and
794 :meth:`server_activate` at some later time
795 before calling :meth:`start`, so that the server will
796 set up the socket and listen on it.
797 """
798 def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True):
799 class DelegatingUDPRequestHandler(DatagramRequestHandler):
800
801 def handle(self):
802 self.server._handler(self)
803 ThreadingUDPServer.__init__(self, addr, DelegatingUDPRequestHandler,
804 bind_and_activate)
805 ControlMixin.__init__(self, handler, poll_interval)
806
807 def server_bind(self):
808 super(TestUDPServer, self).server_bind()
809 self.port = self.socket.getsockname()[1]
810
811
812# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100813
814class SMTPHandlerTest(BaseTest):
815 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100816 sockmap = {}
817 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
818 sockmap)
819 server.start()
820 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100821 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
822 self.assertEqual(h.toaddrs, ['you'])
823 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100824 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100825 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100826 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100827 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100828 server.stop()
829 self.assertEqual(len(self.messages), 1)
830 peer, mailfrom, rcpttos, data = self.messages[0]
831 self.assertEqual(mailfrom, 'me')
832 self.assertEqual(rcpttos, ['you'])
833 self.assertTrue('\nSubject: Log\n' in data)
834 self.assertTrue(data.endswith('\n\nHello'))
835 h.close()
836
837 def process_message(self, *args):
838 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100839 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100840
Christian Heimes180510d2008-03-03 19:15:45 +0000841class MemoryHandlerTest(BaseTest):
842
843 """Tests for the MemoryHandler."""
844
845 # Do not bother with a logger name group.
846 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
847
848 def setUp(self):
849 BaseTest.setUp(self)
850 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
851 self.root_hdlr)
852 self.mem_logger = logging.getLogger('mem')
853 self.mem_logger.propagate = 0
854 self.mem_logger.addHandler(self.mem_hdlr)
855
856 def tearDown(self):
857 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000858 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000859
860 def test_flush(self):
861 # The memory handler flushes to its target handler based on specific
862 # criteria (message count and message level).
863 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000864 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000865 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000866 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000867 # This will flush because the level is >= logging.WARNING
868 self.mem_logger.warn(self.next_message())
869 lines = [
870 ('DEBUG', '1'),
871 ('INFO', '2'),
872 ('WARNING', '3'),
873 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000874 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000875 for n in (4, 14):
876 for i in range(9):
877 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000878 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000879 # This will flush because it's the 10th message since the last
880 # flush.
881 self.mem_logger.debug(self.next_message())
882 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000883 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000884
885 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000886 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000887
888
889class ExceptionFormatter(logging.Formatter):
890 """A special exception formatter."""
891 def formatException(self, ei):
892 return "Got a [%s]" % ei[0].__name__
893
894
895class ConfigFileTest(BaseTest):
896
897 """Reading logging config from a .ini-style config file."""
898
899 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
900
901 # config0 is a standard configuration.
902 config0 = """
903 [loggers]
904 keys=root
905
906 [handlers]
907 keys=hand1
908
909 [formatters]
910 keys=form1
911
912 [logger_root]
913 level=WARNING
914 handlers=hand1
915
916 [handler_hand1]
917 class=StreamHandler
918 level=NOTSET
919 formatter=form1
920 args=(sys.stdout,)
921
922 [formatter_form1]
923 format=%(levelname)s ++ %(message)s
924 datefmt=
925 """
926
927 # config1 adds a little to the standard configuration.
928 config1 = """
929 [loggers]
930 keys=root,parser
931
932 [handlers]
933 keys=hand1
934
935 [formatters]
936 keys=form1
937
938 [logger_root]
939 level=WARNING
940 handlers=
941
942 [logger_parser]
943 level=DEBUG
944 handlers=hand1
945 propagate=1
946 qualname=compiler.parser
947
948 [handler_hand1]
949 class=StreamHandler
950 level=NOTSET
951 formatter=form1
952 args=(sys.stdout,)
953
954 [formatter_form1]
955 format=%(levelname)s ++ %(message)s
956 datefmt=
957 """
958
Vinay Sajip3f84b072011-03-07 17:49:33 +0000959 # config1a moves the handler to the root.
960 config1a = """
961 [loggers]
962 keys=root,parser
963
964 [handlers]
965 keys=hand1
966
967 [formatters]
968 keys=form1
969
970 [logger_root]
971 level=WARNING
972 handlers=hand1
973
974 [logger_parser]
975 level=DEBUG
976 handlers=
977 propagate=1
978 qualname=compiler.parser
979
980 [handler_hand1]
981 class=StreamHandler
982 level=NOTSET
983 formatter=form1
984 args=(sys.stdout,)
985
986 [formatter_form1]
987 format=%(levelname)s ++ %(message)s
988 datefmt=
989 """
990
Christian Heimes180510d2008-03-03 19:15:45 +0000991 # config2 has a subtle configuration error that should be reported
992 config2 = config1.replace("sys.stdout", "sys.stbout")
993
994 # config3 has a less subtle configuration error
995 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
996
997 # config4 specifies a custom formatter class to be loaded
998 config4 = """
999 [loggers]
1000 keys=root
1001
1002 [handlers]
1003 keys=hand1
1004
1005 [formatters]
1006 keys=form1
1007
1008 [logger_root]
1009 level=NOTSET
1010 handlers=hand1
1011
1012 [handler_hand1]
1013 class=StreamHandler
1014 level=NOTSET
1015 formatter=form1
1016 args=(sys.stdout,)
1017
1018 [formatter_form1]
1019 class=""" + __name__ + """.ExceptionFormatter
1020 format=%(levelname)s:%(name)s:%(message)s
1021 datefmt=
1022 """
1023
Georg Brandl3dbca812008-07-23 16:10:53 +00001024 # config5 specifies a custom handler class to be loaded
1025 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1026
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001027 # config6 uses ', ' delimiters in the handlers and formatters sections
1028 config6 = """
1029 [loggers]
1030 keys=root,parser
1031
1032 [handlers]
1033 keys=hand1, hand2
1034
1035 [formatters]
1036 keys=form1, form2
1037
1038 [logger_root]
1039 level=WARNING
1040 handlers=
1041
1042 [logger_parser]
1043 level=DEBUG
1044 handlers=hand1
1045 propagate=1
1046 qualname=compiler.parser
1047
1048 [handler_hand1]
1049 class=StreamHandler
1050 level=NOTSET
1051 formatter=form1
1052 args=(sys.stdout,)
1053
1054 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001055 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001056 level=NOTSET
1057 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001058 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001059
1060 [formatter_form1]
1061 format=%(levelname)s ++ %(message)s
1062 datefmt=
1063
1064 [formatter_form2]
1065 format=%(message)s
1066 datefmt=
1067 """
1068
Vinay Sajip3f84b072011-03-07 17:49:33 +00001069 # config7 adds a compiler logger.
1070 config7 = """
1071 [loggers]
1072 keys=root,parser,compiler
1073
1074 [handlers]
1075 keys=hand1
1076
1077 [formatters]
1078 keys=form1
1079
1080 [logger_root]
1081 level=WARNING
1082 handlers=hand1
1083
1084 [logger_compiler]
1085 level=DEBUG
1086 handlers=
1087 propagate=1
1088 qualname=compiler
1089
1090 [logger_parser]
1091 level=DEBUG
1092 handlers=
1093 propagate=1
1094 qualname=compiler.parser
1095
1096 [handler_hand1]
1097 class=StreamHandler
1098 level=NOTSET
1099 formatter=form1
1100 args=(sys.stdout,)
1101
1102 [formatter_form1]
1103 format=%(levelname)s ++ %(message)s
1104 datefmt=
1105 """
1106
Christian Heimes180510d2008-03-03 19:15:45 +00001107 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001108 file = io.StringIO(textwrap.dedent(conf))
1109 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001110
1111 def test_config0_ok(self):
1112 # A simple config file which overrides the default settings.
1113 with captured_stdout() as output:
1114 self.apply_config(self.config0)
1115 logger = logging.getLogger()
1116 # Won't output anything
1117 logger.info(self.next_message())
1118 # Outputs a message
1119 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001120 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001121 ('ERROR', '2'),
1122 ], stream=output)
1123 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001124 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001125
Georg Brandl3dbca812008-07-23 16:10:53 +00001126 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001127 # A config file defining a sub-parser as well.
1128 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001129 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001130 logger = logging.getLogger("compiler.parser")
1131 # Both will output a message
1132 logger.info(self.next_message())
1133 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001134 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001135 ('INFO', '1'),
1136 ('ERROR', '2'),
1137 ], stream=output)
1138 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001139 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001140
1141 def test_config2_failure(self):
1142 # A simple config file which overrides the default settings.
1143 self.assertRaises(Exception, self.apply_config, self.config2)
1144
1145 def test_config3_failure(self):
1146 # A simple config file which overrides the default settings.
1147 self.assertRaises(Exception, self.apply_config, self.config3)
1148
1149 def test_config4_ok(self):
1150 # A config file specifying a custom formatter class.
1151 with captured_stdout() as output:
1152 self.apply_config(self.config4)
1153 logger = logging.getLogger()
1154 try:
1155 raise RuntimeError()
1156 except RuntimeError:
1157 logging.exception("just testing")
1158 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001159 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001160 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1161 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001162 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001163
Georg Brandl3dbca812008-07-23 16:10:53 +00001164 def test_config5_ok(self):
1165 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001166
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001167 def test_config6_ok(self):
1168 self.test_config1_ok(config=self.config6)
1169
Vinay Sajip3f84b072011-03-07 17:49:33 +00001170 def test_config7_ok(self):
1171 with captured_stdout() as output:
1172 self.apply_config(self.config1a)
1173 logger = logging.getLogger("compiler.parser")
1174 # See issue #11424. compiler-hyphenated sorts
1175 # between compiler and compiler.xyz and this
1176 # was preventing compiler.xyz from being included
1177 # in the child loggers of compiler because of an
1178 # overzealous loop termination condition.
1179 hyphenated = logging.getLogger('compiler-hyphenated')
1180 # All will output a message
1181 logger.info(self.next_message())
1182 logger.error(self.next_message())
1183 hyphenated.critical(self.next_message())
1184 self.assert_log_lines([
1185 ('INFO', '1'),
1186 ('ERROR', '2'),
1187 ('CRITICAL', '3'),
1188 ], stream=output)
1189 # Original logger output is empty.
1190 self.assert_log_lines([])
1191 with captured_stdout() as output:
1192 self.apply_config(self.config7)
1193 logger = logging.getLogger("compiler.parser")
1194 self.assertFalse(logger.disabled)
1195 # Both will output a message
1196 logger.info(self.next_message())
1197 logger.error(self.next_message())
1198 logger = logging.getLogger("compiler.lexer")
1199 # Both will output a message
1200 logger.info(self.next_message())
1201 logger.error(self.next_message())
1202 # Will not appear
1203 hyphenated.critical(self.next_message())
1204 self.assert_log_lines([
1205 ('INFO', '4'),
1206 ('ERROR', '5'),
1207 ('INFO', '6'),
1208 ('ERROR', '7'),
1209 ], stream=output)
1210 # Original logger output is empty.
1211 self.assert_log_lines([])
1212
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001213
Victor Stinner45df8202010-04-28 22:31:17 +00001214@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001215class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001216
Christian Heimes180510d2008-03-03 19:15:45 +00001217 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001218
Christian Heimes180510d2008-03-03 19:15:45 +00001219 def setUp(self):
1220 """Set up a TCP server to receive log messages, and a SocketHandler
1221 pointing to that server's address and port."""
1222 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001223 addr = ('localhost', 0)
1224 self.server = server = TestTCPServer(addr, self.handle_socket,
1225 0.01)
1226 server.start()
1227 server.ready.wait()
1228 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1229 server.port)
1230 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001231 self.root_logger.removeHandler(self.root_logger.handlers[0])
1232 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001233 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001234
Christian Heimes180510d2008-03-03 19:15:45 +00001235 def tearDown(self):
1236 """Shutdown the TCP server."""
1237 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001238 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001239 self.root_logger.removeHandler(self.sock_hdlr)
1240 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001241 finally:
1242 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001243
Vinay Sajip7367d082011-05-02 13:17:27 +01001244 def handle_socket(self, request):
1245 conn = request.connection
1246 while True:
1247 chunk = conn.recv(4)
1248 if len(chunk) < 4:
1249 break
1250 slen = struct.unpack(">L", chunk)[0]
1251 chunk = conn.recv(slen)
1252 while len(chunk) < slen:
1253 chunk = chunk + conn.recv(slen - len(chunk))
1254 obj = pickle.loads(chunk)
1255 record = logging.makeLogRecord(obj)
1256 self.log_output += record.msg + '\n'
1257 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001258
Christian Heimes180510d2008-03-03 19:15:45 +00001259 def test_output(self):
1260 # The log message sent to the SocketHandler is properly received.
1261 logger = logging.getLogger("tcp")
1262 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001263 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001264 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001265 self.handled.acquire()
1266 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001267
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001268 def test_noserver(self):
1269 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001270 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001271 #The logging call should try to connect, which should fail
1272 try:
1273 raise RuntimeError('Deliberate mistake')
1274 except RuntimeError:
1275 self.root_logger.exception('Never sent')
1276 self.root_logger.error('Never sent, either')
1277 now = time.time()
1278 self.assertTrue(self.sock_hdlr.retryTime > now)
1279 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1280 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001281
Vinay Sajip7367d082011-05-02 13:17:27 +01001282
1283@unittest.skipUnless(threading, 'Threading required for this test.')
1284class DatagramHandlerTest(BaseTest):
1285
1286 """Test for DatagramHandler."""
1287
1288 def setUp(self):
1289 """Set up a UDP server to receive log messages, and a DatagramHandler
1290 pointing to that server's address and port."""
1291 BaseTest.setUp(self)
1292 addr = ('localhost', 0)
1293 self.server = server = TestUDPServer(addr, self.handle_datagram,
1294 0.01)
1295 server.start()
1296 server.ready.wait()
1297 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1298 server.port)
1299 self.log_output = ''
1300 self.root_logger.removeHandler(self.root_logger.handlers[0])
1301 self.root_logger.addHandler(self.sock_hdlr)
1302 self.handled = threading.Event()
1303
1304 def tearDown(self):
1305 """Shutdown the UDP server."""
1306 try:
1307 self.server.stop(2.0)
1308 self.root_logger.removeHandler(self.sock_hdlr)
1309 self.sock_hdlr.close()
1310 finally:
1311 BaseTest.tearDown(self)
1312
1313 def handle_datagram(self, request):
1314 slen = struct.pack('>L', 0) # length of prefix
1315 packet = request.packet[len(slen):]
1316 obj = pickle.loads(packet)
1317 record = logging.makeLogRecord(obj)
1318 self.log_output += record.msg + '\n'
1319 self.handled.set()
1320
1321 def test_output(self):
1322 # The log message sent to the DatagramHandler is properly received.
1323 logger = logging.getLogger("udp")
1324 logger.error("spam")
1325 self.handled.wait()
1326 self.assertEqual(self.log_output, "spam\n")
1327
1328
1329@unittest.skipUnless(threading, 'Threading required for this test.')
1330class SysLogHandlerTest(BaseTest):
1331
1332 """Test for SysLogHandler using UDP."""
1333
1334 def setUp(self):
1335 """Set up a UDP server to receive log messages, and a SysLogHandler
1336 pointing to that server's address and port."""
1337 BaseTest.setUp(self)
1338 addr = ('localhost', 0)
1339 self.server = server = TestUDPServer(addr, self.handle_datagram,
1340 0.01)
1341 server.start()
1342 server.ready.wait()
1343 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1344 server.port))
1345 self.log_output = ''
1346 self.root_logger.removeHandler(self.root_logger.handlers[0])
1347 self.root_logger.addHandler(self.sl_hdlr)
1348 self.handled = threading.Event()
1349
1350 def tearDown(self):
1351 """Shutdown the UDP server."""
1352 try:
1353 self.server.stop(2.0)
1354 self.root_logger.removeHandler(self.sl_hdlr)
1355 self.sl_hdlr.close()
1356 finally:
1357 BaseTest.tearDown(self)
1358
1359 def handle_datagram(self, request):
1360 self.log_output = request.packet
1361 self.handled.set()
1362
1363 def test_output(self):
1364 # The log message sent to the SysLogHandler is properly received.
1365 logger = logging.getLogger("slh")
1366 logger.error("sp\xe4m")
1367 self.handled.wait()
1368 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1369
1370
1371@unittest.skipUnless(threading, 'Threading required for this test.')
1372class HTTPHandlerTest(BaseTest):
1373
1374 """Test for HTTPHandler."""
1375
1376 def setUp(self):
1377 """Set up an HTTP server to receive log messages, and a HTTPHandler
1378 pointing to that server's address and port."""
1379 BaseTest.setUp(self)
1380 addr = ('localhost', 0)
1381 self.server = server = TestHTTPServer(addr, self.handle_request,
1382 0.01)
1383 server.start()
1384 server.ready.wait()
1385 host = 'localhost:%d' % server.server_port
1386 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob')
1387 self.log_data = None
1388 self.root_logger.removeHandler(self.root_logger.handlers[0])
1389 self.root_logger.addHandler(self.h_hdlr)
1390 self.handled = threading.Event()
1391
1392 def tearDown(self):
1393 """Shutdown the UDP server."""
1394 try:
1395 self.server.stop(2.0)
1396 self.root_logger.removeHandler(self.h_hdlr)
1397 self.h_hdlr.close()
1398 finally:
1399 BaseTest.tearDown(self)
1400
1401 def handle_request(self, request):
1402 self.log_data = urlparse(request.path)
1403 request.send_response(200)
1404 self.handled.set()
1405
1406 def test_output(self):
1407 # The log message sent to the SysLogHandler is properly received.
1408 logger = logging.getLogger("http")
1409 msg = "sp\xe4m"
1410 logger.error(msg)
1411 self.handled.wait()
1412 self.assertEqual(self.log_data.path, '/frob')
1413 d = parse_qs(self.log_data.query)
1414 self.assertEqual(d['name'], ['http'])
1415 self.assertEqual(d['funcName'], ['test_output'])
1416 self.assertEqual(d['msg'], [msg])
1417
1418
Christian Heimes180510d2008-03-03 19:15:45 +00001419class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001420
Christian Heimes180510d2008-03-03 19:15:45 +00001421 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001422
Christian Heimes180510d2008-03-03 19:15:45 +00001423 def setUp(self):
1424 """Create a dict to remember potentially destroyed objects."""
1425 BaseTest.setUp(self)
1426 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001427
Christian Heimes180510d2008-03-03 19:15:45 +00001428 def _watch_for_survival(self, *args):
1429 """Watch the given objects for survival, by creating weakrefs to
1430 them."""
1431 for obj in args:
1432 key = id(obj), repr(obj)
1433 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001434
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001435 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001436 """Assert that all objects watched for survival have survived."""
1437 # Trigger cycle breaking.
1438 gc.collect()
1439 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001440 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001441 if ref() is None:
1442 dead.append(repr_)
1443 if dead:
1444 self.fail("%d objects should have survived "
1445 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001446
Christian Heimes180510d2008-03-03 19:15:45 +00001447 def test_persistent_loggers(self):
1448 # Logger objects are persistent and retain their configuration, even
1449 # if visible references are destroyed.
1450 self.root_logger.setLevel(logging.INFO)
1451 foo = logging.getLogger("foo")
1452 self._watch_for_survival(foo)
1453 foo.setLevel(logging.DEBUG)
1454 self.root_logger.debug(self.next_message())
1455 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001456 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001457 ('foo', 'DEBUG', '2'),
1458 ])
1459 del foo
1460 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001461 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001462 # foo has retained its settings.
1463 bar = logging.getLogger("foo")
1464 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001465 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001466 ('foo', 'DEBUG', '2'),
1467 ('foo', 'DEBUG', '3'),
1468 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001469
Benjamin Petersonf91df042009-02-13 02:50:59 +00001470
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001471class EncodingTest(BaseTest):
1472 def test_encoding_plain_file(self):
1473 # In Python 2.x, a plain file object is treated as having no encoding.
1474 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001475 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1476 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001477 # the non-ascii data we write to the log.
1478 data = "foo\x80"
1479 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001480 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001481 log.addHandler(handler)
1482 try:
1483 # write non-ascii data to the log.
1484 log.warning(data)
1485 finally:
1486 log.removeHandler(handler)
1487 handler.close()
1488 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001489 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001490 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001491 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001492 finally:
1493 f.close()
1494 finally:
1495 if os.path.isfile(fn):
1496 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001497
Benjamin Petersonf91df042009-02-13 02:50:59 +00001498 def test_encoding_cyrillic_unicode(self):
1499 log = logging.getLogger("test")
1500 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1501 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1502 #Ensure it's written in a Cyrillic encoding
1503 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001504 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001505 stream = io.BytesIO()
1506 writer = writer_class(stream, 'strict')
1507 handler = logging.StreamHandler(writer)
1508 log.addHandler(handler)
1509 try:
1510 log.warning(message)
1511 finally:
1512 log.removeHandler(handler)
1513 handler.close()
1514 # check we wrote exactly those bytes, ignoring trailing \n etc
1515 s = stream.getvalue()
1516 #Compare against what the data should be when encoded in CP-1251
1517 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1518
1519
Georg Brandlf9734072008-12-07 15:30:06 +00001520class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001521
Georg Brandlf9734072008-12-07 15:30:06 +00001522 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001523 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001524 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001525 self.addCleanup(lambda: logging.captureWarnings(False))
1526 warnings.filterwarnings("always", category=UserWarning)
1527 stream = io.StringIO()
1528 h = logging.StreamHandler(stream)
1529 logger = logging.getLogger("py.warnings")
1530 logger.addHandler(h)
1531 warnings.warn("I'm warning you...")
1532 logger.removeHandler(h)
1533 s = stream.getvalue()
1534 h.close()
1535 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001536
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001537 #See if an explicit file uses the original implementation
1538 a_file = io.StringIO()
1539 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1540 a_file, "Dummy line")
1541 s = a_file.getvalue()
1542 a_file.close()
1543 self.assertEqual(s,
1544 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1545
1546 def test_warnings_no_handlers(self):
1547 with warnings.catch_warnings():
1548 logging.captureWarnings(True)
1549 self.addCleanup(lambda: logging.captureWarnings(False))
1550
1551 # confirm our assumption: no loggers are set
1552 logger = logging.getLogger("py.warnings")
1553 assert logger.handlers == []
1554
1555 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1556 self.assertTrue(len(logger.handlers) == 1)
1557 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001558
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001559
1560def formatFunc(format, datefmt=None):
1561 return logging.Formatter(format, datefmt)
1562
1563def handlerFunc():
1564 return logging.StreamHandler()
1565
1566class CustomHandler(logging.StreamHandler):
1567 pass
1568
1569class ConfigDictTest(BaseTest):
1570
1571 """Reading logging config from a dictionary."""
1572
1573 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1574
1575 # config0 is a standard configuration.
1576 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001577 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001578 'formatters': {
1579 'form1' : {
1580 'format' : '%(levelname)s ++ %(message)s',
1581 },
1582 },
1583 'handlers' : {
1584 'hand1' : {
1585 'class' : 'logging.StreamHandler',
1586 'formatter' : 'form1',
1587 'level' : 'NOTSET',
1588 'stream' : 'ext://sys.stdout',
1589 },
1590 },
1591 'root' : {
1592 'level' : 'WARNING',
1593 'handlers' : ['hand1'],
1594 },
1595 }
1596
1597 # config1 adds a little to the standard configuration.
1598 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001599 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001600 'formatters': {
1601 'form1' : {
1602 'format' : '%(levelname)s ++ %(message)s',
1603 },
1604 },
1605 'handlers' : {
1606 'hand1' : {
1607 'class' : 'logging.StreamHandler',
1608 'formatter' : 'form1',
1609 'level' : 'NOTSET',
1610 'stream' : 'ext://sys.stdout',
1611 },
1612 },
1613 'loggers' : {
1614 'compiler.parser' : {
1615 'level' : 'DEBUG',
1616 'handlers' : ['hand1'],
1617 },
1618 },
1619 'root' : {
1620 'level' : 'WARNING',
1621 },
1622 }
1623
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001624 # config1a moves the handler to the root. Used with config8a
1625 config1a = {
1626 'version': 1,
1627 'formatters': {
1628 'form1' : {
1629 'format' : '%(levelname)s ++ %(message)s',
1630 },
1631 },
1632 'handlers' : {
1633 'hand1' : {
1634 'class' : 'logging.StreamHandler',
1635 'formatter' : 'form1',
1636 'level' : 'NOTSET',
1637 'stream' : 'ext://sys.stdout',
1638 },
1639 },
1640 'loggers' : {
1641 'compiler.parser' : {
1642 'level' : 'DEBUG',
1643 },
1644 },
1645 'root' : {
1646 'level' : 'WARNING',
1647 'handlers' : ['hand1'],
1648 },
1649 }
1650
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001651 # config2 has a subtle configuration error that should be reported
1652 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001653 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001654 'formatters': {
1655 'form1' : {
1656 'format' : '%(levelname)s ++ %(message)s',
1657 },
1658 },
1659 'handlers' : {
1660 'hand1' : {
1661 'class' : 'logging.StreamHandler',
1662 'formatter' : 'form1',
1663 'level' : 'NOTSET',
1664 'stream' : 'ext://sys.stdbout',
1665 },
1666 },
1667 'loggers' : {
1668 'compiler.parser' : {
1669 'level' : 'DEBUG',
1670 'handlers' : ['hand1'],
1671 },
1672 },
1673 'root' : {
1674 'level' : 'WARNING',
1675 },
1676 }
1677
1678 #As config1 but with a misspelt level on a handler
1679 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001680 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001681 'formatters': {
1682 'form1' : {
1683 'format' : '%(levelname)s ++ %(message)s',
1684 },
1685 },
1686 'handlers' : {
1687 'hand1' : {
1688 'class' : 'logging.StreamHandler',
1689 'formatter' : 'form1',
1690 'level' : 'NTOSET',
1691 'stream' : 'ext://sys.stdout',
1692 },
1693 },
1694 'loggers' : {
1695 'compiler.parser' : {
1696 'level' : 'DEBUG',
1697 'handlers' : ['hand1'],
1698 },
1699 },
1700 'root' : {
1701 'level' : 'WARNING',
1702 },
1703 }
1704
1705
1706 #As config1 but with a misspelt level on a logger
1707 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001708 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001709 'formatters': {
1710 'form1' : {
1711 'format' : '%(levelname)s ++ %(message)s',
1712 },
1713 },
1714 'handlers' : {
1715 'hand1' : {
1716 'class' : 'logging.StreamHandler',
1717 'formatter' : 'form1',
1718 'level' : 'NOTSET',
1719 'stream' : 'ext://sys.stdout',
1720 },
1721 },
1722 'loggers' : {
1723 'compiler.parser' : {
1724 'level' : 'DEBUG',
1725 'handlers' : ['hand1'],
1726 },
1727 },
1728 'root' : {
1729 'level' : 'WRANING',
1730 },
1731 }
1732
1733 # config3 has a less subtle configuration error
1734 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001735 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001736 'formatters': {
1737 'form1' : {
1738 'format' : '%(levelname)s ++ %(message)s',
1739 },
1740 },
1741 'handlers' : {
1742 'hand1' : {
1743 'class' : 'logging.StreamHandler',
1744 'formatter' : 'misspelled_name',
1745 'level' : 'NOTSET',
1746 'stream' : 'ext://sys.stdout',
1747 },
1748 },
1749 'loggers' : {
1750 'compiler.parser' : {
1751 'level' : 'DEBUG',
1752 'handlers' : ['hand1'],
1753 },
1754 },
1755 'root' : {
1756 'level' : 'WARNING',
1757 },
1758 }
1759
1760 # config4 specifies a custom formatter class to be loaded
1761 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001762 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001763 'formatters': {
1764 'form1' : {
1765 '()' : __name__ + '.ExceptionFormatter',
1766 'format' : '%(levelname)s:%(name)s:%(message)s',
1767 },
1768 },
1769 'handlers' : {
1770 'hand1' : {
1771 'class' : 'logging.StreamHandler',
1772 'formatter' : 'form1',
1773 'level' : 'NOTSET',
1774 'stream' : 'ext://sys.stdout',
1775 },
1776 },
1777 'root' : {
1778 'level' : 'NOTSET',
1779 'handlers' : ['hand1'],
1780 },
1781 }
1782
1783 # As config4 but using an actual callable rather than a string
1784 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001785 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001786 'formatters': {
1787 'form1' : {
1788 '()' : ExceptionFormatter,
1789 'format' : '%(levelname)s:%(name)s:%(message)s',
1790 },
1791 'form2' : {
1792 '()' : __name__ + '.formatFunc',
1793 'format' : '%(levelname)s:%(name)s:%(message)s',
1794 },
1795 'form3' : {
1796 '()' : formatFunc,
1797 'format' : '%(levelname)s:%(name)s:%(message)s',
1798 },
1799 },
1800 'handlers' : {
1801 'hand1' : {
1802 'class' : 'logging.StreamHandler',
1803 'formatter' : 'form1',
1804 'level' : 'NOTSET',
1805 'stream' : 'ext://sys.stdout',
1806 },
1807 'hand2' : {
1808 '()' : handlerFunc,
1809 },
1810 },
1811 'root' : {
1812 'level' : 'NOTSET',
1813 'handlers' : ['hand1'],
1814 },
1815 }
1816
1817 # config5 specifies a custom handler class to be loaded
1818 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001819 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001820 'formatters': {
1821 'form1' : {
1822 'format' : '%(levelname)s ++ %(message)s',
1823 },
1824 },
1825 'handlers' : {
1826 'hand1' : {
1827 'class' : __name__ + '.CustomHandler',
1828 'formatter' : 'form1',
1829 'level' : 'NOTSET',
1830 'stream' : 'ext://sys.stdout',
1831 },
1832 },
1833 'loggers' : {
1834 'compiler.parser' : {
1835 'level' : 'DEBUG',
1836 'handlers' : ['hand1'],
1837 },
1838 },
1839 'root' : {
1840 'level' : 'WARNING',
1841 },
1842 }
1843
1844 # config6 specifies a custom handler class to be loaded
1845 # but has bad arguments
1846 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001847 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001848 'formatters': {
1849 'form1' : {
1850 'format' : '%(levelname)s ++ %(message)s',
1851 },
1852 },
1853 'handlers' : {
1854 'hand1' : {
1855 'class' : __name__ + '.CustomHandler',
1856 'formatter' : 'form1',
1857 'level' : 'NOTSET',
1858 'stream' : 'ext://sys.stdout',
1859 '9' : 'invalid parameter name',
1860 },
1861 },
1862 'loggers' : {
1863 'compiler.parser' : {
1864 'level' : 'DEBUG',
1865 'handlers' : ['hand1'],
1866 },
1867 },
1868 'root' : {
1869 'level' : 'WARNING',
1870 },
1871 }
1872
1873 #config 7 does not define compiler.parser but defines compiler.lexer
1874 #so compiler.parser should be disabled after applying it
1875 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001876 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001877 'formatters': {
1878 'form1' : {
1879 'format' : '%(levelname)s ++ %(message)s',
1880 },
1881 },
1882 'handlers' : {
1883 'hand1' : {
1884 'class' : 'logging.StreamHandler',
1885 'formatter' : 'form1',
1886 'level' : 'NOTSET',
1887 'stream' : 'ext://sys.stdout',
1888 },
1889 },
1890 'loggers' : {
1891 'compiler.lexer' : {
1892 'level' : 'DEBUG',
1893 'handlers' : ['hand1'],
1894 },
1895 },
1896 'root' : {
1897 'level' : 'WARNING',
1898 },
1899 }
1900
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001901 # config8 defines both compiler and compiler.lexer
1902 # so compiler.parser should not be disabled (since
1903 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001904 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001905 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001906 'disable_existing_loggers' : False,
1907 'formatters': {
1908 'form1' : {
1909 'format' : '%(levelname)s ++ %(message)s',
1910 },
1911 },
1912 'handlers' : {
1913 'hand1' : {
1914 'class' : 'logging.StreamHandler',
1915 'formatter' : 'form1',
1916 'level' : 'NOTSET',
1917 'stream' : 'ext://sys.stdout',
1918 },
1919 },
1920 'loggers' : {
1921 'compiler' : {
1922 'level' : 'DEBUG',
1923 'handlers' : ['hand1'],
1924 },
1925 'compiler.lexer' : {
1926 },
1927 },
1928 'root' : {
1929 'level' : 'WARNING',
1930 },
1931 }
1932
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001933 # config8a disables existing loggers
1934 config8a = {
1935 'version': 1,
1936 'disable_existing_loggers' : True,
1937 'formatters': {
1938 'form1' : {
1939 'format' : '%(levelname)s ++ %(message)s',
1940 },
1941 },
1942 'handlers' : {
1943 'hand1' : {
1944 'class' : 'logging.StreamHandler',
1945 'formatter' : 'form1',
1946 'level' : 'NOTSET',
1947 'stream' : 'ext://sys.stdout',
1948 },
1949 },
1950 'loggers' : {
1951 'compiler' : {
1952 'level' : 'DEBUG',
1953 'handlers' : ['hand1'],
1954 },
1955 'compiler.lexer' : {
1956 },
1957 },
1958 'root' : {
1959 'level' : 'WARNING',
1960 },
1961 }
1962
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001963 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001964 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001965 'formatters': {
1966 'form1' : {
1967 'format' : '%(levelname)s ++ %(message)s',
1968 },
1969 },
1970 'handlers' : {
1971 'hand1' : {
1972 'class' : 'logging.StreamHandler',
1973 'formatter' : 'form1',
1974 'level' : 'WARNING',
1975 'stream' : 'ext://sys.stdout',
1976 },
1977 },
1978 'loggers' : {
1979 'compiler.parser' : {
1980 'level' : 'WARNING',
1981 'handlers' : ['hand1'],
1982 },
1983 },
1984 'root' : {
1985 'level' : 'NOTSET',
1986 },
1987 }
1988
1989 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001990 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001991 'incremental' : True,
1992 'handlers' : {
1993 'hand1' : {
1994 'level' : 'WARNING',
1995 },
1996 },
1997 'loggers' : {
1998 'compiler.parser' : {
1999 'level' : 'INFO',
2000 },
2001 },
2002 }
2003
2004 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002005 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002006 'incremental' : True,
2007 'handlers' : {
2008 'hand1' : {
2009 'level' : 'INFO',
2010 },
2011 },
2012 'loggers' : {
2013 'compiler.parser' : {
2014 'level' : 'INFO',
2015 },
2016 },
2017 }
2018
2019 #As config1 but with a filter added
2020 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002021 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002022 'formatters': {
2023 'form1' : {
2024 'format' : '%(levelname)s ++ %(message)s',
2025 },
2026 },
2027 'filters' : {
2028 'filt1' : {
2029 'name' : 'compiler.parser',
2030 },
2031 },
2032 'handlers' : {
2033 'hand1' : {
2034 'class' : 'logging.StreamHandler',
2035 'formatter' : 'form1',
2036 'level' : 'NOTSET',
2037 'stream' : 'ext://sys.stdout',
2038 'filters' : ['filt1'],
2039 },
2040 },
2041 'loggers' : {
2042 'compiler.parser' : {
2043 'level' : 'DEBUG',
2044 'filters' : ['filt1'],
2045 },
2046 },
2047 'root' : {
2048 'level' : 'WARNING',
2049 'handlers' : ['hand1'],
2050 },
2051 }
2052
2053 #As config1 but using cfg:// references
2054 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002055 'version': 1,
2056 'true_formatters': {
2057 'form1' : {
2058 'format' : '%(levelname)s ++ %(message)s',
2059 },
2060 },
2061 'handler_configs': {
2062 'hand1' : {
2063 'class' : 'logging.StreamHandler',
2064 'formatter' : 'form1',
2065 'level' : 'NOTSET',
2066 'stream' : 'ext://sys.stdout',
2067 },
2068 },
2069 'formatters' : 'cfg://true_formatters',
2070 'handlers' : {
2071 'hand1' : 'cfg://handler_configs[hand1]',
2072 },
2073 'loggers' : {
2074 'compiler.parser' : {
2075 'level' : 'DEBUG',
2076 'handlers' : ['hand1'],
2077 },
2078 },
2079 'root' : {
2080 'level' : 'WARNING',
2081 },
2082 }
2083
2084 #As config11 but missing the version key
2085 config12 = {
2086 'true_formatters': {
2087 'form1' : {
2088 'format' : '%(levelname)s ++ %(message)s',
2089 },
2090 },
2091 'handler_configs': {
2092 'hand1' : {
2093 'class' : 'logging.StreamHandler',
2094 'formatter' : 'form1',
2095 'level' : 'NOTSET',
2096 'stream' : 'ext://sys.stdout',
2097 },
2098 },
2099 'formatters' : 'cfg://true_formatters',
2100 'handlers' : {
2101 'hand1' : 'cfg://handler_configs[hand1]',
2102 },
2103 'loggers' : {
2104 'compiler.parser' : {
2105 'level' : 'DEBUG',
2106 'handlers' : ['hand1'],
2107 },
2108 },
2109 'root' : {
2110 'level' : 'WARNING',
2111 },
2112 }
2113
2114 #As config11 but using an unsupported version
2115 config13 = {
2116 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002117 'true_formatters': {
2118 'form1' : {
2119 'format' : '%(levelname)s ++ %(message)s',
2120 },
2121 },
2122 'handler_configs': {
2123 'hand1' : {
2124 'class' : 'logging.StreamHandler',
2125 'formatter' : 'form1',
2126 'level' : 'NOTSET',
2127 'stream' : 'ext://sys.stdout',
2128 },
2129 },
2130 'formatters' : 'cfg://true_formatters',
2131 'handlers' : {
2132 'hand1' : 'cfg://handler_configs[hand1]',
2133 },
2134 'loggers' : {
2135 'compiler.parser' : {
2136 'level' : 'DEBUG',
2137 'handlers' : ['hand1'],
2138 },
2139 },
2140 'root' : {
2141 'level' : 'WARNING',
2142 },
2143 }
2144
2145 def apply_config(self, conf):
2146 logging.config.dictConfig(conf)
2147
2148 def test_config0_ok(self):
2149 # A simple config which overrides the default settings.
2150 with captured_stdout() as output:
2151 self.apply_config(self.config0)
2152 logger = logging.getLogger()
2153 # Won't output anything
2154 logger.info(self.next_message())
2155 # Outputs a message
2156 logger.error(self.next_message())
2157 self.assert_log_lines([
2158 ('ERROR', '2'),
2159 ], stream=output)
2160 # Original logger output is empty.
2161 self.assert_log_lines([])
2162
2163 def test_config1_ok(self, config=config1):
2164 # A config defining a sub-parser as well.
2165 with captured_stdout() as output:
2166 self.apply_config(config)
2167 logger = logging.getLogger("compiler.parser")
2168 # Both will output a message
2169 logger.info(self.next_message())
2170 logger.error(self.next_message())
2171 self.assert_log_lines([
2172 ('INFO', '1'),
2173 ('ERROR', '2'),
2174 ], stream=output)
2175 # Original logger output is empty.
2176 self.assert_log_lines([])
2177
2178 def test_config2_failure(self):
2179 # A simple config which overrides the default settings.
2180 self.assertRaises(Exception, self.apply_config, self.config2)
2181
2182 def test_config2a_failure(self):
2183 # A simple config which overrides the default settings.
2184 self.assertRaises(Exception, self.apply_config, self.config2a)
2185
2186 def test_config2b_failure(self):
2187 # A simple config which overrides the default settings.
2188 self.assertRaises(Exception, self.apply_config, self.config2b)
2189
2190 def test_config3_failure(self):
2191 # A simple config which overrides the default settings.
2192 self.assertRaises(Exception, self.apply_config, self.config3)
2193
2194 def test_config4_ok(self):
2195 # A config specifying a custom formatter class.
2196 with captured_stdout() as output:
2197 self.apply_config(self.config4)
2198 #logger = logging.getLogger()
2199 try:
2200 raise RuntimeError()
2201 except RuntimeError:
2202 logging.exception("just testing")
2203 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002204 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002205 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2206 # Original logger output is empty
2207 self.assert_log_lines([])
2208
2209 def test_config4a_ok(self):
2210 # A config specifying a custom formatter class.
2211 with captured_stdout() as output:
2212 self.apply_config(self.config4a)
2213 #logger = logging.getLogger()
2214 try:
2215 raise RuntimeError()
2216 except RuntimeError:
2217 logging.exception("just testing")
2218 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002219 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002220 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2221 # Original logger output is empty
2222 self.assert_log_lines([])
2223
2224 def test_config5_ok(self):
2225 self.test_config1_ok(config=self.config5)
2226
2227 def test_config6_failure(self):
2228 self.assertRaises(Exception, self.apply_config, self.config6)
2229
2230 def test_config7_ok(self):
2231 with captured_stdout() as output:
2232 self.apply_config(self.config1)
2233 logger = logging.getLogger("compiler.parser")
2234 # Both will output a message
2235 logger.info(self.next_message())
2236 logger.error(self.next_message())
2237 self.assert_log_lines([
2238 ('INFO', '1'),
2239 ('ERROR', '2'),
2240 ], stream=output)
2241 # Original logger output is empty.
2242 self.assert_log_lines([])
2243 with captured_stdout() as output:
2244 self.apply_config(self.config7)
2245 logger = logging.getLogger("compiler.parser")
2246 self.assertTrue(logger.disabled)
2247 logger = logging.getLogger("compiler.lexer")
2248 # Both will output a message
2249 logger.info(self.next_message())
2250 logger.error(self.next_message())
2251 self.assert_log_lines([
2252 ('INFO', '3'),
2253 ('ERROR', '4'),
2254 ], stream=output)
2255 # Original logger output is empty.
2256 self.assert_log_lines([])
2257
2258 #Same as test_config_7_ok but don't disable old loggers.
2259 def test_config_8_ok(self):
2260 with captured_stdout() as output:
2261 self.apply_config(self.config1)
2262 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002263 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002264 logger.info(self.next_message())
2265 logger.error(self.next_message())
2266 self.assert_log_lines([
2267 ('INFO', '1'),
2268 ('ERROR', '2'),
2269 ], stream=output)
2270 # Original logger output is empty.
2271 self.assert_log_lines([])
2272 with captured_stdout() as output:
2273 self.apply_config(self.config8)
2274 logger = logging.getLogger("compiler.parser")
2275 self.assertFalse(logger.disabled)
2276 # Both will output a message
2277 logger.info(self.next_message())
2278 logger.error(self.next_message())
2279 logger = logging.getLogger("compiler.lexer")
2280 # Both will output a message
2281 logger.info(self.next_message())
2282 logger.error(self.next_message())
2283 self.assert_log_lines([
2284 ('INFO', '3'),
2285 ('ERROR', '4'),
2286 ('INFO', '5'),
2287 ('ERROR', '6'),
2288 ], stream=output)
2289 # Original logger output is empty.
2290 self.assert_log_lines([])
2291
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002292 def test_config_8a_ok(self):
2293 with captured_stdout() as output:
2294 self.apply_config(self.config1a)
2295 logger = logging.getLogger("compiler.parser")
2296 # See issue #11424. compiler-hyphenated sorts
2297 # between compiler and compiler.xyz and this
2298 # was preventing compiler.xyz from being included
2299 # in the child loggers of compiler because of an
2300 # overzealous loop termination condition.
2301 hyphenated = logging.getLogger('compiler-hyphenated')
2302 # All will output a message
2303 logger.info(self.next_message())
2304 logger.error(self.next_message())
2305 hyphenated.critical(self.next_message())
2306 self.assert_log_lines([
2307 ('INFO', '1'),
2308 ('ERROR', '2'),
2309 ('CRITICAL', '3'),
2310 ], stream=output)
2311 # Original logger output is empty.
2312 self.assert_log_lines([])
2313 with captured_stdout() as output:
2314 self.apply_config(self.config8a)
2315 logger = logging.getLogger("compiler.parser")
2316 self.assertFalse(logger.disabled)
2317 # Both will output a message
2318 logger.info(self.next_message())
2319 logger.error(self.next_message())
2320 logger = logging.getLogger("compiler.lexer")
2321 # Both will output a message
2322 logger.info(self.next_message())
2323 logger.error(self.next_message())
2324 # Will not appear
2325 hyphenated.critical(self.next_message())
2326 self.assert_log_lines([
2327 ('INFO', '4'),
2328 ('ERROR', '5'),
2329 ('INFO', '6'),
2330 ('ERROR', '7'),
2331 ], stream=output)
2332 # Original logger output is empty.
2333 self.assert_log_lines([])
2334
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002335 def test_config_9_ok(self):
2336 with captured_stdout() as output:
2337 self.apply_config(self.config9)
2338 logger = logging.getLogger("compiler.parser")
2339 #Nothing will be output since both handler and logger are set to WARNING
2340 logger.info(self.next_message())
2341 self.assert_log_lines([], stream=output)
2342 self.apply_config(self.config9a)
2343 #Nothing will be output since both handler is still set to WARNING
2344 logger.info(self.next_message())
2345 self.assert_log_lines([], stream=output)
2346 self.apply_config(self.config9b)
2347 #Message should now be output
2348 logger.info(self.next_message())
2349 self.assert_log_lines([
2350 ('INFO', '3'),
2351 ], stream=output)
2352
2353 def test_config_10_ok(self):
2354 with captured_stdout() as output:
2355 self.apply_config(self.config10)
2356 logger = logging.getLogger("compiler.parser")
2357 logger.warning(self.next_message())
2358 logger = logging.getLogger('compiler')
2359 #Not output, because filtered
2360 logger.warning(self.next_message())
2361 logger = logging.getLogger('compiler.lexer')
2362 #Not output, because filtered
2363 logger.warning(self.next_message())
2364 logger = logging.getLogger("compiler.parser.codegen")
2365 #Output, as not filtered
2366 logger.error(self.next_message())
2367 self.assert_log_lines([
2368 ('WARNING', '1'),
2369 ('ERROR', '4'),
2370 ], stream=output)
2371
2372 def test_config11_ok(self):
2373 self.test_config1_ok(self.config11)
2374
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002375 def test_config12_failure(self):
2376 self.assertRaises(Exception, self.apply_config, self.config12)
2377
2378 def test_config13_failure(self):
2379 self.assertRaises(Exception, self.apply_config, self.config13)
2380
Victor Stinner45df8202010-04-28 22:31:17 +00002381 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002382 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002383 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002384 # Ask for a randomly assigned port (by using port 0)
2385 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002386 t.start()
2387 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002388 # Now get the port allocated
2389 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002390 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002391 try:
2392 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2393 sock.settimeout(2.0)
2394 sock.connect(('localhost', port))
2395
2396 slen = struct.pack('>L', len(text))
2397 s = slen + text
2398 sentsofar = 0
2399 left = len(s)
2400 while left > 0:
2401 sent = sock.send(s[sentsofar:])
2402 sentsofar += sent
2403 left -= sent
2404 sock.close()
2405 finally:
2406 t.ready.wait(2.0)
2407 logging.config.stopListening()
2408 t.join(2.0)
2409
2410 def test_listen_config_10_ok(self):
2411 with captured_stdout() as output:
2412 self.setup_via_listener(json.dumps(self.config10))
2413 logger = logging.getLogger("compiler.parser")
2414 logger.warning(self.next_message())
2415 logger = logging.getLogger('compiler')
2416 #Not output, because filtered
2417 logger.warning(self.next_message())
2418 logger = logging.getLogger('compiler.lexer')
2419 #Not output, because filtered
2420 logger.warning(self.next_message())
2421 logger = logging.getLogger("compiler.parser.codegen")
2422 #Output, as not filtered
2423 logger.error(self.next_message())
2424 self.assert_log_lines([
2425 ('WARNING', '1'),
2426 ('ERROR', '4'),
2427 ], stream=output)
2428
2429 def test_listen_config_1_ok(self):
2430 with captured_stdout() as output:
2431 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2432 logger = logging.getLogger("compiler.parser")
2433 # Both will output a message
2434 logger.info(self.next_message())
2435 logger.error(self.next_message())
2436 self.assert_log_lines([
2437 ('INFO', '1'),
2438 ('ERROR', '2'),
2439 ], stream=output)
2440 # Original logger output is empty.
2441 self.assert_log_lines([])
2442
Vinay Sajip373baef2011-04-26 20:05:24 +01002443 def test_baseconfig(self):
2444 d = {
2445 'atuple': (1, 2, 3),
2446 'alist': ['a', 'b', 'c'],
2447 'adict': {'d': 'e', 'f': 3 },
2448 'nest1': ('g', ('h', 'i'), 'j'),
2449 'nest2': ['k', ['l', 'm'], 'n'],
2450 'nest3': ['o', 'cfg://alist', 'p'],
2451 }
2452 bc = logging.config.BaseConfigurator(d)
2453 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2454 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2455 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2456 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2457 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2458 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2459 v = bc.convert('cfg://nest3')
2460 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2461 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2462 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2463 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002464
2465class ManagerTest(BaseTest):
2466 def test_manager_loggerclass(self):
2467 logged = []
2468
2469 class MyLogger(logging.Logger):
2470 def _log(self, level, msg, args, exc_info=None, extra=None):
2471 logged.append(msg)
2472
2473 man = logging.Manager(None)
2474 self.assertRaises(TypeError, man.setLoggerClass, int)
2475 man.setLoggerClass(MyLogger)
2476 logger = man.getLogger('test')
2477 logger.warning('should appear in logged')
2478 logging.warning('should not appear in logged')
2479
2480 self.assertEqual(logged, ['should appear in logged'])
2481
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002482 def test_set_log_record_factory(self):
2483 man = logging.Manager(None)
2484 expected = object()
2485 man.setLogRecordFactory(expected)
2486 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002487
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002488class ChildLoggerTest(BaseTest):
2489 def test_child_loggers(self):
2490 r = logging.getLogger()
2491 l1 = logging.getLogger('abc')
2492 l2 = logging.getLogger('def.ghi')
2493 c1 = r.getChild('xyz')
2494 c2 = r.getChild('uvw.xyz')
2495 self.assertTrue(c1 is logging.getLogger('xyz'))
2496 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2497 c1 = l1.getChild('def')
2498 c2 = c1.getChild('ghi')
2499 c3 = l1.getChild('def.ghi')
2500 self.assertTrue(c1 is logging.getLogger('abc.def'))
2501 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2502 self.assertTrue(c2 is c3)
2503
2504
Vinay Sajip6fac8172010-10-19 20:44:14 +00002505class DerivedLogRecord(logging.LogRecord):
2506 pass
2507
Vinay Sajip61561522010-12-03 11:50:38 +00002508class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002509
2510 def setUp(self):
2511 class CheckingFilter(logging.Filter):
2512 def __init__(self, cls):
2513 self.cls = cls
2514
2515 def filter(self, record):
2516 t = type(record)
2517 if t is not self.cls:
2518 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2519 self.cls)
2520 raise TypeError(msg)
2521 return True
2522
2523 BaseTest.setUp(self)
2524 self.filter = CheckingFilter(DerivedLogRecord)
2525 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002526 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002527
2528 def tearDown(self):
2529 self.root_logger.removeFilter(self.filter)
2530 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002531 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002532
2533 def test_logrecord_class(self):
2534 self.assertRaises(TypeError, self.root_logger.warning,
2535 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002536 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002537 self.root_logger.error(self.next_message())
2538 self.assert_log_lines([
2539 ('root', 'ERROR', '2'),
2540 ])
2541
2542
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002543class QueueHandlerTest(BaseTest):
2544 # Do not bother with a logger name group.
2545 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2546
2547 def setUp(self):
2548 BaseTest.setUp(self)
2549 self.queue = queue.Queue(-1)
2550 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2551 self.que_logger = logging.getLogger('que')
2552 self.que_logger.propagate = False
2553 self.que_logger.setLevel(logging.WARNING)
2554 self.que_logger.addHandler(self.que_hdlr)
2555
2556 def tearDown(self):
2557 self.que_hdlr.close()
2558 BaseTest.tearDown(self)
2559
2560 def test_queue_handler(self):
2561 self.que_logger.debug(self.next_message())
2562 self.assertRaises(queue.Empty, self.queue.get_nowait)
2563 self.que_logger.info(self.next_message())
2564 self.assertRaises(queue.Empty, self.queue.get_nowait)
2565 msg = self.next_message()
2566 self.que_logger.warning(msg)
2567 data = self.queue.get_nowait()
2568 self.assertTrue(isinstance(data, logging.LogRecord))
2569 self.assertEqual(data.name, self.que_logger.name)
2570 self.assertEqual((data.msg, data.args), (msg, None))
2571
Vinay Sajipe723e962011-04-15 22:27:17 +01002572 def test_queue_listener(self):
2573 handler = TestHandler(Matcher())
2574 listener = logging.handlers.QueueListener(self.queue, handler)
2575 listener.start()
2576 try:
2577 self.que_logger.warning(self.next_message())
2578 self.que_logger.error(self.next_message())
2579 self.que_logger.critical(self.next_message())
2580 finally:
2581 listener.stop()
2582 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2583 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2584 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2585
Vinay Sajip37eb3382011-04-26 20:26:41 +01002586ZERO = datetime.timedelta(0)
2587
2588class UTC(datetime.tzinfo):
2589 def utcoffset(self, dt):
2590 return ZERO
2591
2592 dst = utcoffset
2593
2594 def tzname(self, dt):
2595 return 'UTC'
2596
2597utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002598
Vinay Sajipa39c5712010-10-25 13:57:39 +00002599class FormatterTest(unittest.TestCase):
2600 def setUp(self):
2601 self.common = {
2602 'name': 'formatter.test',
2603 'level': logging.DEBUG,
2604 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2605 'lineno': 42,
2606 'exc_info': None,
2607 'func': None,
2608 'msg': 'Message with %d %s',
2609 'args': (2, 'placeholders'),
2610 }
2611 self.variants = {
2612 }
2613
2614 def get_record(self, name=None):
2615 result = dict(self.common)
2616 if name is not None:
2617 result.update(self.variants[name])
2618 return logging.makeLogRecord(result)
2619
2620 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002621 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002622 r = self.get_record()
2623 f = logging.Formatter('${%(message)s}')
2624 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2625 f = logging.Formatter('%(random)s')
2626 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002627 self.assertFalse(f.usesTime())
2628 f = logging.Formatter('%(asctime)s')
2629 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002630 f = logging.Formatter('%(asctime)-15s')
2631 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002632 f = logging.Formatter('asctime')
2633 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002634
2635 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002636 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002637 r = self.get_record()
2638 f = logging.Formatter('$%{message}%$', style='{')
2639 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2640 f = logging.Formatter('{random}', style='{')
2641 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002642 self.assertFalse(f.usesTime())
2643 f = logging.Formatter('{asctime}', style='{')
2644 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002645 f = logging.Formatter('{asctime!s:15}', style='{')
2646 self.assertTrue(f.usesTime())
2647 f = logging.Formatter('{asctime:15}', style='{')
2648 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002649 f = logging.Formatter('asctime', style='{')
2650 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002651
2652 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002653 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002654 r = self.get_record()
2655 f = logging.Formatter('$message', style='$')
2656 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2657 f = logging.Formatter('$$%${message}%$$', style='$')
2658 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2659 f = logging.Formatter('${random}', style='$')
2660 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002661 self.assertFalse(f.usesTime())
2662 f = logging.Formatter('${asctime}', style='$')
2663 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002664 f = logging.Formatter('${asctime', style='$')
2665 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002666 f = logging.Formatter('$asctime', style='$')
2667 self.assertTrue(f.usesTime())
2668 f = logging.Formatter('asctime', style='$')
2669 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002670
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002671 def test_invalid_style(self):
2672 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2673
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002674 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002675 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002676 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002677 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002678 r.msecs = 123
2679 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002680 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002681 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2682 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002683 f.format(r)
2684 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2685
2686class TestBufferingFormatter(logging.BufferingFormatter):
2687 def formatHeader(self, records):
2688 return '[(%d)' % len(records)
2689
2690 def formatFooter(self, records):
2691 return '(%d)]' % len(records)
2692
2693class BufferingFormatterTest(unittest.TestCase):
2694 def setUp(self):
2695 self.records = [
2696 logging.makeLogRecord({'msg': 'one'}),
2697 logging.makeLogRecord({'msg': 'two'}),
2698 ]
2699
2700 def test_default(self):
2701 f = logging.BufferingFormatter()
2702 self.assertEqual('', f.format([]))
2703 self.assertEqual('onetwo', f.format(self.records))
2704
2705 def test_custom(self):
2706 f = TestBufferingFormatter()
2707 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2708 lf = logging.Formatter('<%(message)s>')
2709 f = TestBufferingFormatter(lf)
2710 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002711
2712class ExceptionTest(BaseTest):
2713 def test_formatting(self):
2714 r = self.root_logger
2715 h = RecordingHandler()
2716 r.addHandler(h)
2717 try:
2718 raise RuntimeError('deliberate mistake')
2719 except:
2720 logging.exception('failed', stack_info=True)
2721 r.removeHandler(h)
2722 h.close()
2723 r = h.records[0]
2724 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2725 'call last):\n'))
2726 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2727 'deliberate mistake'))
2728 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2729 'call last):\n'))
2730 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2731 'stack_info=True)'))
2732
2733
Vinay Sajip5a27d402010-12-10 11:42:57 +00002734class LastResortTest(BaseTest):
2735 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002736 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002737 root = self.root_logger
2738 root.removeHandler(self.root_hdlr)
2739 old_stderr = sys.stderr
2740 old_lastresort = logging.lastResort
2741 old_raise_exceptions = logging.raiseExceptions
2742 try:
2743 sys.stderr = sio = io.StringIO()
2744 root.warning('This is your final chance!')
2745 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2746 #No handlers and no last resort, so 'No handlers' message
2747 logging.lastResort = None
2748 sys.stderr = sio = io.StringIO()
2749 root.warning('This is your final chance!')
2750 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2751 # 'No handlers' message only printed once
2752 sys.stderr = sio = io.StringIO()
2753 root.warning('This is your final chance!')
2754 self.assertEqual(sio.getvalue(), '')
2755 root.manager.emittedNoHandlerWarning = False
2756 #If raiseExceptions is False, no message is printed
2757 logging.raiseExceptions = False
2758 sys.stderr = sio = io.StringIO()
2759 root.warning('This is your final chance!')
2760 self.assertEqual(sio.getvalue(), '')
2761 finally:
2762 sys.stderr = old_stderr
2763 root.addHandler(self.root_hdlr)
2764 logging.lastResort = old_lastresort
2765 logging.raiseExceptions = old_raise_exceptions
2766
2767
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002768class FakeHandler:
2769
2770 def __init__(self, identifier, called):
2771 for method in ('acquire', 'flush', 'close', 'release'):
2772 setattr(self, method, self.record_call(identifier, method, called))
2773
2774 def record_call(self, identifier, method_name, called):
2775 def inner():
2776 called.append('{} - {}'.format(identifier, method_name))
2777 return inner
2778
2779
2780class RecordingHandler(logging.NullHandler):
2781
2782 def __init__(self, *args, **kwargs):
2783 super(RecordingHandler, self).__init__(*args, **kwargs)
2784 self.records = []
2785
2786 def handle(self, record):
2787 """Keep track of all the emitted records."""
2788 self.records.append(record)
2789
2790
2791class ShutdownTest(BaseTest):
2792
Vinay Sajip5e66b162011-04-20 15:41:14 +01002793 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002794
2795 def setUp(self):
2796 super(ShutdownTest, self).setUp()
2797 self.called = []
2798
2799 raise_exceptions = logging.raiseExceptions
2800 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2801
2802 def raise_error(self, error):
2803 def inner():
2804 raise error()
2805 return inner
2806
2807 def test_no_failure(self):
2808 # create some fake handlers
2809 handler0 = FakeHandler(0, self.called)
2810 handler1 = FakeHandler(1, self.called)
2811 handler2 = FakeHandler(2, self.called)
2812
2813 # create live weakref to those handlers
2814 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2815
2816 logging.shutdown(handlerList=list(handlers))
2817
2818 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2819 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2820 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2821 self.assertEqual(expected, self.called)
2822
2823 def _test_with_failure_in_method(self, method, error):
2824 handler = FakeHandler(0, self.called)
2825 setattr(handler, method, self.raise_error(error))
2826 handlers = [logging.weakref.ref(handler)]
2827
2828 logging.shutdown(handlerList=list(handlers))
2829
2830 self.assertEqual('0 - release', self.called[-1])
2831
2832 def test_with_ioerror_in_acquire(self):
2833 self._test_with_failure_in_method('acquire', IOError)
2834
2835 def test_with_ioerror_in_flush(self):
2836 self._test_with_failure_in_method('flush', IOError)
2837
2838 def test_with_ioerror_in_close(self):
2839 self._test_with_failure_in_method('close', IOError)
2840
2841 def test_with_valueerror_in_acquire(self):
2842 self._test_with_failure_in_method('acquire', ValueError)
2843
2844 def test_with_valueerror_in_flush(self):
2845 self._test_with_failure_in_method('flush', ValueError)
2846
2847 def test_with_valueerror_in_close(self):
2848 self._test_with_failure_in_method('close', ValueError)
2849
2850 def test_with_other_error_in_acquire_without_raise(self):
2851 logging.raiseExceptions = False
2852 self._test_with_failure_in_method('acquire', IndexError)
2853
2854 def test_with_other_error_in_flush_without_raise(self):
2855 logging.raiseExceptions = False
2856 self._test_with_failure_in_method('flush', IndexError)
2857
2858 def test_with_other_error_in_close_without_raise(self):
2859 logging.raiseExceptions = False
2860 self._test_with_failure_in_method('close', IndexError)
2861
2862 def test_with_other_error_in_acquire_with_raise(self):
2863 logging.raiseExceptions = True
2864 self.assertRaises(IndexError, self._test_with_failure_in_method,
2865 'acquire', IndexError)
2866
2867 def test_with_other_error_in_flush_with_raise(self):
2868 logging.raiseExceptions = True
2869 self.assertRaises(IndexError, self._test_with_failure_in_method,
2870 'flush', IndexError)
2871
2872 def test_with_other_error_in_close_with_raise(self):
2873 logging.raiseExceptions = True
2874 self.assertRaises(IndexError, self._test_with_failure_in_method,
2875 'close', IndexError)
2876
2877
2878class ModuleLevelMiscTest(BaseTest):
2879
Vinay Sajip5e66b162011-04-20 15:41:14 +01002880 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002881
2882 def test_disable(self):
2883 old_disable = logging.root.manager.disable
2884 # confirm our assumptions are correct
2885 assert old_disable == 0
2886 self.addCleanup(lambda: logging.disable(old_disable))
2887
2888 logging.disable(83)
2889 self.assertEqual(logging.root.manager.disable, 83)
2890
2891 def _test_log(self, method, level=None):
2892 called = []
2893 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002894 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002895
2896 recording = RecordingHandler()
2897 logging.root.addHandler(recording)
2898
2899 log_method = getattr(logging, method)
2900 if level is not None:
2901 log_method(level, "test me: %r", recording)
2902 else:
2903 log_method("test me: %r", recording)
2904
2905 self.assertEqual(len(recording.records), 1)
2906 record = recording.records[0]
2907 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2908
2909 expected_level = level if level is not None else getattr(logging, method.upper())
2910 self.assertEqual(record.levelno, expected_level)
2911
2912 # basicConfig was not called!
2913 self.assertEqual(called, [])
2914
2915 def test_log(self):
2916 self._test_log('log', logging.ERROR)
2917
2918 def test_debug(self):
2919 self._test_log('debug')
2920
2921 def test_info(self):
2922 self._test_log('info')
2923
2924 def test_warning(self):
2925 self._test_log('warning')
2926
2927 def test_error(self):
2928 self._test_log('error')
2929
2930 def test_critical(self):
2931 self._test_log('critical')
2932
2933 def test_set_logger_class(self):
2934 self.assertRaises(TypeError, logging.setLoggerClass, object)
2935
2936 class MyLogger(logging.Logger):
2937 pass
2938
2939 logging.setLoggerClass(MyLogger)
2940 self.assertEqual(logging.getLoggerClass(), MyLogger)
2941
2942 logging.setLoggerClass(logging.Logger)
2943 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2944
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002945class LogRecordTest(BaseTest):
2946 def test_str_rep(self):
2947 r = logging.makeLogRecord({})
2948 s = str(r)
2949 self.assertTrue(s.startswith('<LogRecord: '))
2950 self.assertTrue(s.endswith('>'))
2951
2952 def test_dict_arg(self):
2953 h = RecordingHandler()
2954 r = logging.getLogger()
2955 r.addHandler(h)
2956 d = {'less' : 'more' }
2957 logging.warning('less is %(less)s', d)
2958 self.assertIs(h.records[0].args, d)
2959 self.assertEqual(h.records[0].message, 'less is more')
2960 r.removeHandler(h)
2961 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002962
Vinay Sajip7fe1d512011-04-28 12:04:58 +01002963 def test_multiprocessing(self):
2964 r = logging.makeLogRecord({})
2965 self.assertEqual(r.processName, 'MainProcess')
2966 import multiprocessing as mp
2967 r = logging.makeLogRecord({})
2968 self.assertEqual(r.processName, mp.current_process().name)
2969
2970 def test_optional(self):
2971 r = logging.makeLogRecord({})
2972 NOT_NONE = self.assertIsNotNone
2973 NOT_NONE(r.thread)
2974 NOT_NONE(r.threadName)
2975 NOT_NONE(r.process)
2976 NOT_NONE(r.processName)
2977 log_threads = logging.logThreads
2978 log_processes = logging.logProcesses
2979 log_multiprocessing = logging.logMultiprocessing
2980 try:
2981 logging.logThreads = False
2982 logging.logProcesses = False
2983 logging.logMultiprocessing = False
2984 r = logging.makeLogRecord({})
2985 NONE = self.assertIsNone
2986 NONE(r.thread)
2987 NONE(r.threadName)
2988 NONE(r.process)
2989 NONE(r.processName)
2990 finally:
2991 logging.logThreads = log_threads
2992 logging.logProcesses = log_processes
2993 logging.logMultiprocessing = log_multiprocessing
2994
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002995class BasicConfigTest(unittest.TestCase):
2996
Vinay Sajip95bf5042011-04-20 11:50:56 +01002997 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002998
2999 def setUp(self):
3000 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003001 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003002 self.saved_handlers = logging._handlers.copy()
3003 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003004 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003005 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003006 logging.root.handlers = []
3007
3008 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003009 for h in logging.root.handlers[:]:
3010 logging.root.removeHandler(h)
3011 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003012 super(BasicConfigTest, self).tearDown()
3013
Vinay Sajip3def7e02011-04-20 10:58:06 +01003014 def cleanup(self):
3015 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003016 logging._handlers.clear()
3017 logging._handlers.update(self.saved_handlers)
3018 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003019 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003020
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003021 def test_no_kwargs(self):
3022 logging.basicConfig()
3023
3024 # handler defaults to a StreamHandler to sys.stderr
3025 self.assertEqual(len(logging.root.handlers), 1)
3026 handler = logging.root.handlers[0]
3027 self.assertIsInstance(handler, logging.StreamHandler)
3028 self.assertEqual(handler.stream, sys.stderr)
3029
3030 formatter = handler.formatter
3031 # format defaults to logging.BASIC_FORMAT
3032 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3033 # datefmt defaults to None
3034 self.assertIsNone(formatter.datefmt)
3035 # style defaults to %
3036 self.assertIsInstance(formatter._style, logging.PercentStyle)
3037
3038 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003039 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003040
3041 def test_filename(self):
3042 logging.basicConfig(filename='test.log')
3043
3044 self.assertEqual(len(logging.root.handlers), 1)
3045 handler = logging.root.handlers[0]
3046 self.assertIsInstance(handler, logging.FileHandler)
3047
3048 expected = logging.FileHandler('test.log', 'a')
3049 self.addCleanup(expected.close)
3050 self.assertEqual(handler.stream.mode, expected.stream.mode)
3051 self.assertEqual(handler.stream.name, expected.stream.name)
3052
3053 def test_filemode(self):
3054 logging.basicConfig(filename='test.log', filemode='wb')
3055
3056 handler = logging.root.handlers[0]
3057 expected = logging.FileHandler('test.log', 'wb')
3058 self.addCleanup(expected.close)
3059 self.assertEqual(handler.stream.mode, expected.stream.mode)
3060
3061 def test_stream(self):
3062 stream = io.StringIO()
3063 self.addCleanup(stream.close)
3064 logging.basicConfig(stream=stream)
3065
3066 self.assertEqual(len(logging.root.handlers), 1)
3067 handler = logging.root.handlers[0]
3068 self.assertIsInstance(handler, logging.StreamHandler)
3069 self.assertEqual(handler.stream, stream)
3070
3071 def test_format(self):
3072 logging.basicConfig(format='foo')
3073
3074 formatter = logging.root.handlers[0].formatter
3075 self.assertEqual(formatter._style._fmt, 'foo')
3076
3077 def test_datefmt(self):
3078 logging.basicConfig(datefmt='bar')
3079
3080 formatter = logging.root.handlers[0].formatter
3081 self.assertEqual(formatter.datefmt, 'bar')
3082
3083 def test_style(self):
3084 logging.basicConfig(style='$')
3085
3086 formatter = logging.root.handlers[0].formatter
3087 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3088
3089 def test_level(self):
3090 old_level = logging.root.level
3091 self.addCleanup(lambda: logging.root.setLevel(old_level))
3092
3093 logging.basicConfig(level=57)
3094 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003095 # Test that second call has no effect
3096 logging.basicConfig(level=58)
3097 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003098
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003099 def test_incompatible(self):
3100 assertRaises = self.assertRaises
3101 handlers = [logging.StreamHandler()]
3102 stream = sys.stderr
3103 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3104 stream=stream)
3105 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3106 handlers=handlers)
3107 assertRaises(ValueError, logging.basicConfig, stream=stream,
3108 handlers=handlers)
3109
3110 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003111 handlers = [
3112 logging.StreamHandler(),
3113 logging.StreamHandler(sys.stdout),
3114 logging.StreamHandler(),
3115 ]
3116 f = logging.Formatter()
3117 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003118 logging.basicConfig(handlers=handlers)
3119 self.assertIs(handlers[0], logging.root.handlers[0])
3120 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003121 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003122 self.assertIsNotNone(handlers[0].formatter)
3123 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003124 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003125 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3126
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003127 def _test_log(self, method, level=None):
3128 # logging.root has no handlers so basicConfig should be called
3129 called = []
3130
3131 old_basic_config = logging.basicConfig
3132 def my_basic_config(*a, **kw):
3133 old_basic_config()
3134 old_level = logging.root.level
3135 logging.root.setLevel(100) # avoid having messages in stderr
3136 self.addCleanup(lambda: logging.root.setLevel(old_level))
3137 called.append((a, kw))
3138
3139 patch(self, logging, 'basicConfig', my_basic_config)
3140
3141 log_method = getattr(logging, method)
3142 if level is not None:
3143 log_method(level, "test me")
3144 else:
3145 log_method("test me")
3146
3147 # basicConfig was called with no arguments
3148 self.assertEqual(called, [((), {})])
3149
3150 def test_log(self):
3151 self._test_log('log', logging.WARNING)
3152
3153 def test_debug(self):
3154 self._test_log('debug')
3155
3156 def test_info(self):
3157 self._test_log('info')
3158
3159 def test_warning(self):
3160 self._test_log('warning')
3161
3162 def test_error(self):
3163 self._test_log('error')
3164
3165 def test_critical(self):
3166 self._test_log('critical')
3167
3168
3169class LoggerAdapterTest(unittest.TestCase):
3170
3171 def setUp(self):
3172 super(LoggerAdapterTest, self).setUp()
3173 old_handler_list = logging._handlerList[:]
3174
3175 self.recording = RecordingHandler()
3176 self.logger = logging.root
3177 self.logger.addHandler(self.recording)
3178 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3179 self.addCleanup(self.recording.close)
3180
3181 def cleanup():
3182 logging._handlerList[:] = old_handler_list
3183
3184 self.addCleanup(cleanup)
3185 self.addCleanup(logging.shutdown)
3186 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3187
3188 def test_exception(self):
3189 msg = 'testing exception: %r'
3190 exc = None
3191 try:
3192 assert False
3193 except AssertionError as e:
3194 exc = e
3195 self.adapter.exception(msg, self.recording)
3196
3197 self.assertEqual(len(self.recording.records), 1)
3198 record = self.recording.records[0]
3199 self.assertEqual(record.levelno, logging.ERROR)
3200 self.assertEqual(record.msg, msg)
3201 self.assertEqual(record.args, (self.recording,))
3202 self.assertEqual(record.exc_info,
3203 (exc.__class__, exc, exc.__traceback__))
3204
3205 def test_critical(self):
3206 msg = 'critical test! %r'
3207 self.adapter.critical(msg, self.recording)
3208
3209 self.assertEqual(len(self.recording.records), 1)
3210 record = self.recording.records[0]
3211 self.assertEqual(record.levelno, logging.CRITICAL)
3212 self.assertEqual(record.msg, msg)
3213 self.assertEqual(record.args, (self.recording,))
3214
3215 def test_is_enabled_for(self):
3216 old_disable = self.adapter.logger.manager.disable
3217 self.adapter.logger.manager.disable = 33
3218 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3219 'disable', old_disable))
3220 self.assertFalse(self.adapter.isEnabledFor(32))
3221
3222 def test_has_handlers(self):
3223 self.assertTrue(self.adapter.hasHandlers())
3224
3225 for handler in self.logger.handlers:
3226 self.logger.removeHandler(handler)
3227 assert not self.logger.hasHandlers()
3228
3229 self.assertFalse(self.adapter.hasHandlers())
3230
3231
3232class LoggerTest(BaseTest):
3233
3234 def setUp(self):
3235 super(LoggerTest, self).setUp()
3236 self.recording = RecordingHandler()
3237 self.logger = logging.Logger(name='blah')
3238 self.logger.addHandler(self.recording)
3239 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3240 self.addCleanup(self.recording.close)
3241 self.addCleanup(logging.shutdown)
3242
3243 def test_set_invalid_level(self):
3244 self.assertRaises(TypeError, self.logger.setLevel, object())
3245
3246 def test_exception(self):
3247 msg = 'testing exception: %r'
3248 exc = None
3249 try:
3250 assert False
3251 except AssertionError as e:
3252 exc = e
3253 self.logger.exception(msg, self.recording)
3254
3255 self.assertEqual(len(self.recording.records), 1)
3256 record = self.recording.records[0]
3257 self.assertEqual(record.levelno, logging.ERROR)
3258 self.assertEqual(record.msg, msg)
3259 self.assertEqual(record.args, (self.recording,))
3260 self.assertEqual(record.exc_info,
3261 (exc.__class__, exc, exc.__traceback__))
3262
3263 def test_log_invalid_level_with_raise(self):
3264 old_raise = logging.raiseExceptions
3265 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3266
3267 logging.raiseExceptions = True
3268 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3269
3270 def test_log_invalid_level_no_raise(self):
3271 old_raise = logging.raiseExceptions
3272 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3273
3274 logging.raiseExceptions = False
3275 self.logger.log('10', 'test message') # no exception happens
3276
3277 def test_find_caller_with_stack_info(self):
3278 called = []
3279 patch(self, logging.traceback, 'print_stack',
3280 lambda f, file: called.append(file.getvalue()))
3281
3282 self.logger.findCaller(stack_info=True)
3283
3284 self.assertEqual(len(called), 1)
3285 self.assertEqual('Stack (most recent call last):\n', called[0])
3286
3287 def test_make_record_with_extra_overwrite(self):
3288 name = 'my record'
3289 level = 13
3290 fn = lno = msg = args = exc_info = func = sinfo = None
3291 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3292 exc_info, func, sinfo)
3293
3294 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3295 extra = {key: 'some value'}
3296 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3297 fn, lno, msg, args, exc_info,
3298 extra=extra, sinfo=sinfo)
3299
3300 def test_make_record_with_extra_no_overwrite(self):
3301 name = 'my record'
3302 level = 13
3303 fn = lno = msg = args = exc_info = func = sinfo = None
3304 extra = {'valid_key': 'some value'}
3305 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3306 exc_info, extra=extra, sinfo=sinfo)
3307 self.assertIn('valid_key', result.__dict__)
3308
3309 def test_has_handlers(self):
3310 self.assertTrue(self.logger.hasHandlers())
3311
3312 for handler in self.logger.handlers:
3313 self.logger.removeHandler(handler)
3314 assert not self.logger.hasHandlers()
3315
3316 self.assertFalse(self.logger.hasHandlers())
3317
3318 def test_has_handlers_no_propagate(self):
3319 child_logger = logging.getLogger('blah.child')
3320 child_logger.propagate = False
3321 assert child_logger.handlers == []
3322
3323 self.assertFalse(child_logger.hasHandlers())
3324
3325 def test_is_enabled_for(self):
3326 old_disable = self.logger.manager.disable
3327 self.logger.manager.disable = 23
3328 self.addCleanup(lambda: setattr(self.logger.manager,
3329 'disable', old_disable))
3330 self.assertFalse(self.logger.isEnabledFor(22))
3331
3332
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003333class BaseFileTest(BaseTest):
3334 "Base class for handler tests that write log files"
3335
3336 def setUp(self):
3337 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003338 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3339 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003340 self.rmfiles = []
3341
3342 def tearDown(self):
3343 for fn in self.rmfiles:
3344 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003345 if os.path.exists(self.fn):
3346 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003347 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003348
3349 def assertLogFile(self, filename):
3350 "Assert a log file is there and register it for deletion"
3351 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003352 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003353 self.rmfiles.append(filename)
3354
3355
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003356class FileHandlerTest(BaseFileTest):
3357 def test_delay(self):
3358 os.unlink(self.fn)
3359 fh = logging.FileHandler(self.fn, delay=True)
3360 self.assertIsNone(fh.stream)
3361 self.assertFalse(os.path.exists(self.fn))
3362 fh.handle(logging.makeLogRecord({}))
3363 self.assertIsNotNone(fh.stream)
3364 self.assertTrue(os.path.exists(self.fn))
3365 fh.close()
3366
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003367class RotatingFileHandlerTest(BaseFileTest):
3368 def next_rec(self):
3369 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3370 self.next_message(), None, None, None)
3371
3372 def test_should_not_rollover(self):
3373 # If maxbytes is zero rollover never occurs
3374 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3375 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003376 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003377
3378 def test_should_rollover(self):
3379 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3380 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003381 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003382
3383 def test_file_created(self):
3384 # checks that the file is created and assumes it was created
3385 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003386 rh = logging.handlers.RotatingFileHandler(self.fn)
3387 rh.emit(self.next_rec())
3388 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003389 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003390
3391 def test_rollover_filenames(self):
3392 rh = logging.handlers.RotatingFileHandler(
3393 self.fn, backupCount=2, maxBytes=1)
3394 rh.emit(self.next_rec())
3395 self.assertLogFile(self.fn)
3396 rh.emit(self.next_rec())
3397 self.assertLogFile(self.fn + ".1")
3398 rh.emit(self.next_rec())
3399 self.assertLogFile(self.fn + ".2")
3400 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003401 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003402
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003403class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003404 # other test methods added below
3405 def test_rollover(self):
3406 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S')
3407 r = logging.makeLogRecord({'msg': 'testing'})
3408 fh.emit(r)
3409 self.assertLogFile(self.fn)
3410 time.sleep(1.0)
3411 fh.emit(r)
3412 now = datetime.datetime.now()
3413 prevsec = now - datetime.timedelta(seconds=1)
3414 suffix = prevsec.strftime(".%Y-%m-%d_%H-%M-%S")
3415 self.assertLogFile(self.fn + suffix)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003416
3417def secs(**kw):
3418 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3419
3420for when, exp in (('S', 1),
3421 ('M', 60),
3422 ('H', 60 * 60),
3423 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003424 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003425 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003426 ('W0', secs(days=4, hours=24)),
3427 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003428 def test_compute_rollover(self, when=when, exp=exp):
3429 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003430 self.fn, when=when, interval=1, backupCount=0, utc=True)
3431 currentTime = 0.0
3432 actual = rh.computeRollover(currentTime)
3433 if exp != actual:
3434 # Failures occur on some systems for MIDNIGHT and W0.
3435 # Print detailed calculation for MIDNIGHT so we can try to see
3436 # what's going on
3437 import time
3438 if when == 'MIDNIGHT':
3439 try:
3440 if rh.utc:
3441 t = time.gmtime(currentTime)
3442 else:
3443 t = time.localtime(currentTime)
3444 currentHour = t[3]
3445 currentMinute = t[4]
3446 currentSecond = t[5]
3447 # r is the number of seconds left between now and midnight
3448 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3449 currentMinute) * 60 +
3450 currentSecond)
3451 result = currentTime + r
3452 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3453 print('currentHour: %s' % currentHour, file=sys.stderr)
3454 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3455 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3456 print('r: %s' % r, file=sys.stderr)
3457 print('result: %s' % result, file=sys.stderr)
3458 except Exception:
3459 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3460 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003461 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003462 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3463
Christian Heimes180510d2008-03-03 19:15:45 +00003464# Set the locale to the platform-dependent default. I have no idea
3465# why the test does this, but in any case we save the current locale
3466# first and restore it at the end.
3467@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003468def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003469 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003470 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003471 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3472 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3473 ManagerTest, FormatterTest, BufferingFormatterTest,
3474 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3475 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3476 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3477 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003478 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003479 SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003480 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003481 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003482
Christian Heimes180510d2008-03-03 19:15:45 +00003483if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003484 test_main()