blob: 06dd6d25cd45f4a78384f61a00bdf9257b51fee2 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajiped0473c2011-02-26 15:35:38 +00003# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajiped0473c2011-02-26 15:35:38 +000021Copyright (C) 2001-2011 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Christian Heimes180510d2008-03-03 19:15:45 +000036import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000037import select
Christian Heimes180510d2008-03-03 19:15:45 +000038import socket
Alexandre Vassalottice261952008-05-12 02:31:37 +000039from socketserver import ThreadingTCPServer, StreamRequestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010043from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010044from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000045import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010046import time
Christian Heimes180510d2008-03-03 19:15:45 +000047import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000048import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000049import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000050try:
51 import threading
52except ImportError:
53 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000054
55
Christian Heimes180510d2008-03-03 19:15:45 +000056class BaseTest(unittest.TestCase):
57
58 """Base class for logging tests."""
59
60 log_format = "%(name)s -> %(levelname)s: %(message)s"
61 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
62 message_num = 0
63
64 def setUp(self):
65 """Setup the default logging stream to an internal StringIO instance,
66 so that we can examine log output as we want."""
67 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000068 logging._acquireLock()
69 try:
Christian Heimes180510d2008-03-03 19:15:45 +000070 self.saved_handlers = logging._handlers.copy()
71 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000072 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000073 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000074 self.logger_states = logger_states = {}
75 for name in saved_loggers:
76 logger_states[name] = getattr(saved_loggers[name],
77 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000078 finally:
79 logging._releaseLock()
80
Benjamin Peterson22005fc2010-04-11 16:25:06 +000081 # Set two unused loggers: one non-ASCII and one Unicode.
82 # This is to test correct operation when sorting existing
83 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000084 self.logger1 = logging.getLogger("\xab\xd7\xbb")
85 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000086
Christian Heimes180510d2008-03-03 19:15:45 +000087 self.root_logger = logging.getLogger("")
88 self.original_logging_level = self.root_logger.getEffectiveLevel()
89
90 self.stream = io.StringIO()
91 self.root_logger.setLevel(logging.DEBUG)
92 self.root_hdlr = logging.StreamHandler(self.stream)
93 self.root_formatter = logging.Formatter(self.log_format)
94 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000095 if self.logger1.hasHandlers():
96 hlist = self.logger1.handlers + self.root_logger.handlers
97 raise AssertionError('Unexpected handlers: %s' % hlist)
98 if self.logger2.hasHandlers():
99 hlist = self.logger2.handlers + self.root_logger.handlers
100 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000101 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000102 self.assertTrue(self.logger1.hasHandlers())
103 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000104
105 def tearDown(self):
106 """Remove our logging stream, and restore the original logging
107 level."""
108 self.stream.close()
109 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000110 while self.root_logger.handlers:
111 h = self.root_logger.handlers[0]
112 self.root_logger.removeHandler(h)
113 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000114 self.root_logger.setLevel(self.original_logging_level)
115 logging._acquireLock()
116 try:
117 logging._levelNames.clear()
118 logging._levelNames.update(self.saved_level_names)
119 logging._handlers.clear()
120 logging._handlers.update(self.saved_handlers)
121 logging._handlerList[:] = self.saved_handler_list
122 loggerDict = logging.getLogger().manager.loggerDict
123 loggerDict.clear()
124 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000125 logger_states = self.logger_states
126 for name in self.logger_states:
127 if logger_states[name] is not None:
128 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000129 finally:
130 logging._releaseLock()
131
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000132 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000133 """Match the collected log lines against the regular expression
134 self.expected_log_pat, and compare the extracted group values to
135 the expected_values list of tuples."""
136 stream = stream or self.stream
137 pat = re.compile(self.expected_log_pat)
138 try:
139 stream.reset()
140 actual_lines = stream.readlines()
141 except AttributeError:
142 # StringIO.StringIO lacks a reset() method.
143 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000144 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000145 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000146 for actual, expected in zip(actual_lines, expected_values):
147 match = pat.search(actual)
148 if not match:
149 self.fail("Log line does not match expected pattern:\n" +
150 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000151 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000152 s = stream.read()
153 if s:
154 self.fail("Remaining output at end of log stream:\n" + s)
155
156 def next_message(self):
157 """Generate a message consisting solely of an auto-incrementing
158 integer."""
159 self.message_num += 1
160 return "%d" % self.message_num
161
162
163class BuiltinLevelsTest(BaseTest):
164 """Test builtin levels and their inheritance."""
165
166 def test_flat(self):
167 #Logging levels in a flat logger namespace.
168 m = self.next_message
169
170 ERR = logging.getLogger("ERR")
171 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000172 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000173 INF.setLevel(logging.INFO)
174 DEB = logging.getLogger("DEB")
175 DEB.setLevel(logging.DEBUG)
176
177 # These should log.
178 ERR.log(logging.CRITICAL, m())
179 ERR.error(m())
180
181 INF.log(logging.CRITICAL, m())
182 INF.error(m())
183 INF.warn(m())
184 INF.info(m())
185
186 DEB.log(logging.CRITICAL, m())
187 DEB.error(m())
188 DEB.warn (m())
189 DEB.info (m())
190 DEB.debug(m())
191
192 # These should not log.
193 ERR.warn(m())
194 ERR.info(m())
195 ERR.debug(m())
196
197 INF.debug(m())
198
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000199 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000200 ('ERR', 'CRITICAL', '1'),
201 ('ERR', 'ERROR', '2'),
202 ('INF', 'CRITICAL', '3'),
203 ('INF', 'ERROR', '4'),
204 ('INF', 'WARNING', '5'),
205 ('INF', 'INFO', '6'),
206 ('DEB', 'CRITICAL', '7'),
207 ('DEB', 'ERROR', '8'),
208 ('DEB', 'WARNING', '9'),
209 ('DEB', 'INFO', '10'),
210 ('DEB', 'DEBUG', '11'),
211 ])
212
213 def test_nested_explicit(self):
214 # Logging levels in a nested namespace, all explicitly set.
215 m = self.next_message
216
217 INF = logging.getLogger("INF")
218 INF.setLevel(logging.INFO)
219 INF_ERR = logging.getLogger("INF.ERR")
220 INF_ERR.setLevel(logging.ERROR)
221
222 # These should log.
223 INF_ERR.log(logging.CRITICAL, m())
224 INF_ERR.error(m())
225
226 # These should not log.
227 INF_ERR.warn(m())
228 INF_ERR.info(m())
229 INF_ERR.debug(m())
230
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000231 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000232 ('INF.ERR', 'CRITICAL', '1'),
233 ('INF.ERR', 'ERROR', '2'),
234 ])
235
236 def test_nested_inherited(self):
237 #Logging levels in a nested namespace, inherited from parent loggers.
238 m = self.next_message
239
240 INF = logging.getLogger("INF")
241 INF.setLevel(logging.INFO)
242 INF_ERR = logging.getLogger("INF.ERR")
243 INF_ERR.setLevel(logging.ERROR)
244 INF_UNDEF = logging.getLogger("INF.UNDEF")
245 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
246 UNDEF = logging.getLogger("UNDEF")
247
248 # These should log.
249 INF_UNDEF.log(logging.CRITICAL, m())
250 INF_UNDEF.error(m())
251 INF_UNDEF.warn(m())
252 INF_UNDEF.info(m())
253 INF_ERR_UNDEF.log(logging.CRITICAL, m())
254 INF_ERR_UNDEF.error(m())
255
256 # These should not log.
257 INF_UNDEF.debug(m())
258 INF_ERR_UNDEF.warn(m())
259 INF_ERR_UNDEF.info(m())
260 INF_ERR_UNDEF.debug(m())
261
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000262 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000263 ('INF.UNDEF', 'CRITICAL', '1'),
264 ('INF.UNDEF', 'ERROR', '2'),
265 ('INF.UNDEF', 'WARNING', '3'),
266 ('INF.UNDEF', 'INFO', '4'),
267 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
268 ('INF.ERR.UNDEF', 'ERROR', '6'),
269 ])
270
271 def test_nested_with_virtual_parent(self):
272 # Logging levels when some parent does not exist yet.
273 m = self.next_message
274
275 INF = logging.getLogger("INF")
276 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
277 CHILD = logging.getLogger("INF.BADPARENT")
278 INF.setLevel(logging.INFO)
279
280 # These should log.
281 GRANDCHILD.log(logging.FATAL, m())
282 GRANDCHILD.info(m())
283 CHILD.log(logging.FATAL, m())
284 CHILD.info(m())
285
286 # These should not log.
287 GRANDCHILD.debug(m())
288 CHILD.debug(m())
289
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000290 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000291 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
292 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
293 ('INF.BADPARENT', 'CRITICAL', '3'),
294 ('INF.BADPARENT', 'INFO', '4'),
295 ])
296
297
298class BasicFilterTest(BaseTest):
299
300 """Test the bundled Filter class."""
301
302 def test_filter(self):
303 # Only messages satisfying the specified criteria pass through the
304 # filter.
305 filter_ = logging.Filter("spam.eggs")
306 handler = self.root_logger.handlers[0]
307 try:
308 handler.addFilter(filter_)
309 spam = logging.getLogger("spam")
310 spam_eggs = logging.getLogger("spam.eggs")
311 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
312 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
313
314 spam.info(self.next_message())
315 spam_eggs.info(self.next_message()) # Good.
316 spam_eggs_fish.info(self.next_message()) # Good.
317 spam_bakedbeans.info(self.next_message())
318
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000319 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000320 ('spam.eggs', 'INFO', '2'),
321 ('spam.eggs.fish', 'INFO', '3'),
322 ])
323 finally:
324 handler.removeFilter(filter_)
325
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000326 def test_callable_filter(self):
327 # Only messages satisfying the specified criteria pass through the
328 # filter.
329
330 def filterfunc(record):
331 parts = record.name.split('.')
332 prefix = '.'.join(parts[:2])
333 return prefix == 'spam.eggs'
334
335 handler = self.root_logger.handlers[0]
336 try:
337 handler.addFilter(filterfunc)
338 spam = logging.getLogger("spam")
339 spam_eggs = logging.getLogger("spam.eggs")
340 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
341 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
342
343 spam.info(self.next_message())
344 spam_eggs.info(self.next_message()) # Good.
345 spam_eggs_fish.info(self.next_message()) # Good.
346 spam_bakedbeans.info(self.next_message())
347
348 self.assert_log_lines([
349 ('spam.eggs', 'INFO', '2'),
350 ('spam.eggs.fish', 'INFO', '3'),
351 ])
352 finally:
353 handler.removeFilter(filterfunc)
354
Vinay Sajip985ef872011-04-26 19:34:04 +0100355 def test_empty_filter(self):
356 f = logging.Filter()
357 r = logging.makeLogRecord({'name': 'spam.eggs'})
358 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000359
360#
361# First, we define our levels. There can be as many as you want - the only
362# limitations are that they should be integers, the lowest should be > 0 and
363# larger values mean less information being logged. If you need specific
364# level values which do not fit into these limitations, you can use a
365# mapping dictionary to convert between your application levels and the
366# logging system.
367#
368SILENT = 120
369TACITURN = 119
370TERSE = 118
371EFFUSIVE = 117
372SOCIABLE = 116
373VERBOSE = 115
374TALKATIVE = 114
375GARRULOUS = 113
376CHATTERBOX = 112
377BORING = 111
378
379LEVEL_RANGE = range(BORING, SILENT + 1)
380
381#
382# Next, we define names for our levels. You don't need to do this - in which
383# case the system will use "Level n" to denote the text for the level.
384#
385my_logging_levels = {
386 SILENT : 'Silent',
387 TACITURN : 'Taciturn',
388 TERSE : 'Terse',
389 EFFUSIVE : 'Effusive',
390 SOCIABLE : 'Sociable',
391 VERBOSE : 'Verbose',
392 TALKATIVE : 'Talkative',
393 GARRULOUS : 'Garrulous',
394 CHATTERBOX : 'Chatterbox',
395 BORING : 'Boring',
396}
397
398class GarrulousFilter(logging.Filter):
399
400 """A filter which blocks garrulous messages."""
401
402 def filter(self, record):
403 return record.levelno != GARRULOUS
404
405class VerySpecificFilter(logging.Filter):
406
407 """A filter which blocks sociable and taciturn messages."""
408
409 def filter(self, record):
410 return record.levelno not in [SOCIABLE, TACITURN]
411
412
413class CustomLevelsAndFiltersTest(BaseTest):
414
415 """Test various filtering possibilities with custom logging levels."""
416
417 # Skip the logger name group.
418 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
419
420 def setUp(self):
421 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000422 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000423 logging.addLevelName(k, v)
424
425 def log_at_all_levels(self, logger):
426 for lvl in LEVEL_RANGE:
427 logger.log(lvl, self.next_message())
428
429 def test_logger_filter(self):
430 # Filter at logger level.
431 self.root_logger.setLevel(VERBOSE)
432 # Levels >= 'Verbose' are good.
433 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000434 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000435 ('Verbose', '5'),
436 ('Sociable', '6'),
437 ('Effusive', '7'),
438 ('Terse', '8'),
439 ('Taciturn', '9'),
440 ('Silent', '10'),
441 ])
442
443 def test_handler_filter(self):
444 # Filter at handler level.
445 self.root_logger.handlers[0].setLevel(SOCIABLE)
446 try:
447 # Levels >= 'Sociable' are good.
448 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000449 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000450 ('Sociable', '6'),
451 ('Effusive', '7'),
452 ('Terse', '8'),
453 ('Taciturn', '9'),
454 ('Silent', '10'),
455 ])
456 finally:
457 self.root_logger.handlers[0].setLevel(logging.NOTSET)
458
459 def test_specific_filters(self):
460 # Set a specific filter object on the handler, and then add another
461 # filter object on the logger itself.
462 handler = self.root_logger.handlers[0]
463 specific_filter = None
464 garr = GarrulousFilter()
465 handler.addFilter(garr)
466 try:
467 self.log_at_all_levels(self.root_logger)
468 first_lines = [
469 # Notice how 'Garrulous' is missing
470 ('Boring', '1'),
471 ('Chatterbox', '2'),
472 ('Talkative', '4'),
473 ('Verbose', '5'),
474 ('Sociable', '6'),
475 ('Effusive', '7'),
476 ('Terse', '8'),
477 ('Taciturn', '9'),
478 ('Silent', '10'),
479 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000480 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000481
482 specific_filter = VerySpecificFilter()
483 self.root_logger.addFilter(specific_filter)
484 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000485 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000486 # Not only 'Garrulous' is still missing, but also 'Sociable'
487 # and 'Taciturn'
488 ('Boring', '11'),
489 ('Chatterbox', '12'),
490 ('Talkative', '14'),
491 ('Verbose', '15'),
492 ('Effusive', '17'),
493 ('Terse', '18'),
494 ('Silent', '20'),
495 ])
496 finally:
497 if specific_filter:
498 self.root_logger.removeFilter(specific_filter)
499 handler.removeFilter(garr)
500
501
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100502class HandlerTest(BaseTest):
503 def test_name(self):
504 h = logging.Handler()
505 h.name = 'generic'
506 self.assertEqual(h.name, 'generic')
507 h.name = 'anothergeneric'
508 self.assertEqual(h.name, 'anothergeneric')
509 self.assertRaises(NotImplementedError, h.emit, None)
510
Vinay Sajip5a35b062011-04-27 11:31:14 +0100511 def test_builtin_handlers(self):
512 # We can't actually *use* too many handlers in the tests,
513 # but we can try instantiating them with various options
514 if sys.platform in ('linux2', 'darwin'):
515 for existing in (True, False):
516 fd, fn = tempfile.mkstemp()
517 os.close(fd)
518 if not existing:
519 os.unlink(fn)
520 h = logging.handlers.WatchedFileHandler(fn, delay=True)
521 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100522 dev, ino = h.dev, h.ino
523 self.assertNotEqual(dev, -1)
524 self.assertNotEqual(ino, -1)
525 r = logging.makeLogRecord({'msg': 'Test'})
526 h.handle(r)
527 # Now remove the file.
528 os.unlink(fn)
529 self.assertFalse(os.path.exists(fn))
530 # The next call should recreate the file.
531 h.handle(r)
532 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100533 else:
534 self.assertEqual(h.dev, -1)
535 self.assertEqual(h.ino, -1)
536 h.close()
537 if existing:
538 os.unlink(fn)
539 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100540 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100541 else:
542 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100543 try:
544 h = logging.handlers.SysLogHandler(sockname)
545 self.assertEqual(h.facility, h.LOG_USER)
546 self.assertTrue(h.unixsocket)
547 h.close()
548 except socket.error: # syslogd might not be available
549 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100550 h = logging.handlers.SMTPHandler('localhost', 'me', 'you', 'Log')
551 self.assertEqual(h.toaddrs, ['you'])
552 h.close()
553 for method in ('GET', 'POST', 'PUT'):
554 if method == 'PUT':
555 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
556 'localhost', '/log', method)
557 else:
558 h = logging.handlers.HTTPHandler('localhost', '/log', method)
559 h.close()
560 h = logging.handlers.BufferingHandler(0)
561 r = logging.makeLogRecord({})
562 self.assertTrue(h.shouldFlush(r))
563 h.close()
564 h = logging.handlers.BufferingHandler(1)
565 self.assertFalse(h.shouldFlush(r))
566 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100567
568class BadStream(object):
569 def write(self, data):
570 raise RuntimeError('deliberate mistake')
571
572class TestStreamHandler(logging.StreamHandler):
573 def handleError(self, record):
574 self.error_record = record
575
576class StreamHandlerTest(BaseTest):
577 def test_error_handling(self):
578 h = TestStreamHandler(BadStream())
579 r = logging.makeLogRecord({})
580 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100581 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100582 try:
583 h.handle(r)
584 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100585 h = logging.StreamHandler(BadStream())
586 sys.stderr = sio = io.StringIO()
587 h.handle(r)
588 self.assertTrue('\nRuntimeError: '
589 'deliberate mistake\n' in sio.getvalue())
590 logging.raiseExceptions = False
591 sys.stderr = sio = io.StringIO()
592 h.handle(r)
593 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100594 finally:
595 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100596 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100597
Christian Heimes180510d2008-03-03 19:15:45 +0000598class MemoryHandlerTest(BaseTest):
599
600 """Tests for the MemoryHandler."""
601
602 # Do not bother with a logger name group.
603 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
604
605 def setUp(self):
606 BaseTest.setUp(self)
607 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
608 self.root_hdlr)
609 self.mem_logger = logging.getLogger('mem')
610 self.mem_logger.propagate = 0
611 self.mem_logger.addHandler(self.mem_hdlr)
612
613 def tearDown(self):
614 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000615 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000616
617 def test_flush(self):
618 # The memory handler flushes to its target handler based on specific
619 # criteria (message count and message level).
620 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000621 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000622 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000623 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000624 # This will flush because the level is >= logging.WARNING
625 self.mem_logger.warn(self.next_message())
626 lines = [
627 ('DEBUG', '1'),
628 ('INFO', '2'),
629 ('WARNING', '3'),
630 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000631 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000632 for n in (4, 14):
633 for i in range(9):
634 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000635 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000636 # This will flush because it's the 10th message since the last
637 # flush.
638 self.mem_logger.debug(self.next_message())
639 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000640 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000641
642 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000643 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000644
645
646class ExceptionFormatter(logging.Formatter):
647 """A special exception formatter."""
648 def formatException(self, ei):
649 return "Got a [%s]" % ei[0].__name__
650
651
652class ConfigFileTest(BaseTest):
653
654 """Reading logging config from a .ini-style config file."""
655
656 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
657
658 # config0 is a standard configuration.
659 config0 = """
660 [loggers]
661 keys=root
662
663 [handlers]
664 keys=hand1
665
666 [formatters]
667 keys=form1
668
669 [logger_root]
670 level=WARNING
671 handlers=hand1
672
673 [handler_hand1]
674 class=StreamHandler
675 level=NOTSET
676 formatter=form1
677 args=(sys.stdout,)
678
679 [formatter_form1]
680 format=%(levelname)s ++ %(message)s
681 datefmt=
682 """
683
684 # config1 adds a little to the standard configuration.
685 config1 = """
686 [loggers]
687 keys=root,parser
688
689 [handlers]
690 keys=hand1
691
692 [formatters]
693 keys=form1
694
695 [logger_root]
696 level=WARNING
697 handlers=
698
699 [logger_parser]
700 level=DEBUG
701 handlers=hand1
702 propagate=1
703 qualname=compiler.parser
704
705 [handler_hand1]
706 class=StreamHandler
707 level=NOTSET
708 formatter=form1
709 args=(sys.stdout,)
710
711 [formatter_form1]
712 format=%(levelname)s ++ %(message)s
713 datefmt=
714 """
715
Vinay Sajip3f84b072011-03-07 17:49:33 +0000716 # config1a moves the handler to the root.
717 config1a = """
718 [loggers]
719 keys=root,parser
720
721 [handlers]
722 keys=hand1
723
724 [formatters]
725 keys=form1
726
727 [logger_root]
728 level=WARNING
729 handlers=hand1
730
731 [logger_parser]
732 level=DEBUG
733 handlers=
734 propagate=1
735 qualname=compiler.parser
736
737 [handler_hand1]
738 class=StreamHandler
739 level=NOTSET
740 formatter=form1
741 args=(sys.stdout,)
742
743 [formatter_form1]
744 format=%(levelname)s ++ %(message)s
745 datefmt=
746 """
747
Christian Heimes180510d2008-03-03 19:15:45 +0000748 # config2 has a subtle configuration error that should be reported
749 config2 = config1.replace("sys.stdout", "sys.stbout")
750
751 # config3 has a less subtle configuration error
752 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
753
754 # config4 specifies a custom formatter class to be loaded
755 config4 = """
756 [loggers]
757 keys=root
758
759 [handlers]
760 keys=hand1
761
762 [formatters]
763 keys=form1
764
765 [logger_root]
766 level=NOTSET
767 handlers=hand1
768
769 [handler_hand1]
770 class=StreamHandler
771 level=NOTSET
772 formatter=form1
773 args=(sys.stdout,)
774
775 [formatter_form1]
776 class=""" + __name__ + """.ExceptionFormatter
777 format=%(levelname)s:%(name)s:%(message)s
778 datefmt=
779 """
780
Georg Brandl3dbca812008-07-23 16:10:53 +0000781 # config5 specifies a custom handler class to be loaded
782 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
783
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000784 # config6 uses ', ' delimiters in the handlers and formatters sections
785 config6 = """
786 [loggers]
787 keys=root,parser
788
789 [handlers]
790 keys=hand1, hand2
791
792 [formatters]
793 keys=form1, form2
794
795 [logger_root]
796 level=WARNING
797 handlers=
798
799 [logger_parser]
800 level=DEBUG
801 handlers=hand1
802 propagate=1
803 qualname=compiler.parser
804
805 [handler_hand1]
806 class=StreamHandler
807 level=NOTSET
808 formatter=form1
809 args=(sys.stdout,)
810
811 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000812 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000813 level=NOTSET
814 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000815 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000816
817 [formatter_form1]
818 format=%(levelname)s ++ %(message)s
819 datefmt=
820
821 [formatter_form2]
822 format=%(message)s
823 datefmt=
824 """
825
Vinay Sajip3f84b072011-03-07 17:49:33 +0000826 # config7 adds a compiler logger.
827 config7 = """
828 [loggers]
829 keys=root,parser,compiler
830
831 [handlers]
832 keys=hand1
833
834 [formatters]
835 keys=form1
836
837 [logger_root]
838 level=WARNING
839 handlers=hand1
840
841 [logger_compiler]
842 level=DEBUG
843 handlers=
844 propagate=1
845 qualname=compiler
846
847 [logger_parser]
848 level=DEBUG
849 handlers=
850 propagate=1
851 qualname=compiler.parser
852
853 [handler_hand1]
854 class=StreamHandler
855 level=NOTSET
856 formatter=form1
857 args=(sys.stdout,)
858
859 [formatter_form1]
860 format=%(levelname)s ++ %(message)s
861 datefmt=
862 """
863
Christian Heimes180510d2008-03-03 19:15:45 +0000864 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000865 file = io.StringIO(textwrap.dedent(conf))
866 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000867
868 def test_config0_ok(self):
869 # A simple config file which overrides the default settings.
870 with captured_stdout() as output:
871 self.apply_config(self.config0)
872 logger = logging.getLogger()
873 # Won't output anything
874 logger.info(self.next_message())
875 # Outputs a message
876 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000877 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000878 ('ERROR', '2'),
879 ], stream=output)
880 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000881 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000882
Georg Brandl3dbca812008-07-23 16:10:53 +0000883 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000884 # A config file defining a sub-parser as well.
885 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000886 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000887 logger = logging.getLogger("compiler.parser")
888 # Both will output a message
889 logger.info(self.next_message())
890 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000891 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000892 ('INFO', '1'),
893 ('ERROR', '2'),
894 ], stream=output)
895 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000896 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000897
898 def test_config2_failure(self):
899 # A simple config file which overrides the default settings.
900 self.assertRaises(Exception, self.apply_config, self.config2)
901
902 def test_config3_failure(self):
903 # A simple config file which overrides the default settings.
904 self.assertRaises(Exception, self.apply_config, self.config3)
905
906 def test_config4_ok(self):
907 # A config file specifying a custom formatter class.
908 with captured_stdout() as output:
909 self.apply_config(self.config4)
910 logger = logging.getLogger()
911 try:
912 raise RuntimeError()
913 except RuntimeError:
914 logging.exception("just testing")
915 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000916 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000917 "ERROR:root:just testing\nGot a [RuntimeError]\n")
918 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000919 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000920
Georg Brandl3dbca812008-07-23 16:10:53 +0000921 def test_config5_ok(self):
922 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000923
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000924 def test_config6_ok(self):
925 self.test_config1_ok(config=self.config6)
926
Vinay Sajip3f84b072011-03-07 17:49:33 +0000927 def test_config7_ok(self):
928 with captured_stdout() as output:
929 self.apply_config(self.config1a)
930 logger = logging.getLogger("compiler.parser")
931 # See issue #11424. compiler-hyphenated sorts
932 # between compiler and compiler.xyz and this
933 # was preventing compiler.xyz from being included
934 # in the child loggers of compiler because of an
935 # overzealous loop termination condition.
936 hyphenated = logging.getLogger('compiler-hyphenated')
937 # All will output a message
938 logger.info(self.next_message())
939 logger.error(self.next_message())
940 hyphenated.critical(self.next_message())
941 self.assert_log_lines([
942 ('INFO', '1'),
943 ('ERROR', '2'),
944 ('CRITICAL', '3'),
945 ], stream=output)
946 # Original logger output is empty.
947 self.assert_log_lines([])
948 with captured_stdout() as output:
949 self.apply_config(self.config7)
950 logger = logging.getLogger("compiler.parser")
951 self.assertFalse(logger.disabled)
952 # Both will output a message
953 logger.info(self.next_message())
954 logger.error(self.next_message())
955 logger = logging.getLogger("compiler.lexer")
956 # Both will output a message
957 logger.info(self.next_message())
958 logger.error(self.next_message())
959 # Will not appear
960 hyphenated.critical(self.next_message())
961 self.assert_log_lines([
962 ('INFO', '4'),
963 ('ERROR', '5'),
964 ('INFO', '6'),
965 ('ERROR', '7'),
966 ], stream=output)
967 # Original logger output is empty.
968 self.assert_log_lines([])
969
Christian Heimes180510d2008-03-03 19:15:45 +0000970class LogRecordStreamHandler(StreamRequestHandler):
971
972 """Handler for a streaming logging request. It saves the log message in the
973 TCP server's 'log_output' attribute."""
974
975 TCP_LOG_END = "!!!END!!!"
976
977 def handle(self):
978 """Handle multiple requests - each expected to be of 4-byte length,
979 followed by the LogRecord in pickle format. Logs the record
980 according to whatever policy is configured locally."""
981 while True:
982 chunk = self.connection.recv(4)
983 if len(chunk) < 4:
984 break
985 slen = struct.unpack(">L", chunk)[0]
986 chunk = self.connection.recv(slen)
987 while len(chunk) < slen:
988 chunk = chunk + self.connection.recv(slen - len(chunk))
989 obj = self.unpickle(chunk)
990 record = logging.makeLogRecord(obj)
991 self.handle_log_record(record)
992
993 def unpickle(self, data):
994 return pickle.loads(data)
995
996 def handle_log_record(self, record):
997 # If the end-of-messages sentinel is seen, tell the server to
998 # terminate.
999 if self.TCP_LOG_END in record.msg:
1000 self.server.abort = 1
1001 return
1002 self.server.log_output += record.msg + "\n"
1003
Guido van Rossum376e6362003-04-25 14:22:00 +00001004
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001005class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +00001006
1007 """A simple-minded TCP socket-based logging receiver suitable for test
1008 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001009
1010 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +00001011 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001012
1013 def __init__(self, host='localhost',
1014 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
1015 handler=LogRecordStreamHandler):
1016 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +00001017 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +00001018 self.timeout = 0.1
1019 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001020
1021 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +00001022 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +00001023 rd, wr, ex = select.select([self.socket.fileno()], [], [],
1024 self.timeout)
1025 if rd:
1026 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +00001027 # Notify the main thread that we're about to exit
1028 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +00001029 # close the listen socket
1030 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001031
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001032
Victor Stinner45df8202010-04-28 22:31:17 +00001033@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001034class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001035
Christian Heimes180510d2008-03-03 19:15:45 +00001036 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001037
Christian Heimes180510d2008-03-03 19:15:45 +00001038 def setUp(self):
1039 """Set up a TCP server to receive log messages, and a SocketHandler
1040 pointing to that server's address and port."""
1041 BaseTest.setUp(self)
1042 self.tcpserver = LogRecordSocketReceiver(port=0)
1043 self.port = self.tcpserver.socket.getsockname()[1]
1044 self.threads = [
1045 threading.Thread(target=self.tcpserver.serve_until_stopped)]
1046 for thread in self.threads:
1047 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001048
Christian Heimes180510d2008-03-03 19:15:45 +00001049 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
1050 self.sock_hdlr.setFormatter(self.root_formatter)
1051 self.root_logger.removeHandler(self.root_logger.handlers[0])
1052 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001053
Christian Heimes180510d2008-03-03 19:15:45 +00001054 def tearDown(self):
1055 """Shutdown the TCP server."""
1056 try:
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001057 if hasattr(self, 'tcpserver'):
1058 self.tcpserver.abort = True
1059 del self.tcpserver
Christian Heimes180510d2008-03-03 19:15:45 +00001060 self.root_logger.removeHandler(self.sock_hdlr)
1061 self.sock_hdlr.close()
1062 for thread in self.threads:
1063 thread.join(2.0)
1064 finally:
1065 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001066
Christian Heimes180510d2008-03-03 19:15:45 +00001067 def get_output(self):
1068 """Get the log output as received by the TCP server."""
1069 # Signal the TCP receiver and wait for it to terminate.
1070 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
1071 self.tcpserver.finished.wait(2.0)
1072 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001073
Christian Heimes180510d2008-03-03 19:15:45 +00001074 def test_output(self):
1075 # The log message sent to the SocketHandler is properly received.
1076 logger = logging.getLogger("tcp")
1077 logger.error("spam")
1078 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +00001079 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001080
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001081 def test_noserver(self):
1082 # Kill the server
1083 self.tcpserver.abort = True
1084 del self.tcpserver
1085 for thread in self.threads:
1086 thread.join(2.0)
1087 #The logging call should try to connect, which should fail
1088 try:
1089 raise RuntimeError('Deliberate mistake')
1090 except RuntimeError:
1091 self.root_logger.exception('Never sent')
1092 self.root_logger.error('Never sent, either')
1093 now = time.time()
1094 self.assertTrue(self.sock_hdlr.retryTime > now)
1095 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1096 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001097
Christian Heimes180510d2008-03-03 19:15:45 +00001098class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001099
Christian Heimes180510d2008-03-03 19:15:45 +00001100 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001101
Christian Heimes180510d2008-03-03 19:15:45 +00001102 def setUp(self):
1103 """Create a dict to remember potentially destroyed objects."""
1104 BaseTest.setUp(self)
1105 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001106
Christian Heimes180510d2008-03-03 19:15:45 +00001107 def _watch_for_survival(self, *args):
1108 """Watch the given objects for survival, by creating weakrefs to
1109 them."""
1110 for obj in args:
1111 key = id(obj), repr(obj)
1112 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001113
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001114 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001115 """Assert that all objects watched for survival have survived."""
1116 # Trigger cycle breaking.
1117 gc.collect()
1118 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001119 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001120 if ref() is None:
1121 dead.append(repr_)
1122 if dead:
1123 self.fail("%d objects should have survived "
1124 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001125
Christian Heimes180510d2008-03-03 19:15:45 +00001126 def test_persistent_loggers(self):
1127 # Logger objects are persistent and retain their configuration, even
1128 # if visible references are destroyed.
1129 self.root_logger.setLevel(logging.INFO)
1130 foo = logging.getLogger("foo")
1131 self._watch_for_survival(foo)
1132 foo.setLevel(logging.DEBUG)
1133 self.root_logger.debug(self.next_message())
1134 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001135 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001136 ('foo', 'DEBUG', '2'),
1137 ])
1138 del foo
1139 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001140 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001141 # foo has retained its settings.
1142 bar = logging.getLogger("foo")
1143 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001144 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001145 ('foo', 'DEBUG', '2'),
1146 ('foo', 'DEBUG', '3'),
1147 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001148
Benjamin Petersonf91df042009-02-13 02:50:59 +00001149
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001150class EncodingTest(BaseTest):
1151 def test_encoding_plain_file(self):
1152 # In Python 2.x, a plain file object is treated as having no encoding.
1153 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001154 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1155 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001156 # the non-ascii data we write to the log.
1157 data = "foo\x80"
1158 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001159 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001160 log.addHandler(handler)
1161 try:
1162 # write non-ascii data to the log.
1163 log.warning(data)
1164 finally:
1165 log.removeHandler(handler)
1166 handler.close()
1167 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001168 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001169 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001170 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001171 finally:
1172 f.close()
1173 finally:
1174 if os.path.isfile(fn):
1175 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001176
Benjamin Petersonf91df042009-02-13 02:50:59 +00001177 def test_encoding_cyrillic_unicode(self):
1178 log = logging.getLogger("test")
1179 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1180 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1181 #Ensure it's written in a Cyrillic encoding
1182 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001183 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001184 stream = io.BytesIO()
1185 writer = writer_class(stream, 'strict')
1186 handler = logging.StreamHandler(writer)
1187 log.addHandler(handler)
1188 try:
1189 log.warning(message)
1190 finally:
1191 log.removeHandler(handler)
1192 handler.close()
1193 # check we wrote exactly those bytes, ignoring trailing \n etc
1194 s = stream.getvalue()
1195 #Compare against what the data should be when encoded in CP-1251
1196 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1197
1198
Georg Brandlf9734072008-12-07 15:30:06 +00001199class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001200
Georg Brandlf9734072008-12-07 15:30:06 +00001201 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001202 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001203 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001204 self.addCleanup(lambda: logging.captureWarnings(False))
1205 warnings.filterwarnings("always", category=UserWarning)
1206 stream = io.StringIO()
1207 h = logging.StreamHandler(stream)
1208 logger = logging.getLogger("py.warnings")
1209 logger.addHandler(h)
1210 warnings.warn("I'm warning you...")
1211 logger.removeHandler(h)
1212 s = stream.getvalue()
1213 h.close()
1214 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001215
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001216 #See if an explicit file uses the original implementation
1217 a_file = io.StringIO()
1218 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1219 a_file, "Dummy line")
1220 s = a_file.getvalue()
1221 a_file.close()
1222 self.assertEqual(s,
1223 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1224
1225 def test_warnings_no_handlers(self):
1226 with warnings.catch_warnings():
1227 logging.captureWarnings(True)
1228 self.addCleanup(lambda: logging.captureWarnings(False))
1229
1230 # confirm our assumption: no loggers are set
1231 logger = logging.getLogger("py.warnings")
1232 assert logger.handlers == []
1233
1234 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1235 self.assertTrue(len(logger.handlers) == 1)
1236 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001237
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001238
1239def formatFunc(format, datefmt=None):
1240 return logging.Formatter(format, datefmt)
1241
1242def handlerFunc():
1243 return logging.StreamHandler()
1244
1245class CustomHandler(logging.StreamHandler):
1246 pass
1247
1248class ConfigDictTest(BaseTest):
1249
1250 """Reading logging config from a dictionary."""
1251
1252 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1253
1254 # config0 is a standard configuration.
1255 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001256 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001257 'formatters': {
1258 'form1' : {
1259 'format' : '%(levelname)s ++ %(message)s',
1260 },
1261 },
1262 'handlers' : {
1263 'hand1' : {
1264 'class' : 'logging.StreamHandler',
1265 'formatter' : 'form1',
1266 'level' : 'NOTSET',
1267 'stream' : 'ext://sys.stdout',
1268 },
1269 },
1270 'root' : {
1271 'level' : 'WARNING',
1272 'handlers' : ['hand1'],
1273 },
1274 }
1275
1276 # config1 adds a little to the standard configuration.
1277 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001278 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001279 'formatters': {
1280 'form1' : {
1281 'format' : '%(levelname)s ++ %(message)s',
1282 },
1283 },
1284 'handlers' : {
1285 'hand1' : {
1286 'class' : 'logging.StreamHandler',
1287 'formatter' : 'form1',
1288 'level' : 'NOTSET',
1289 'stream' : 'ext://sys.stdout',
1290 },
1291 },
1292 'loggers' : {
1293 'compiler.parser' : {
1294 'level' : 'DEBUG',
1295 'handlers' : ['hand1'],
1296 },
1297 },
1298 'root' : {
1299 'level' : 'WARNING',
1300 },
1301 }
1302
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001303 # config1a moves the handler to the root. Used with config8a
1304 config1a = {
1305 'version': 1,
1306 'formatters': {
1307 'form1' : {
1308 'format' : '%(levelname)s ++ %(message)s',
1309 },
1310 },
1311 'handlers' : {
1312 'hand1' : {
1313 'class' : 'logging.StreamHandler',
1314 'formatter' : 'form1',
1315 'level' : 'NOTSET',
1316 'stream' : 'ext://sys.stdout',
1317 },
1318 },
1319 'loggers' : {
1320 'compiler.parser' : {
1321 'level' : 'DEBUG',
1322 },
1323 },
1324 'root' : {
1325 'level' : 'WARNING',
1326 'handlers' : ['hand1'],
1327 },
1328 }
1329
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001330 # config2 has a subtle configuration error that should be reported
1331 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001332 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001333 'formatters': {
1334 'form1' : {
1335 'format' : '%(levelname)s ++ %(message)s',
1336 },
1337 },
1338 'handlers' : {
1339 'hand1' : {
1340 'class' : 'logging.StreamHandler',
1341 'formatter' : 'form1',
1342 'level' : 'NOTSET',
1343 'stream' : 'ext://sys.stdbout',
1344 },
1345 },
1346 'loggers' : {
1347 'compiler.parser' : {
1348 'level' : 'DEBUG',
1349 'handlers' : ['hand1'],
1350 },
1351 },
1352 'root' : {
1353 'level' : 'WARNING',
1354 },
1355 }
1356
1357 #As config1 but with a misspelt level on a handler
1358 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001359 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001360 'formatters': {
1361 'form1' : {
1362 'format' : '%(levelname)s ++ %(message)s',
1363 },
1364 },
1365 'handlers' : {
1366 'hand1' : {
1367 'class' : 'logging.StreamHandler',
1368 'formatter' : 'form1',
1369 'level' : 'NTOSET',
1370 'stream' : 'ext://sys.stdout',
1371 },
1372 },
1373 'loggers' : {
1374 'compiler.parser' : {
1375 'level' : 'DEBUG',
1376 'handlers' : ['hand1'],
1377 },
1378 },
1379 'root' : {
1380 'level' : 'WARNING',
1381 },
1382 }
1383
1384
1385 #As config1 but with a misspelt level on a logger
1386 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001387 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001388 'formatters': {
1389 'form1' : {
1390 'format' : '%(levelname)s ++ %(message)s',
1391 },
1392 },
1393 'handlers' : {
1394 'hand1' : {
1395 'class' : 'logging.StreamHandler',
1396 'formatter' : 'form1',
1397 'level' : 'NOTSET',
1398 'stream' : 'ext://sys.stdout',
1399 },
1400 },
1401 'loggers' : {
1402 'compiler.parser' : {
1403 'level' : 'DEBUG',
1404 'handlers' : ['hand1'],
1405 },
1406 },
1407 'root' : {
1408 'level' : 'WRANING',
1409 },
1410 }
1411
1412 # config3 has a less subtle configuration error
1413 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001414 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001415 'formatters': {
1416 'form1' : {
1417 'format' : '%(levelname)s ++ %(message)s',
1418 },
1419 },
1420 'handlers' : {
1421 'hand1' : {
1422 'class' : 'logging.StreamHandler',
1423 'formatter' : 'misspelled_name',
1424 'level' : 'NOTSET',
1425 'stream' : 'ext://sys.stdout',
1426 },
1427 },
1428 'loggers' : {
1429 'compiler.parser' : {
1430 'level' : 'DEBUG',
1431 'handlers' : ['hand1'],
1432 },
1433 },
1434 'root' : {
1435 'level' : 'WARNING',
1436 },
1437 }
1438
1439 # config4 specifies a custom formatter class to be loaded
1440 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001441 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001442 'formatters': {
1443 'form1' : {
1444 '()' : __name__ + '.ExceptionFormatter',
1445 'format' : '%(levelname)s:%(name)s:%(message)s',
1446 },
1447 },
1448 'handlers' : {
1449 'hand1' : {
1450 'class' : 'logging.StreamHandler',
1451 'formatter' : 'form1',
1452 'level' : 'NOTSET',
1453 'stream' : 'ext://sys.stdout',
1454 },
1455 },
1456 'root' : {
1457 'level' : 'NOTSET',
1458 'handlers' : ['hand1'],
1459 },
1460 }
1461
1462 # As config4 but using an actual callable rather than a string
1463 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001464 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001465 'formatters': {
1466 'form1' : {
1467 '()' : ExceptionFormatter,
1468 'format' : '%(levelname)s:%(name)s:%(message)s',
1469 },
1470 'form2' : {
1471 '()' : __name__ + '.formatFunc',
1472 'format' : '%(levelname)s:%(name)s:%(message)s',
1473 },
1474 'form3' : {
1475 '()' : formatFunc,
1476 'format' : '%(levelname)s:%(name)s:%(message)s',
1477 },
1478 },
1479 'handlers' : {
1480 'hand1' : {
1481 'class' : 'logging.StreamHandler',
1482 'formatter' : 'form1',
1483 'level' : 'NOTSET',
1484 'stream' : 'ext://sys.stdout',
1485 },
1486 'hand2' : {
1487 '()' : handlerFunc,
1488 },
1489 },
1490 'root' : {
1491 'level' : 'NOTSET',
1492 'handlers' : ['hand1'],
1493 },
1494 }
1495
1496 # config5 specifies a custom handler class to be loaded
1497 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001498 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001499 'formatters': {
1500 'form1' : {
1501 'format' : '%(levelname)s ++ %(message)s',
1502 },
1503 },
1504 'handlers' : {
1505 'hand1' : {
1506 'class' : __name__ + '.CustomHandler',
1507 'formatter' : 'form1',
1508 'level' : 'NOTSET',
1509 'stream' : 'ext://sys.stdout',
1510 },
1511 },
1512 'loggers' : {
1513 'compiler.parser' : {
1514 'level' : 'DEBUG',
1515 'handlers' : ['hand1'],
1516 },
1517 },
1518 'root' : {
1519 'level' : 'WARNING',
1520 },
1521 }
1522
1523 # config6 specifies a custom handler class to be loaded
1524 # but has bad arguments
1525 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001526 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001527 'formatters': {
1528 'form1' : {
1529 'format' : '%(levelname)s ++ %(message)s',
1530 },
1531 },
1532 'handlers' : {
1533 'hand1' : {
1534 'class' : __name__ + '.CustomHandler',
1535 'formatter' : 'form1',
1536 'level' : 'NOTSET',
1537 'stream' : 'ext://sys.stdout',
1538 '9' : 'invalid parameter name',
1539 },
1540 },
1541 'loggers' : {
1542 'compiler.parser' : {
1543 'level' : 'DEBUG',
1544 'handlers' : ['hand1'],
1545 },
1546 },
1547 'root' : {
1548 'level' : 'WARNING',
1549 },
1550 }
1551
1552 #config 7 does not define compiler.parser but defines compiler.lexer
1553 #so compiler.parser should be disabled after applying it
1554 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001555 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001556 'formatters': {
1557 'form1' : {
1558 'format' : '%(levelname)s ++ %(message)s',
1559 },
1560 },
1561 'handlers' : {
1562 'hand1' : {
1563 'class' : 'logging.StreamHandler',
1564 'formatter' : 'form1',
1565 'level' : 'NOTSET',
1566 'stream' : 'ext://sys.stdout',
1567 },
1568 },
1569 'loggers' : {
1570 'compiler.lexer' : {
1571 'level' : 'DEBUG',
1572 'handlers' : ['hand1'],
1573 },
1574 },
1575 'root' : {
1576 'level' : 'WARNING',
1577 },
1578 }
1579
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001580 # config8 defines both compiler and compiler.lexer
1581 # so compiler.parser should not be disabled (since
1582 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001583 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001584 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001585 'disable_existing_loggers' : False,
1586 'formatters': {
1587 'form1' : {
1588 'format' : '%(levelname)s ++ %(message)s',
1589 },
1590 },
1591 'handlers' : {
1592 'hand1' : {
1593 'class' : 'logging.StreamHandler',
1594 'formatter' : 'form1',
1595 'level' : 'NOTSET',
1596 'stream' : 'ext://sys.stdout',
1597 },
1598 },
1599 'loggers' : {
1600 'compiler' : {
1601 'level' : 'DEBUG',
1602 'handlers' : ['hand1'],
1603 },
1604 'compiler.lexer' : {
1605 },
1606 },
1607 'root' : {
1608 'level' : 'WARNING',
1609 },
1610 }
1611
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001612 # config8a disables existing loggers
1613 config8a = {
1614 'version': 1,
1615 'disable_existing_loggers' : True,
1616 'formatters': {
1617 'form1' : {
1618 'format' : '%(levelname)s ++ %(message)s',
1619 },
1620 },
1621 'handlers' : {
1622 'hand1' : {
1623 'class' : 'logging.StreamHandler',
1624 'formatter' : 'form1',
1625 'level' : 'NOTSET',
1626 'stream' : 'ext://sys.stdout',
1627 },
1628 },
1629 'loggers' : {
1630 'compiler' : {
1631 'level' : 'DEBUG',
1632 'handlers' : ['hand1'],
1633 },
1634 'compiler.lexer' : {
1635 },
1636 },
1637 'root' : {
1638 'level' : 'WARNING',
1639 },
1640 }
1641
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001642 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001643 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001644 'formatters': {
1645 'form1' : {
1646 'format' : '%(levelname)s ++ %(message)s',
1647 },
1648 },
1649 'handlers' : {
1650 'hand1' : {
1651 'class' : 'logging.StreamHandler',
1652 'formatter' : 'form1',
1653 'level' : 'WARNING',
1654 'stream' : 'ext://sys.stdout',
1655 },
1656 },
1657 'loggers' : {
1658 'compiler.parser' : {
1659 'level' : 'WARNING',
1660 'handlers' : ['hand1'],
1661 },
1662 },
1663 'root' : {
1664 'level' : 'NOTSET',
1665 },
1666 }
1667
1668 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001669 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001670 'incremental' : True,
1671 'handlers' : {
1672 'hand1' : {
1673 'level' : 'WARNING',
1674 },
1675 },
1676 'loggers' : {
1677 'compiler.parser' : {
1678 'level' : 'INFO',
1679 },
1680 },
1681 }
1682
1683 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001684 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001685 'incremental' : True,
1686 'handlers' : {
1687 'hand1' : {
1688 'level' : 'INFO',
1689 },
1690 },
1691 'loggers' : {
1692 'compiler.parser' : {
1693 'level' : 'INFO',
1694 },
1695 },
1696 }
1697
1698 #As config1 but with a filter added
1699 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001700 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001701 'formatters': {
1702 'form1' : {
1703 'format' : '%(levelname)s ++ %(message)s',
1704 },
1705 },
1706 'filters' : {
1707 'filt1' : {
1708 'name' : 'compiler.parser',
1709 },
1710 },
1711 'handlers' : {
1712 'hand1' : {
1713 'class' : 'logging.StreamHandler',
1714 'formatter' : 'form1',
1715 'level' : 'NOTSET',
1716 'stream' : 'ext://sys.stdout',
1717 'filters' : ['filt1'],
1718 },
1719 },
1720 'loggers' : {
1721 'compiler.parser' : {
1722 'level' : 'DEBUG',
1723 'filters' : ['filt1'],
1724 },
1725 },
1726 'root' : {
1727 'level' : 'WARNING',
1728 'handlers' : ['hand1'],
1729 },
1730 }
1731
1732 #As config1 but using cfg:// references
1733 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001734 'version': 1,
1735 'true_formatters': {
1736 'form1' : {
1737 'format' : '%(levelname)s ++ %(message)s',
1738 },
1739 },
1740 'handler_configs': {
1741 'hand1' : {
1742 'class' : 'logging.StreamHandler',
1743 'formatter' : 'form1',
1744 'level' : 'NOTSET',
1745 'stream' : 'ext://sys.stdout',
1746 },
1747 },
1748 'formatters' : 'cfg://true_formatters',
1749 'handlers' : {
1750 'hand1' : 'cfg://handler_configs[hand1]',
1751 },
1752 'loggers' : {
1753 'compiler.parser' : {
1754 'level' : 'DEBUG',
1755 'handlers' : ['hand1'],
1756 },
1757 },
1758 'root' : {
1759 'level' : 'WARNING',
1760 },
1761 }
1762
1763 #As config11 but missing the version key
1764 config12 = {
1765 'true_formatters': {
1766 'form1' : {
1767 'format' : '%(levelname)s ++ %(message)s',
1768 },
1769 },
1770 'handler_configs': {
1771 'hand1' : {
1772 'class' : 'logging.StreamHandler',
1773 'formatter' : 'form1',
1774 'level' : 'NOTSET',
1775 'stream' : 'ext://sys.stdout',
1776 },
1777 },
1778 'formatters' : 'cfg://true_formatters',
1779 'handlers' : {
1780 'hand1' : 'cfg://handler_configs[hand1]',
1781 },
1782 'loggers' : {
1783 'compiler.parser' : {
1784 'level' : 'DEBUG',
1785 'handlers' : ['hand1'],
1786 },
1787 },
1788 'root' : {
1789 'level' : 'WARNING',
1790 },
1791 }
1792
1793 #As config11 but using an unsupported version
1794 config13 = {
1795 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001796 'true_formatters': {
1797 'form1' : {
1798 'format' : '%(levelname)s ++ %(message)s',
1799 },
1800 },
1801 'handler_configs': {
1802 'hand1' : {
1803 'class' : 'logging.StreamHandler',
1804 'formatter' : 'form1',
1805 'level' : 'NOTSET',
1806 'stream' : 'ext://sys.stdout',
1807 },
1808 },
1809 'formatters' : 'cfg://true_formatters',
1810 'handlers' : {
1811 'hand1' : 'cfg://handler_configs[hand1]',
1812 },
1813 'loggers' : {
1814 'compiler.parser' : {
1815 'level' : 'DEBUG',
1816 'handlers' : ['hand1'],
1817 },
1818 },
1819 'root' : {
1820 'level' : 'WARNING',
1821 },
1822 }
1823
1824 def apply_config(self, conf):
1825 logging.config.dictConfig(conf)
1826
1827 def test_config0_ok(self):
1828 # A simple config which overrides the default settings.
1829 with captured_stdout() as output:
1830 self.apply_config(self.config0)
1831 logger = logging.getLogger()
1832 # Won't output anything
1833 logger.info(self.next_message())
1834 # Outputs a message
1835 logger.error(self.next_message())
1836 self.assert_log_lines([
1837 ('ERROR', '2'),
1838 ], stream=output)
1839 # Original logger output is empty.
1840 self.assert_log_lines([])
1841
1842 def test_config1_ok(self, config=config1):
1843 # A config defining a sub-parser as well.
1844 with captured_stdout() as output:
1845 self.apply_config(config)
1846 logger = logging.getLogger("compiler.parser")
1847 # Both will output a message
1848 logger.info(self.next_message())
1849 logger.error(self.next_message())
1850 self.assert_log_lines([
1851 ('INFO', '1'),
1852 ('ERROR', '2'),
1853 ], stream=output)
1854 # Original logger output is empty.
1855 self.assert_log_lines([])
1856
1857 def test_config2_failure(self):
1858 # A simple config which overrides the default settings.
1859 self.assertRaises(Exception, self.apply_config, self.config2)
1860
1861 def test_config2a_failure(self):
1862 # A simple config which overrides the default settings.
1863 self.assertRaises(Exception, self.apply_config, self.config2a)
1864
1865 def test_config2b_failure(self):
1866 # A simple config which overrides the default settings.
1867 self.assertRaises(Exception, self.apply_config, self.config2b)
1868
1869 def test_config3_failure(self):
1870 # A simple config which overrides the default settings.
1871 self.assertRaises(Exception, self.apply_config, self.config3)
1872
1873 def test_config4_ok(self):
1874 # A config specifying a custom formatter class.
1875 with captured_stdout() as output:
1876 self.apply_config(self.config4)
1877 #logger = logging.getLogger()
1878 try:
1879 raise RuntimeError()
1880 except RuntimeError:
1881 logging.exception("just testing")
1882 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001883 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001884 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1885 # Original logger output is empty
1886 self.assert_log_lines([])
1887
1888 def test_config4a_ok(self):
1889 # A config specifying a custom formatter class.
1890 with captured_stdout() as output:
1891 self.apply_config(self.config4a)
1892 #logger = logging.getLogger()
1893 try:
1894 raise RuntimeError()
1895 except RuntimeError:
1896 logging.exception("just testing")
1897 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001898 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001899 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1900 # Original logger output is empty
1901 self.assert_log_lines([])
1902
1903 def test_config5_ok(self):
1904 self.test_config1_ok(config=self.config5)
1905
1906 def test_config6_failure(self):
1907 self.assertRaises(Exception, self.apply_config, self.config6)
1908
1909 def test_config7_ok(self):
1910 with captured_stdout() as output:
1911 self.apply_config(self.config1)
1912 logger = logging.getLogger("compiler.parser")
1913 # Both will output a message
1914 logger.info(self.next_message())
1915 logger.error(self.next_message())
1916 self.assert_log_lines([
1917 ('INFO', '1'),
1918 ('ERROR', '2'),
1919 ], stream=output)
1920 # Original logger output is empty.
1921 self.assert_log_lines([])
1922 with captured_stdout() as output:
1923 self.apply_config(self.config7)
1924 logger = logging.getLogger("compiler.parser")
1925 self.assertTrue(logger.disabled)
1926 logger = logging.getLogger("compiler.lexer")
1927 # Both will output a message
1928 logger.info(self.next_message())
1929 logger.error(self.next_message())
1930 self.assert_log_lines([
1931 ('INFO', '3'),
1932 ('ERROR', '4'),
1933 ], stream=output)
1934 # Original logger output is empty.
1935 self.assert_log_lines([])
1936
1937 #Same as test_config_7_ok but don't disable old loggers.
1938 def test_config_8_ok(self):
1939 with captured_stdout() as output:
1940 self.apply_config(self.config1)
1941 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001942 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001943 logger.info(self.next_message())
1944 logger.error(self.next_message())
1945 self.assert_log_lines([
1946 ('INFO', '1'),
1947 ('ERROR', '2'),
1948 ], stream=output)
1949 # Original logger output is empty.
1950 self.assert_log_lines([])
1951 with captured_stdout() as output:
1952 self.apply_config(self.config8)
1953 logger = logging.getLogger("compiler.parser")
1954 self.assertFalse(logger.disabled)
1955 # Both will output a message
1956 logger.info(self.next_message())
1957 logger.error(self.next_message())
1958 logger = logging.getLogger("compiler.lexer")
1959 # Both will output a message
1960 logger.info(self.next_message())
1961 logger.error(self.next_message())
1962 self.assert_log_lines([
1963 ('INFO', '3'),
1964 ('ERROR', '4'),
1965 ('INFO', '5'),
1966 ('ERROR', '6'),
1967 ], stream=output)
1968 # Original logger output is empty.
1969 self.assert_log_lines([])
1970
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001971 def test_config_8a_ok(self):
1972 with captured_stdout() as output:
1973 self.apply_config(self.config1a)
1974 logger = logging.getLogger("compiler.parser")
1975 # See issue #11424. compiler-hyphenated sorts
1976 # between compiler and compiler.xyz and this
1977 # was preventing compiler.xyz from being included
1978 # in the child loggers of compiler because of an
1979 # overzealous loop termination condition.
1980 hyphenated = logging.getLogger('compiler-hyphenated')
1981 # All will output a message
1982 logger.info(self.next_message())
1983 logger.error(self.next_message())
1984 hyphenated.critical(self.next_message())
1985 self.assert_log_lines([
1986 ('INFO', '1'),
1987 ('ERROR', '2'),
1988 ('CRITICAL', '3'),
1989 ], stream=output)
1990 # Original logger output is empty.
1991 self.assert_log_lines([])
1992 with captured_stdout() as output:
1993 self.apply_config(self.config8a)
1994 logger = logging.getLogger("compiler.parser")
1995 self.assertFalse(logger.disabled)
1996 # Both will output a message
1997 logger.info(self.next_message())
1998 logger.error(self.next_message())
1999 logger = logging.getLogger("compiler.lexer")
2000 # Both will output a message
2001 logger.info(self.next_message())
2002 logger.error(self.next_message())
2003 # Will not appear
2004 hyphenated.critical(self.next_message())
2005 self.assert_log_lines([
2006 ('INFO', '4'),
2007 ('ERROR', '5'),
2008 ('INFO', '6'),
2009 ('ERROR', '7'),
2010 ], stream=output)
2011 # Original logger output is empty.
2012 self.assert_log_lines([])
2013
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002014 def test_config_9_ok(self):
2015 with captured_stdout() as output:
2016 self.apply_config(self.config9)
2017 logger = logging.getLogger("compiler.parser")
2018 #Nothing will be output since both handler and logger are set to WARNING
2019 logger.info(self.next_message())
2020 self.assert_log_lines([], stream=output)
2021 self.apply_config(self.config9a)
2022 #Nothing will be output since both handler is still set to WARNING
2023 logger.info(self.next_message())
2024 self.assert_log_lines([], stream=output)
2025 self.apply_config(self.config9b)
2026 #Message should now be output
2027 logger.info(self.next_message())
2028 self.assert_log_lines([
2029 ('INFO', '3'),
2030 ], stream=output)
2031
2032 def test_config_10_ok(self):
2033 with captured_stdout() as output:
2034 self.apply_config(self.config10)
2035 logger = logging.getLogger("compiler.parser")
2036 logger.warning(self.next_message())
2037 logger = logging.getLogger('compiler')
2038 #Not output, because filtered
2039 logger.warning(self.next_message())
2040 logger = logging.getLogger('compiler.lexer')
2041 #Not output, because filtered
2042 logger.warning(self.next_message())
2043 logger = logging.getLogger("compiler.parser.codegen")
2044 #Output, as not filtered
2045 logger.error(self.next_message())
2046 self.assert_log_lines([
2047 ('WARNING', '1'),
2048 ('ERROR', '4'),
2049 ], stream=output)
2050
2051 def test_config11_ok(self):
2052 self.test_config1_ok(self.config11)
2053
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002054 def test_config12_failure(self):
2055 self.assertRaises(Exception, self.apply_config, self.config12)
2056
2057 def test_config13_failure(self):
2058 self.assertRaises(Exception, self.apply_config, self.config13)
2059
Victor Stinner45df8202010-04-28 22:31:17 +00002060 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002061 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002062 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002063 # Ask for a randomly assigned port (by using port 0)
2064 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002065 t.start()
2066 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002067 # Now get the port allocated
2068 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002069 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002070 try:
2071 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2072 sock.settimeout(2.0)
2073 sock.connect(('localhost', port))
2074
2075 slen = struct.pack('>L', len(text))
2076 s = slen + text
2077 sentsofar = 0
2078 left = len(s)
2079 while left > 0:
2080 sent = sock.send(s[sentsofar:])
2081 sentsofar += sent
2082 left -= sent
2083 sock.close()
2084 finally:
2085 t.ready.wait(2.0)
2086 logging.config.stopListening()
2087 t.join(2.0)
2088
2089 def test_listen_config_10_ok(self):
2090 with captured_stdout() as output:
2091 self.setup_via_listener(json.dumps(self.config10))
2092 logger = logging.getLogger("compiler.parser")
2093 logger.warning(self.next_message())
2094 logger = logging.getLogger('compiler')
2095 #Not output, because filtered
2096 logger.warning(self.next_message())
2097 logger = logging.getLogger('compiler.lexer')
2098 #Not output, because filtered
2099 logger.warning(self.next_message())
2100 logger = logging.getLogger("compiler.parser.codegen")
2101 #Output, as not filtered
2102 logger.error(self.next_message())
2103 self.assert_log_lines([
2104 ('WARNING', '1'),
2105 ('ERROR', '4'),
2106 ], stream=output)
2107
2108 def test_listen_config_1_ok(self):
2109 with captured_stdout() as output:
2110 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2111 logger = logging.getLogger("compiler.parser")
2112 # Both will output a message
2113 logger.info(self.next_message())
2114 logger.error(self.next_message())
2115 self.assert_log_lines([
2116 ('INFO', '1'),
2117 ('ERROR', '2'),
2118 ], stream=output)
2119 # Original logger output is empty.
2120 self.assert_log_lines([])
2121
Vinay Sajip373baef2011-04-26 20:05:24 +01002122 def test_baseconfig(self):
2123 d = {
2124 'atuple': (1, 2, 3),
2125 'alist': ['a', 'b', 'c'],
2126 'adict': {'d': 'e', 'f': 3 },
2127 'nest1': ('g', ('h', 'i'), 'j'),
2128 'nest2': ['k', ['l', 'm'], 'n'],
2129 'nest3': ['o', 'cfg://alist', 'p'],
2130 }
2131 bc = logging.config.BaseConfigurator(d)
2132 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2133 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2134 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2135 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2136 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2137 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2138 v = bc.convert('cfg://nest3')
2139 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2140 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2141 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2142 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002143
2144class ManagerTest(BaseTest):
2145 def test_manager_loggerclass(self):
2146 logged = []
2147
2148 class MyLogger(logging.Logger):
2149 def _log(self, level, msg, args, exc_info=None, extra=None):
2150 logged.append(msg)
2151
2152 man = logging.Manager(None)
2153 self.assertRaises(TypeError, man.setLoggerClass, int)
2154 man.setLoggerClass(MyLogger)
2155 logger = man.getLogger('test')
2156 logger.warning('should appear in logged')
2157 logging.warning('should not appear in logged')
2158
2159 self.assertEqual(logged, ['should appear in logged'])
2160
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002161 def test_set_log_record_factory(self):
2162 man = logging.Manager(None)
2163 expected = object()
2164 man.setLogRecordFactory(expected)
2165 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002166
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002167class ChildLoggerTest(BaseTest):
2168 def test_child_loggers(self):
2169 r = logging.getLogger()
2170 l1 = logging.getLogger('abc')
2171 l2 = logging.getLogger('def.ghi')
2172 c1 = r.getChild('xyz')
2173 c2 = r.getChild('uvw.xyz')
2174 self.assertTrue(c1 is logging.getLogger('xyz'))
2175 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2176 c1 = l1.getChild('def')
2177 c2 = c1.getChild('ghi')
2178 c3 = l1.getChild('def.ghi')
2179 self.assertTrue(c1 is logging.getLogger('abc.def'))
2180 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2181 self.assertTrue(c2 is c3)
2182
2183
Vinay Sajip6fac8172010-10-19 20:44:14 +00002184class DerivedLogRecord(logging.LogRecord):
2185 pass
2186
Vinay Sajip61561522010-12-03 11:50:38 +00002187class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002188
2189 def setUp(self):
2190 class CheckingFilter(logging.Filter):
2191 def __init__(self, cls):
2192 self.cls = cls
2193
2194 def filter(self, record):
2195 t = type(record)
2196 if t is not self.cls:
2197 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2198 self.cls)
2199 raise TypeError(msg)
2200 return True
2201
2202 BaseTest.setUp(self)
2203 self.filter = CheckingFilter(DerivedLogRecord)
2204 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002205 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002206
2207 def tearDown(self):
2208 self.root_logger.removeFilter(self.filter)
2209 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002210 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002211
2212 def test_logrecord_class(self):
2213 self.assertRaises(TypeError, self.root_logger.warning,
2214 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002215 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002216 self.root_logger.error(self.next_message())
2217 self.assert_log_lines([
2218 ('root', 'ERROR', '2'),
2219 ])
2220
2221
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002222class QueueHandlerTest(BaseTest):
2223 # Do not bother with a logger name group.
2224 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2225
2226 def setUp(self):
2227 BaseTest.setUp(self)
2228 self.queue = queue.Queue(-1)
2229 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2230 self.que_logger = logging.getLogger('que')
2231 self.que_logger.propagate = False
2232 self.que_logger.setLevel(logging.WARNING)
2233 self.que_logger.addHandler(self.que_hdlr)
2234
2235 def tearDown(self):
2236 self.que_hdlr.close()
2237 BaseTest.tearDown(self)
2238
2239 def test_queue_handler(self):
2240 self.que_logger.debug(self.next_message())
2241 self.assertRaises(queue.Empty, self.queue.get_nowait)
2242 self.que_logger.info(self.next_message())
2243 self.assertRaises(queue.Empty, self.queue.get_nowait)
2244 msg = self.next_message()
2245 self.que_logger.warning(msg)
2246 data = self.queue.get_nowait()
2247 self.assertTrue(isinstance(data, logging.LogRecord))
2248 self.assertEqual(data.name, self.que_logger.name)
2249 self.assertEqual((data.msg, data.args), (msg, None))
2250
Vinay Sajipe723e962011-04-15 22:27:17 +01002251 def test_queue_listener(self):
2252 handler = TestHandler(Matcher())
2253 listener = logging.handlers.QueueListener(self.queue, handler)
2254 listener.start()
2255 try:
2256 self.que_logger.warning(self.next_message())
2257 self.que_logger.error(self.next_message())
2258 self.que_logger.critical(self.next_message())
2259 finally:
2260 listener.stop()
2261 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2262 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2263 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2264
Vinay Sajip37eb3382011-04-26 20:26:41 +01002265ZERO = datetime.timedelta(0)
2266
2267class UTC(datetime.tzinfo):
2268 def utcoffset(self, dt):
2269 return ZERO
2270
2271 dst = utcoffset
2272
2273 def tzname(self, dt):
2274 return 'UTC'
2275
2276utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002277
Vinay Sajipa39c5712010-10-25 13:57:39 +00002278class FormatterTest(unittest.TestCase):
2279 def setUp(self):
2280 self.common = {
2281 'name': 'formatter.test',
2282 'level': logging.DEBUG,
2283 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2284 'lineno': 42,
2285 'exc_info': None,
2286 'func': None,
2287 'msg': 'Message with %d %s',
2288 'args': (2, 'placeholders'),
2289 }
2290 self.variants = {
2291 }
2292
2293 def get_record(self, name=None):
2294 result = dict(self.common)
2295 if name is not None:
2296 result.update(self.variants[name])
2297 return logging.makeLogRecord(result)
2298
2299 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002300 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002301 r = self.get_record()
2302 f = logging.Formatter('${%(message)s}')
2303 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2304 f = logging.Formatter('%(random)s')
2305 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002306 self.assertFalse(f.usesTime())
2307 f = logging.Formatter('%(asctime)s')
2308 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002309 f = logging.Formatter('%(asctime)-15s')
2310 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002311 f = logging.Formatter('asctime')
2312 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002313
2314 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002315 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002316 r = self.get_record()
2317 f = logging.Formatter('$%{message}%$', style='{')
2318 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2319 f = logging.Formatter('{random}', style='{')
2320 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002321 self.assertFalse(f.usesTime())
2322 f = logging.Formatter('{asctime}', style='{')
2323 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002324 f = logging.Formatter('{asctime!s:15}', style='{')
2325 self.assertTrue(f.usesTime())
2326 f = logging.Formatter('{asctime:15}', style='{')
2327 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002328 f = logging.Formatter('asctime', style='{')
2329 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002330
2331 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002332 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002333 r = self.get_record()
2334 f = logging.Formatter('$message', style='$')
2335 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2336 f = logging.Formatter('$$%${message}%$$', style='$')
2337 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2338 f = logging.Formatter('${random}', style='$')
2339 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002340 self.assertFalse(f.usesTime())
2341 f = logging.Formatter('${asctime}', style='$')
2342 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002343 f = logging.Formatter('${asctime', style='$')
2344 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002345 f = logging.Formatter('$asctime', style='$')
2346 self.assertTrue(f.usesTime())
2347 f = logging.Formatter('asctime', style='$')
2348 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002349
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002350 def test_invalid_style(self):
2351 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2352
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002353 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002354 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002355 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002356 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002357 r.msecs = 123
2358 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002359 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002360 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2361 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002362 f.format(r)
2363 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2364
2365class TestBufferingFormatter(logging.BufferingFormatter):
2366 def formatHeader(self, records):
2367 return '[(%d)' % len(records)
2368
2369 def formatFooter(self, records):
2370 return '(%d)]' % len(records)
2371
2372class BufferingFormatterTest(unittest.TestCase):
2373 def setUp(self):
2374 self.records = [
2375 logging.makeLogRecord({'msg': 'one'}),
2376 logging.makeLogRecord({'msg': 'two'}),
2377 ]
2378
2379 def test_default(self):
2380 f = logging.BufferingFormatter()
2381 self.assertEqual('', f.format([]))
2382 self.assertEqual('onetwo', f.format(self.records))
2383
2384 def test_custom(self):
2385 f = TestBufferingFormatter()
2386 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2387 lf = logging.Formatter('<%(message)s>')
2388 f = TestBufferingFormatter(lf)
2389 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002390
2391class ExceptionTest(BaseTest):
2392 def test_formatting(self):
2393 r = self.root_logger
2394 h = RecordingHandler()
2395 r.addHandler(h)
2396 try:
2397 raise RuntimeError('deliberate mistake')
2398 except:
2399 logging.exception('failed', stack_info=True)
2400 r.removeHandler(h)
2401 h.close()
2402 r = h.records[0]
2403 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2404 'call last):\n'))
2405 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2406 'deliberate mistake'))
2407 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2408 'call last):\n'))
2409 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2410 'stack_info=True)'))
2411
2412
Vinay Sajip5a27d402010-12-10 11:42:57 +00002413class LastResortTest(BaseTest):
2414 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002415 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002416 root = self.root_logger
2417 root.removeHandler(self.root_hdlr)
2418 old_stderr = sys.stderr
2419 old_lastresort = logging.lastResort
2420 old_raise_exceptions = logging.raiseExceptions
2421 try:
2422 sys.stderr = sio = io.StringIO()
2423 root.warning('This is your final chance!')
2424 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2425 #No handlers and no last resort, so 'No handlers' message
2426 logging.lastResort = None
2427 sys.stderr = sio = io.StringIO()
2428 root.warning('This is your final chance!')
2429 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2430 # 'No handlers' message only printed once
2431 sys.stderr = sio = io.StringIO()
2432 root.warning('This is your final chance!')
2433 self.assertEqual(sio.getvalue(), '')
2434 root.manager.emittedNoHandlerWarning = False
2435 #If raiseExceptions is False, no message is printed
2436 logging.raiseExceptions = False
2437 sys.stderr = sio = io.StringIO()
2438 root.warning('This is your final chance!')
2439 self.assertEqual(sio.getvalue(), '')
2440 finally:
2441 sys.stderr = old_stderr
2442 root.addHandler(self.root_hdlr)
2443 logging.lastResort = old_lastresort
2444 logging.raiseExceptions = old_raise_exceptions
2445
2446
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002447class FakeHandler:
2448
2449 def __init__(self, identifier, called):
2450 for method in ('acquire', 'flush', 'close', 'release'):
2451 setattr(self, method, self.record_call(identifier, method, called))
2452
2453 def record_call(self, identifier, method_name, called):
2454 def inner():
2455 called.append('{} - {}'.format(identifier, method_name))
2456 return inner
2457
2458
2459class RecordingHandler(logging.NullHandler):
2460
2461 def __init__(self, *args, **kwargs):
2462 super(RecordingHandler, self).__init__(*args, **kwargs)
2463 self.records = []
2464
2465 def handle(self, record):
2466 """Keep track of all the emitted records."""
2467 self.records.append(record)
2468
2469
2470class ShutdownTest(BaseTest):
2471
Vinay Sajip5e66b162011-04-20 15:41:14 +01002472 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002473
2474 def setUp(self):
2475 super(ShutdownTest, self).setUp()
2476 self.called = []
2477
2478 raise_exceptions = logging.raiseExceptions
2479 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2480
2481 def raise_error(self, error):
2482 def inner():
2483 raise error()
2484 return inner
2485
2486 def test_no_failure(self):
2487 # create some fake handlers
2488 handler0 = FakeHandler(0, self.called)
2489 handler1 = FakeHandler(1, self.called)
2490 handler2 = FakeHandler(2, self.called)
2491
2492 # create live weakref to those handlers
2493 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2494
2495 logging.shutdown(handlerList=list(handlers))
2496
2497 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2498 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2499 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2500 self.assertEqual(expected, self.called)
2501
2502 def _test_with_failure_in_method(self, method, error):
2503 handler = FakeHandler(0, self.called)
2504 setattr(handler, method, self.raise_error(error))
2505 handlers = [logging.weakref.ref(handler)]
2506
2507 logging.shutdown(handlerList=list(handlers))
2508
2509 self.assertEqual('0 - release', self.called[-1])
2510
2511 def test_with_ioerror_in_acquire(self):
2512 self._test_with_failure_in_method('acquire', IOError)
2513
2514 def test_with_ioerror_in_flush(self):
2515 self._test_with_failure_in_method('flush', IOError)
2516
2517 def test_with_ioerror_in_close(self):
2518 self._test_with_failure_in_method('close', IOError)
2519
2520 def test_with_valueerror_in_acquire(self):
2521 self._test_with_failure_in_method('acquire', ValueError)
2522
2523 def test_with_valueerror_in_flush(self):
2524 self._test_with_failure_in_method('flush', ValueError)
2525
2526 def test_with_valueerror_in_close(self):
2527 self._test_with_failure_in_method('close', ValueError)
2528
2529 def test_with_other_error_in_acquire_without_raise(self):
2530 logging.raiseExceptions = False
2531 self._test_with_failure_in_method('acquire', IndexError)
2532
2533 def test_with_other_error_in_flush_without_raise(self):
2534 logging.raiseExceptions = False
2535 self._test_with_failure_in_method('flush', IndexError)
2536
2537 def test_with_other_error_in_close_without_raise(self):
2538 logging.raiseExceptions = False
2539 self._test_with_failure_in_method('close', IndexError)
2540
2541 def test_with_other_error_in_acquire_with_raise(self):
2542 logging.raiseExceptions = True
2543 self.assertRaises(IndexError, self._test_with_failure_in_method,
2544 'acquire', IndexError)
2545
2546 def test_with_other_error_in_flush_with_raise(self):
2547 logging.raiseExceptions = True
2548 self.assertRaises(IndexError, self._test_with_failure_in_method,
2549 'flush', IndexError)
2550
2551 def test_with_other_error_in_close_with_raise(self):
2552 logging.raiseExceptions = True
2553 self.assertRaises(IndexError, self._test_with_failure_in_method,
2554 'close', IndexError)
2555
2556
2557class ModuleLevelMiscTest(BaseTest):
2558
Vinay Sajip5e66b162011-04-20 15:41:14 +01002559 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002560
2561 def test_disable(self):
2562 old_disable = logging.root.manager.disable
2563 # confirm our assumptions are correct
2564 assert old_disable == 0
2565 self.addCleanup(lambda: logging.disable(old_disable))
2566
2567 logging.disable(83)
2568 self.assertEqual(logging.root.manager.disable, 83)
2569
2570 def _test_log(self, method, level=None):
2571 called = []
2572 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002573 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002574
2575 recording = RecordingHandler()
2576 logging.root.addHandler(recording)
2577
2578 log_method = getattr(logging, method)
2579 if level is not None:
2580 log_method(level, "test me: %r", recording)
2581 else:
2582 log_method("test me: %r", recording)
2583
2584 self.assertEqual(len(recording.records), 1)
2585 record = recording.records[0]
2586 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2587
2588 expected_level = level if level is not None else getattr(logging, method.upper())
2589 self.assertEqual(record.levelno, expected_level)
2590
2591 # basicConfig was not called!
2592 self.assertEqual(called, [])
2593
2594 def test_log(self):
2595 self._test_log('log', logging.ERROR)
2596
2597 def test_debug(self):
2598 self._test_log('debug')
2599
2600 def test_info(self):
2601 self._test_log('info')
2602
2603 def test_warning(self):
2604 self._test_log('warning')
2605
2606 def test_error(self):
2607 self._test_log('error')
2608
2609 def test_critical(self):
2610 self._test_log('critical')
2611
2612 def test_set_logger_class(self):
2613 self.assertRaises(TypeError, logging.setLoggerClass, object)
2614
2615 class MyLogger(logging.Logger):
2616 pass
2617
2618 logging.setLoggerClass(MyLogger)
2619 self.assertEqual(logging.getLoggerClass(), MyLogger)
2620
2621 logging.setLoggerClass(logging.Logger)
2622 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2623
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002624class LogRecordTest(BaseTest):
2625 def test_str_rep(self):
2626 r = logging.makeLogRecord({})
2627 s = str(r)
2628 self.assertTrue(s.startswith('<LogRecord: '))
2629 self.assertTrue(s.endswith('>'))
2630
2631 def test_dict_arg(self):
2632 h = RecordingHandler()
2633 r = logging.getLogger()
2634 r.addHandler(h)
2635 d = {'less' : 'more' }
2636 logging.warning('less is %(less)s', d)
2637 self.assertIs(h.records[0].args, d)
2638 self.assertEqual(h.records[0].message, 'less is more')
2639 r.removeHandler(h)
2640 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002641
Vinay Sajip7fe1d512011-04-28 12:04:58 +01002642 def test_multiprocessing(self):
2643 r = logging.makeLogRecord({})
2644 self.assertEqual(r.processName, 'MainProcess')
2645 import multiprocessing as mp
2646 r = logging.makeLogRecord({})
2647 self.assertEqual(r.processName, mp.current_process().name)
2648
2649 def test_optional(self):
2650 r = logging.makeLogRecord({})
2651 NOT_NONE = self.assertIsNotNone
2652 NOT_NONE(r.thread)
2653 NOT_NONE(r.threadName)
2654 NOT_NONE(r.process)
2655 NOT_NONE(r.processName)
2656 log_threads = logging.logThreads
2657 log_processes = logging.logProcesses
2658 log_multiprocessing = logging.logMultiprocessing
2659 try:
2660 logging.logThreads = False
2661 logging.logProcesses = False
2662 logging.logMultiprocessing = False
2663 r = logging.makeLogRecord({})
2664 NONE = self.assertIsNone
2665 NONE(r.thread)
2666 NONE(r.threadName)
2667 NONE(r.process)
2668 NONE(r.processName)
2669 finally:
2670 logging.logThreads = log_threads
2671 logging.logProcesses = log_processes
2672 logging.logMultiprocessing = log_multiprocessing
2673
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002674class BasicConfigTest(unittest.TestCase):
2675
Vinay Sajip95bf5042011-04-20 11:50:56 +01002676 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002677
2678 def setUp(self):
2679 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01002680 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01002681 self.saved_handlers = logging._handlers.copy()
2682 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002683 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002684 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002685 logging.root.handlers = []
2686
2687 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01002688 for h in logging.root.handlers[:]:
2689 logging.root.removeHandler(h)
2690 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002691 super(BasicConfigTest, self).tearDown()
2692
Vinay Sajip3def7e02011-04-20 10:58:06 +01002693 def cleanup(self):
2694 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01002695 logging._handlers.clear()
2696 logging._handlers.update(self.saved_handlers)
2697 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002698 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002699
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002700 def test_no_kwargs(self):
2701 logging.basicConfig()
2702
2703 # handler defaults to a StreamHandler to sys.stderr
2704 self.assertEqual(len(logging.root.handlers), 1)
2705 handler = logging.root.handlers[0]
2706 self.assertIsInstance(handler, logging.StreamHandler)
2707 self.assertEqual(handler.stream, sys.stderr)
2708
2709 formatter = handler.formatter
2710 # format defaults to logging.BASIC_FORMAT
2711 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2712 # datefmt defaults to None
2713 self.assertIsNone(formatter.datefmt)
2714 # style defaults to %
2715 self.assertIsInstance(formatter._style, logging.PercentStyle)
2716
2717 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01002718 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002719
2720 def test_filename(self):
2721 logging.basicConfig(filename='test.log')
2722
2723 self.assertEqual(len(logging.root.handlers), 1)
2724 handler = logging.root.handlers[0]
2725 self.assertIsInstance(handler, logging.FileHandler)
2726
2727 expected = logging.FileHandler('test.log', 'a')
2728 self.addCleanup(expected.close)
2729 self.assertEqual(handler.stream.mode, expected.stream.mode)
2730 self.assertEqual(handler.stream.name, expected.stream.name)
2731
2732 def test_filemode(self):
2733 logging.basicConfig(filename='test.log', filemode='wb')
2734
2735 handler = logging.root.handlers[0]
2736 expected = logging.FileHandler('test.log', 'wb')
2737 self.addCleanup(expected.close)
2738 self.assertEqual(handler.stream.mode, expected.stream.mode)
2739
2740 def test_stream(self):
2741 stream = io.StringIO()
2742 self.addCleanup(stream.close)
2743 logging.basicConfig(stream=stream)
2744
2745 self.assertEqual(len(logging.root.handlers), 1)
2746 handler = logging.root.handlers[0]
2747 self.assertIsInstance(handler, logging.StreamHandler)
2748 self.assertEqual(handler.stream, stream)
2749
2750 def test_format(self):
2751 logging.basicConfig(format='foo')
2752
2753 formatter = logging.root.handlers[0].formatter
2754 self.assertEqual(formatter._style._fmt, 'foo')
2755
2756 def test_datefmt(self):
2757 logging.basicConfig(datefmt='bar')
2758
2759 formatter = logging.root.handlers[0].formatter
2760 self.assertEqual(formatter.datefmt, 'bar')
2761
2762 def test_style(self):
2763 logging.basicConfig(style='$')
2764
2765 formatter = logging.root.handlers[0].formatter
2766 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2767
2768 def test_level(self):
2769 old_level = logging.root.level
2770 self.addCleanup(lambda: logging.root.setLevel(old_level))
2771
2772 logging.basicConfig(level=57)
2773 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002774 # Test that second call has no effect
2775 logging.basicConfig(level=58)
2776 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002777
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002778 def test_incompatible(self):
2779 assertRaises = self.assertRaises
2780 handlers = [logging.StreamHandler()]
2781 stream = sys.stderr
2782 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2783 stream=stream)
2784 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2785 handlers=handlers)
2786 assertRaises(ValueError, logging.basicConfig, stream=stream,
2787 handlers=handlers)
2788
2789 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002790 handlers = [
2791 logging.StreamHandler(),
2792 logging.StreamHandler(sys.stdout),
2793 logging.StreamHandler(),
2794 ]
2795 f = logging.Formatter()
2796 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002797 logging.basicConfig(handlers=handlers)
2798 self.assertIs(handlers[0], logging.root.handlers[0])
2799 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002800 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002801 self.assertIsNotNone(handlers[0].formatter)
2802 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002803 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002804 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2805
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002806 def _test_log(self, method, level=None):
2807 # logging.root has no handlers so basicConfig should be called
2808 called = []
2809
2810 old_basic_config = logging.basicConfig
2811 def my_basic_config(*a, **kw):
2812 old_basic_config()
2813 old_level = logging.root.level
2814 logging.root.setLevel(100) # avoid having messages in stderr
2815 self.addCleanup(lambda: logging.root.setLevel(old_level))
2816 called.append((a, kw))
2817
2818 patch(self, logging, 'basicConfig', my_basic_config)
2819
2820 log_method = getattr(logging, method)
2821 if level is not None:
2822 log_method(level, "test me")
2823 else:
2824 log_method("test me")
2825
2826 # basicConfig was called with no arguments
2827 self.assertEqual(called, [((), {})])
2828
2829 def test_log(self):
2830 self._test_log('log', logging.WARNING)
2831
2832 def test_debug(self):
2833 self._test_log('debug')
2834
2835 def test_info(self):
2836 self._test_log('info')
2837
2838 def test_warning(self):
2839 self._test_log('warning')
2840
2841 def test_error(self):
2842 self._test_log('error')
2843
2844 def test_critical(self):
2845 self._test_log('critical')
2846
2847
2848class LoggerAdapterTest(unittest.TestCase):
2849
2850 def setUp(self):
2851 super(LoggerAdapterTest, self).setUp()
2852 old_handler_list = logging._handlerList[:]
2853
2854 self.recording = RecordingHandler()
2855 self.logger = logging.root
2856 self.logger.addHandler(self.recording)
2857 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2858 self.addCleanup(self.recording.close)
2859
2860 def cleanup():
2861 logging._handlerList[:] = old_handler_list
2862
2863 self.addCleanup(cleanup)
2864 self.addCleanup(logging.shutdown)
2865 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2866
2867 def test_exception(self):
2868 msg = 'testing exception: %r'
2869 exc = None
2870 try:
2871 assert False
2872 except AssertionError as e:
2873 exc = e
2874 self.adapter.exception(msg, self.recording)
2875
2876 self.assertEqual(len(self.recording.records), 1)
2877 record = self.recording.records[0]
2878 self.assertEqual(record.levelno, logging.ERROR)
2879 self.assertEqual(record.msg, msg)
2880 self.assertEqual(record.args, (self.recording,))
2881 self.assertEqual(record.exc_info,
2882 (exc.__class__, exc, exc.__traceback__))
2883
2884 def test_critical(self):
2885 msg = 'critical test! %r'
2886 self.adapter.critical(msg, self.recording)
2887
2888 self.assertEqual(len(self.recording.records), 1)
2889 record = self.recording.records[0]
2890 self.assertEqual(record.levelno, logging.CRITICAL)
2891 self.assertEqual(record.msg, msg)
2892 self.assertEqual(record.args, (self.recording,))
2893
2894 def test_is_enabled_for(self):
2895 old_disable = self.adapter.logger.manager.disable
2896 self.adapter.logger.manager.disable = 33
2897 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2898 'disable', old_disable))
2899 self.assertFalse(self.adapter.isEnabledFor(32))
2900
2901 def test_has_handlers(self):
2902 self.assertTrue(self.adapter.hasHandlers())
2903
2904 for handler in self.logger.handlers:
2905 self.logger.removeHandler(handler)
2906 assert not self.logger.hasHandlers()
2907
2908 self.assertFalse(self.adapter.hasHandlers())
2909
2910
2911class LoggerTest(BaseTest):
2912
2913 def setUp(self):
2914 super(LoggerTest, self).setUp()
2915 self.recording = RecordingHandler()
2916 self.logger = logging.Logger(name='blah')
2917 self.logger.addHandler(self.recording)
2918 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2919 self.addCleanup(self.recording.close)
2920 self.addCleanup(logging.shutdown)
2921
2922 def test_set_invalid_level(self):
2923 self.assertRaises(TypeError, self.logger.setLevel, object())
2924
2925 def test_exception(self):
2926 msg = 'testing exception: %r'
2927 exc = None
2928 try:
2929 assert False
2930 except AssertionError as e:
2931 exc = e
2932 self.logger.exception(msg, self.recording)
2933
2934 self.assertEqual(len(self.recording.records), 1)
2935 record = self.recording.records[0]
2936 self.assertEqual(record.levelno, logging.ERROR)
2937 self.assertEqual(record.msg, msg)
2938 self.assertEqual(record.args, (self.recording,))
2939 self.assertEqual(record.exc_info,
2940 (exc.__class__, exc, exc.__traceback__))
2941
2942 def test_log_invalid_level_with_raise(self):
2943 old_raise = logging.raiseExceptions
2944 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2945
2946 logging.raiseExceptions = True
2947 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2948
2949 def test_log_invalid_level_no_raise(self):
2950 old_raise = logging.raiseExceptions
2951 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2952
2953 logging.raiseExceptions = False
2954 self.logger.log('10', 'test message') # no exception happens
2955
2956 def test_find_caller_with_stack_info(self):
2957 called = []
2958 patch(self, logging.traceback, 'print_stack',
2959 lambda f, file: called.append(file.getvalue()))
2960
2961 self.logger.findCaller(stack_info=True)
2962
2963 self.assertEqual(len(called), 1)
2964 self.assertEqual('Stack (most recent call last):\n', called[0])
2965
2966 def test_make_record_with_extra_overwrite(self):
2967 name = 'my record'
2968 level = 13
2969 fn = lno = msg = args = exc_info = func = sinfo = None
2970 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2971 exc_info, func, sinfo)
2972
2973 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2974 extra = {key: 'some value'}
2975 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2976 fn, lno, msg, args, exc_info,
2977 extra=extra, sinfo=sinfo)
2978
2979 def test_make_record_with_extra_no_overwrite(self):
2980 name = 'my record'
2981 level = 13
2982 fn = lno = msg = args = exc_info = func = sinfo = None
2983 extra = {'valid_key': 'some value'}
2984 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2985 exc_info, extra=extra, sinfo=sinfo)
2986 self.assertIn('valid_key', result.__dict__)
2987
2988 def test_has_handlers(self):
2989 self.assertTrue(self.logger.hasHandlers())
2990
2991 for handler in self.logger.handlers:
2992 self.logger.removeHandler(handler)
2993 assert not self.logger.hasHandlers()
2994
2995 self.assertFalse(self.logger.hasHandlers())
2996
2997 def test_has_handlers_no_propagate(self):
2998 child_logger = logging.getLogger('blah.child')
2999 child_logger.propagate = False
3000 assert child_logger.handlers == []
3001
3002 self.assertFalse(child_logger.hasHandlers())
3003
3004 def test_is_enabled_for(self):
3005 old_disable = self.logger.manager.disable
3006 self.logger.manager.disable = 23
3007 self.addCleanup(lambda: setattr(self.logger.manager,
3008 'disable', old_disable))
3009 self.assertFalse(self.logger.isEnabledFor(22))
3010
3011
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003012class BaseFileTest(BaseTest):
3013 "Base class for handler tests that write log files"
3014
3015 def setUp(self):
3016 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003017 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3018 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003019 self.rmfiles = []
3020
3021 def tearDown(self):
3022 for fn in self.rmfiles:
3023 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003024 if os.path.exists(self.fn):
3025 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003026 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003027
3028 def assertLogFile(self, filename):
3029 "Assert a log file is there and register it for deletion"
3030 self.assertTrue(os.path.exists(filename),
3031 msg="Log file %r does not exist")
3032 self.rmfiles.append(filename)
3033
3034
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003035class FileHandlerTest(BaseFileTest):
3036 def test_delay(self):
3037 os.unlink(self.fn)
3038 fh = logging.FileHandler(self.fn, delay=True)
3039 self.assertIsNone(fh.stream)
3040 self.assertFalse(os.path.exists(self.fn))
3041 fh.handle(logging.makeLogRecord({}))
3042 self.assertIsNotNone(fh.stream)
3043 self.assertTrue(os.path.exists(self.fn))
3044 fh.close()
3045
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003046class RotatingFileHandlerTest(BaseFileTest):
3047 def next_rec(self):
3048 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3049 self.next_message(), None, None, None)
3050
3051 def test_should_not_rollover(self):
3052 # If maxbytes is zero rollover never occurs
3053 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3054 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003055 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003056
3057 def test_should_rollover(self):
3058 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3059 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003060 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003061
3062 def test_file_created(self):
3063 # checks that the file is created and assumes it was created
3064 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003065 rh = logging.handlers.RotatingFileHandler(self.fn)
3066 rh.emit(self.next_rec())
3067 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003068 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003069
3070 def test_rollover_filenames(self):
3071 rh = logging.handlers.RotatingFileHandler(
3072 self.fn, backupCount=2, maxBytes=1)
3073 rh.emit(self.next_rec())
3074 self.assertLogFile(self.fn)
3075 rh.emit(self.next_rec())
3076 self.assertLogFile(self.fn + ".1")
3077 rh.emit(self.next_rec())
3078 self.assertLogFile(self.fn + ".2")
3079 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003080 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003081
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003082class TimedRotatingFileHandlerTest(BaseFileTest):
3083 # test methods added below
3084 pass
3085
3086def secs(**kw):
3087 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3088
3089for when, exp in (('S', 1),
3090 ('M', 60),
3091 ('H', 60 * 60),
3092 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003093 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003094 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003095 ('W0', secs(days=4, hours=24)),
3096 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003097 def test_compute_rollover(self, when=when, exp=exp):
3098 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003099 self.fn, when=when, interval=1, backupCount=0, utc=True)
3100 currentTime = 0.0
3101 actual = rh.computeRollover(currentTime)
3102 if exp != actual:
3103 # Failures occur on some systems for MIDNIGHT and W0.
3104 # Print detailed calculation for MIDNIGHT so we can try to see
3105 # what's going on
3106 import time
3107 if when == 'MIDNIGHT':
3108 try:
3109 if rh.utc:
3110 t = time.gmtime(currentTime)
3111 else:
3112 t = time.localtime(currentTime)
3113 currentHour = t[3]
3114 currentMinute = t[4]
3115 currentSecond = t[5]
3116 # r is the number of seconds left between now and midnight
3117 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3118 currentMinute) * 60 +
3119 currentSecond)
3120 result = currentTime + r
3121 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3122 print('currentHour: %s' % currentHour, file=sys.stderr)
3123 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3124 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3125 print('r: %s' % r, file=sys.stderr)
3126 print('result: %s' % result, file=sys.stderr)
3127 except Exception:
3128 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3129 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003130 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003131 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3132
Christian Heimes180510d2008-03-03 19:15:45 +00003133# Set the locale to the platform-dependent default. I have no idea
3134# why the test does this, but in any case we save the current locale
3135# first and restore it at the end.
3136@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003137def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003138 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003139 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003140 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003141 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip985ef872011-04-26 19:34:04 +01003142 FormatterTest, BufferingFormatterTest, StreamHandlerTest,
Vinay Sajip61561522010-12-03 11:50:38 +00003143 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003144 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
3145 LoggerAdapterTest, LoggerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003146 FileHandlerTest, RotatingFileHandlerTest,
3147 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003148 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003149 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003150
Christian Heimes180510d2008-03-03 19:15:45 +00003151if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003152 test_main()