blob: 54ed734c83e57e014b174f966a44845a43d8b072 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajiped0473c2011-02-26 15:35:38 +00003# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajiped0473c2011-02-26 15:35:38 +000021Copyright (C) 2001-2011 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Vinay Sajipa463d252011-04-30 21:52:48 +010028import asynchat
29import asyncore
Benjamin Petersonf91df042009-02-13 02:50:59 +000030import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000031import datetime
Vinay Sajipa463d252011-04-30 21:52:48 +010032import errno
Christian Heimes180510d2008-03-03 19:15:45 +000033import pickle
34import io
35import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000036import json
Christian Heimes180510d2008-03-03 19:15:45 +000037import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000038import queue
Christian Heimes180510d2008-03-03 19:15:45 +000039import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000040import select
Vinay Sajipa463d252011-04-30 21:52:48 +010041import smtpd
Christian Heimes180510d2008-03-03 19:15:45 +000042import socket
Alexandre Vassalottice261952008-05-12 02:31:37 +000043from socketserver import ThreadingTCPServer, StreamRequestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000044import struct
45import sys
46import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010047from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010048from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000049import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010050import time
Christian Heimes180510d2008-03-03 19:15:45 +000051import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000052import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000053import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000054try:
55 import threading
56except ImportError:
57 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000058
59
Christian Heimes180510d2008-03-03 19:15:45 +000060class BaseTest(unittest.TestCase):
61
62 """Base class for logging tests."""
63
64 log_format = "%(name)s -> %(levelname)s: %(message)s"
65 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
66 message_num = 0
67
68 def setUp(self):
69 """Setup the default logging stream to an internal StringIO instance,
70 so that we can examine log output as we want."""
71 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000072 logging._acquireLock()
73 try:
Christian Heimes180510d2008-03-03 19:15:45 +000074 self.saved_handlers = logging._handlers.copy()
75 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000076 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000077 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000078 self.logger_states = logger_states = {}
79 for name in saved_loggers:
80 logger_states[name] = getattr(saved_loggers[name],
81 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000082 finally:
83 logging._releaseLock()
84
Benjamin Peterson22005fc2010-04-11 16:25:06 +000085 # Set two unused loggers: one non-ASCII and one Unicode.
86 # This is to test correct operation when sorting existing
87 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000088 self.logger1 = logging.getLogger("\xab\xd7\xbb")
89 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000090
Christian Heimes180510d2008-03-03 19:15:45 +000091 self.root_logger = logging.getLogger("")
92 self.original_logging_level = self.root_logger.getEffectiveLevel()
93
94 self.stream = io.StringIO()
95 self.root_logger.setLevel(logging.DEBUG)
96 self.root_hdlr = logging.StreamHandler(self.stream)
97 self.root_formatter = logging.Formatter(self.log_format)
98 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000099 if self.logger1.hasHandlers():
100 hlist = self.logger1.handlers + self.root_logger.handlers
101 raise AssertionError('Unexpected handlers: %s' % hlist)
102 if self.logger2.hasHandlers():
103 hlist = self.logger2.handlers + self.root_logger.handlers
104 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000105 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000106 self.assertTrue(self.logger1.hasHandlers())
107 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000108
109 def tearDown(self):
110 """Remove our logging stream, and restore the original logging
111 level."""
112 self.stream.close()
113 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000114 while self.root_logger.handlers:
115 h = self.root_logger.handlers[0]
116 self.root_logger.removeHandler(h)
117 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000118 self.root_logger.setLevel(self.original_logging_level)
119 logging._acquireLock()
120 try:
121 logging._levelNames.clear()
122 logging._levelNames.update(self.saved_level_names)
123 logging._handlers.clear()
124 logging._handlers.update(self.saved_handlers)
125 logging._handlerList[:] = self.saved_handler_list
126 loggerDict = logging.getLogger().manager.loggerDict
127 loggerDict.clear()
128 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000129 logger_states = self.logger_states
130 for name in self.logger_states:
131 if logger_states[name] is not None:
132 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000133 finally:
134 logging._releaseLock()
135
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000136 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000137 """Match the collected log lines against the regular expression
138 self.expected_log_pat, and compare the extracted group values to
139 the expected_values list of tuples."""
140 stream = stream or self.stream
141 pat = re.compile(self.expected_log_pat)
142 try:
143 stream.reset()
144 actual_lines = stream.readlines()
145 except AttributeError:
146 # StringIO.StringIO lacks a reset() method.
147 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000148 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000149 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000150 for actual, expected in zip(actual_lines, expected_values):
151 match = pat.search(actual)
152 if not match:
153 self.fail("Log line does not match expected pattern:\n" +
154 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000155 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000156 s = stream.read()
157 if s:
158 self.fail("Remaining output at end of log stream:\n" + s)
159
160 def next_message(self):
161 """Generate a message consisting solely of an auto-incrementing
162 integer."""
163 self.message_num += 1
164 return "%d" % self.message_num
165
166
167class BuiltinLevelsTest(BaseTest):
168 """Test builtin levels and their inheritance."""
169
170 def test_flat(self):
171 #Logging levels in a flat logger namespace.
172 m = self.next_message
173
174 ERR = logging.getLogger("ERR")
175 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000176 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000177 INF.setLevel(logging.INFO)
178 DEB = logging.getLogger("DEB")
179 DEB.setLevel(logging.DEBUG)
180
181 # These should log.
182 ERR.log(logging.CRITICAL, m())
183 ERR.error(m())
184
185 INF.log(logging.CRITICAL, m())
186 INF.error(m())
187 INF.warn(m())
188 INF.info(m())
189
190 DEB.log(logging.CRITICAL, m())
191 DEB.error(m())
192 DEB.warn (m())
193 DEB.info (m())
194 DEB.debug(m())
195
196 # These should not log.
197 ERR.warn(m())
198 ERR.info(m())
199 ERR.debug(m())
200
201 INF.debug(m())
202
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000203 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000204 ('ERR', 'CRITICAL', '1'),
205 ('ERR', 'ERROR', '2'),
206 ('INF', 'CRITICAL', '3'),
207 ('INF', 'ERROR', '4'),
208 ('INF', 'WARNING', '5'),
209 ('INF', 'INFO', '6'),
210 ('DEB', 'CRITICAL', '7'),
211 ('DEB', 'ERROR', '8'),
212 ('DEB', 'WARNING', '9'),
213 ('DEB', 'INFO', '10'),
214 ('DEB', 'DEBUG', '11'),
215 ])
216
217 def test_nested_explicit(self):
218 # Logging levels in a nested namespace, all explicitly set.
219 m = self.next_message
220
221 INF = logging.getLogger("INF")
222 INF.setLevel(logging.INFO)
223 INF_ERR = logging.getLogger("INF.ERR")
224 INF_ERR.setLevel(logging.ERROR)
225
226 # These should log.
227 INF_ERR.log(logging.CRITICAL, m())
228 INF_ERR.error(m())
229
230 # These should not log.
231 INF_ERR.warn(m())
232 INF_ERR.info(m())
233 INF_ERR.debug(m())
234
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000235 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000236 ('INF.ERR', 'CRITICAL', '1'),
237 ('INF.ERR', 'ERROR', '2'),
238 ])
239
240 def test_nested_inherited(self):
241 #Logging levels in a nested namespace, inherited from parent loggers.
242 m = self.next_message
243
244 INF = logging.getLogger("INF")
245 INF.setLevel(logging.INFO)
246 INF_ERR = logging.getLogger("INF.ERR")
247 INF_ERR.setLevel(logging.ERROR)
248 INF_UNDEF = logging.getLogger("INF.UNDEF")
249 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
250 UNDEF = logging.getLogger("UNDEF")
251
252 # These should log.
253 INF_UNDEF.log(logging.CRITICAL, m())
254 INF_UNDEF.error(m())
255 INF_UNDEF.warn(m())
256 INF_UNDEF.info(m())
257 INF_ERR_UNDEF.log(logging.CRITICAL, m())
258 INF_ERR_UNDEF.error(m())
259
260 # These should not log.
261 INF_UNDEF.debug(m())
262 INF_ERR_UNDEF.warn(m())
263 INF_ERR_UNDEF.info(m())
264 INF_ERR_UNDEF.debug(m())
265
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000266 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000267 ('INF.UNDEF', 'CRITICAL', '1'),
268 ('INF.UNDEF', 'ERROR', '2'),
269 ('INF.UNDEF', 'WARNING', '3'),
270 ('INF.UNDEF', 'INFO', '4'),
271 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
272 ('INF.ERR.UNDEF', 'ERROR', '6'),
273 ])
274
275 def test_nested_with_virtual_parent(self):
276 # Logging levels when some parent does not exist yet.
277 m = self.next_message
278
279 INF = logging.getLogger("INF")
280 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
281 CHILD = logging.getLogger("INF.BADPARENT")
282 INF.setLevel(logging.INFO)
283
284 # These should log.
285 GRANDCHILD.log(logging.FATAL, m())
286 GRANDCHILD.info(m())
287 CHILD.log(logging.FATAL, m())
288 CHILD.info(m())
289
290 # These should not log.
291 GRANDCHILD.debug(m())
292 CHILD.debug(m())
293
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000294 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000295 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
296 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
297 ('INF.BADPARENT', 'CRITICAL', '3'),
298 ('INF.BADPARENT', 'INFO', '4'),
299 ])
300
301
302class BasicFilterTest(BaseTest):
303
304 """Test the bundled Filter class."""
305
306 def test_filter(self):
307 # Only messages satisfying the specified criteria pass through the
308 # filter.
309 filter_ = logging.Filter("spam.eggs")
310 handler = self.root_logger.handlers[0]
311 try:
312 handler.addFilter(filter_)
313 spam = logging.getLogger("spam")
314 spam_eggs = logging.getLogger("spam.eggs")
315 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
316 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
317
318 spam.info(self.next_message())
319 spam_eggs.info(self.next_message()) # Good.
320 spam_eggs_fish.info(self.next_message()) # Good.
321 spam_bakedbeans.info(self.next_message())
322
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000323 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000324 ('spam.eggs', 'INFO', '2'),
325 ('spam.eggs.fish', 'INFO', '3'),
326 ])
327 finally:
328 handler.removeFilter(filter_)
329
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000330 def test_callable_filter(self):
331 # Only messages satisfying the specified criteria pass through the
332 # filter.
333
334 def filterfunc(record):
335 parts = record.name.split('.')
336 prefix = '.'.join(parts[:2])
337 return prefix == 'spam.eggs'
338
339 handler = self.root_logger.handlers[0]
340 try:
341 handler.addFilter(filterfunc)
342 spam = logging.getLogger("spam")
343 spam_eggs = logging.getLogger("spam.eggs")
344 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
345 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
346
347 spam.info(self.next_message())
348 spam_eggs.info(self.next_message()) # Good.
349 spam_eggs_fish.info(self.next_message()) # Good.
350 spam_bakedbeans.info(self.next_message())
351
352 self.assert_log_lines([
353 ('spam.eggs', 'INFO', '2'),
354 ('spam.eggs.fish', 'INFO', '3'),
355 ])
356 finally:
357 handler.removeFilter(filterfunc)
358
Vinay Sajip985ef872011-04-26 19:34:04 +0100359 def test_empty_filter(self):
360 f = logging.Filter()
361 r = logging.makeLogRecord({'name': 'spam.eggs'})
362 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000363
364#
365# First, we define our levels. There can be as many as you want - the only
366# limitations are that they should be integers, the lowest should be > 0 and
367# larger values mean less information being logged. If you need specific
368# level values which do not fit into these limitations, you can use a
369# mapping dictionary to convert between your application levels and the
370# logging system.
371#
372SILENT = 120
373TACITURN = 119
374TERSE = 118
375EFFUSIVE = 117
376SOCIABLE = 116
377VERBOSE = 115
378TALKATIVE = 114
379GARRULOUS = 113
380CHATTERBOX = 112
381BORING = 111
382
383LEVEL_RANGE = range(BORING, SILENT + 1)
384
385#
386# Next, we define names for our levels. You don't need to do this - in which
387# case the system will use "Level n" to denote the text for the level.
388#
389my_logging_levels = {
390 SILENT : 'Silent',
391 TACITURN : 'Taciturn',
392 TERSE : 'Terse',
393 EFFUSIVE : 'Effusive',
394 SOCIABLE : 'Sociable',
395 VERBOSE : 'Verbose',
396 TALKATIVE : 'Talkative',
397 GARRULOUS : 'Garrulous',
398 CHATTERBOX : 'Chatterbox',
399 BORING : 'Boring',
400}
401
402class GarrulousFilter(logging.Filter):
403
404 """A filter which blocks garrulous messages."""
405
406 def filter(self, record):
407 return record.levelno != GARRULOUS
408
409class VerySpecificFilter(logging.Filter):
410
411 """A filter which blocks sociable and taciturn messages."""
412
413 def filter(self, record):
414 return record.levelno not in [SOCIABLE, TACITURN]
415
416
417class CustomLevelsAndFiltersTest(BaseTest):
418
419 """Test various filtering possibilities with custom logging levels."""
420
421 # Skip the logger name group.
422 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
423
424 def setUp(self):
425 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000426 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000427 logging.addLevelName(k, v)
428
429 def log_at_all_levels(self, logger):
430 for lvl in LEVEL_RANGE:
431 logger.log(lvl, self.next_message())
432
433 def test_logger_filter(self):
434 # Filter at logger level.
435 self.root_logger.setLevel(VERBOSE)
436 # Levels >= 'Verbose' are good.
437 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000438 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000439 ('Verbose', '5'),
440 ('Sociable', '6'),
441 ('Effusive', '7'),
442 ('Terse', '8'),
443 ('Taciturn', '9'),
444 ('Silent', '10'),
445 ])
446
447 def test_handler_filter(self):
448 # Filter at handler level.
449 self.root_logger.handlers[0].setLevel(SOCIABLE)
450 try:
451 # Levels >= 'Sociable' are good.
452 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000453 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000454 ('Sociable', '6'),
455 ('Effusive', '7'),
456 ('Terse', '8'),
457 ('Taciturn', '9'),
458 ('Silent', '10'),
459 ])
460 finally:
461 self.root_logger.handlers[0].setLevel(logging.NOTSET)
462
463 def test_specific_filters(self):
464 # Set a specific filter object on the handler, and then add another
465 # filter object on the logger itself.
466 handler = self.root_logger.handlers[0]
467 specific_filter = None
468 garr = GarrulousFilter()
469 handler.addFilter(garr)
470 try:
471 self.log_at_all_levels(self.root_logger)
472 first_lines = [
473 # Notice how 'Garrulous' is missing
474 ('Boring', '1'),
475 ('Chatterbox', '2'),
476 ('Talkative', '4'),
477 ('Verbose', '5'),
478 ('Sociable', '6'),
479 ('Effusive', '7'),
480 ('Terse', '8'),
481 ('Taciturn', '9'),
482 ('Silent', '10'),
483 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000484 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000485
486 specific_filter = VerySpecificFilter()
487 self.root_logger.addFilter(specific_filter)
488 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000489 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000490 # Not only 'Garrulous' is still missing, but also 'Sociable'
491 # and 'Taciturn'
492 ('Boring', '11'),
493 ('Chatterbox', '12'),
494 ('Talkative', '14'),
495 ('Verbose', '15'),
496 ('Effusive', '17'),
497 ('Terse', '18'),
498 ('Silent', '20'),
499 ])
500 finally:
501 if specific_filter:
502 self.root_logger.removeFilter(specific_filter)
503 handler.removeFilter(garr)
504
505
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100506class HandlerTest(BaseTest):
507 def test_name(self):
508 h = logging.Handler()
509 h.name = 'generic'
510 self.assertEqual(h.name, 'generic')
511 h.name = 'anothergeneric'
512 self.assertEqual(h.name, 'anothergeneric')
513 self.assertRaises(NotImplementedError, h.emit, None)
514
Vinay Sajip5a35b062011-04-27 11:31:14 +0100515 def test_builtin_handlers(self):
516 # We can't actually *use* too many handlers in the tests,
517 # but we can try instantiating them with various options
518 if sys.platform in ('linux2', 'darwin'):
519 for existing in (True, False):
520 fd, fn = tempfile.mkstemp()
521 os.close(fd)
522 if not existing:
523 os.unlink(fn)
524 h = logging.handlers.WatchedFileHandler(fn, delay=True)
525 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100526 dev, ino = h.dev, h.ino
527 self.assertNotEqual(dev, -1)
528 self.assertNotEqual(ino, -1)
529 r = logging.makeLogRecord({'msg': 'Test'})
530 h.handle(r)
531 # Now remove the file.
532 os.unlink(fn)
533 self.assertFalse(os.path.exists(fn))
534 # The next call should recreate the file.
535 h.handle(r)
536 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100537 else:
538 self.assertEqual(h.dev, -1)
539 self.assertEqual(h.ino, -1)
540 h.close()
541 if existing:
542 os.unlink(fn)
543 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100544 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100545 else:
546 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100547 try:
548 h = logging.handlers.SysLogHandler(sockname)
549 self.assertEqual(h.facility, h.LOG_USER)
550 self.assertTrue(h.unixsocket)
551 h.close()
552 except socket.error: # syslogd might not be available
553 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100554 for method in ('GET', 'POST', 'PUT'):
555 if method == 'PUT':
556 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
557 'localhost', '/log', method)
558 else:
559 h = logging.handlers.HTTPHandler('localhost', '/log', method)
560 h.close()
561 h = logging.handlers.BufferingHandler(0)
562 r = logging.makeLogRecord({})
563 self.assertTrue(h.shouldFlush(r))
564 h.close()
565 h = logging.handlers.BufferingHandler(1)
566 self.assertFalse(h.shouldFlush(r))
567 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100568
569class BadStream(object):
570 def write(self, data):
571 raise RuntimeError('deliberate mistake')
572
573class TestStreamHandler(logging.StreamHandler):
574 def handleError(self, record):
575 self.error_record = record
576
577class StreamHandlerTest(BaseTest):
578 def test_error_handling(self):
579 h = TestStreamHandler(BadStream())
580 r = logging.makeLogRecord({})
581 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100582 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100583 try:
584 h.handle(r)
585 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100586 h = logging.StreamHandler(BadStream())
587 sys.stderr = sio = io.StringIO()
588 h.handle(r)
589 self.assertTrue('\nRuntimeError: '
590 'deliberate mistake\n' in sio.getvalue())
591 logging.raiseExceptions = False
592 sys.stderr = sio = io.StringIO()
593 h.handle(r)
594 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100595 finally:
596 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100597 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100598
Vinay Sajipa463d252011-04-30 21:52:48 +0100599
600TEST_SMTP_PORT = 9025
601
602
603class TestSMTPChannel(smtpd.SMTPChannel):
604 def __init__(self, server, conn, addr, sockmap):
605 asynchat.async_chat.__init__(self, conn, sockmap)
606 self.smtp_server = server
607 self.conn = conn
608 self.addr = addr
609 self.received_lines = []
610 self.smtp_state = self.COMMAND
611 self.seen_greeting = ''
612 self.mailfrom = None
613 self.rcpttos = []
614 self.received_data = ''
615 self.fqdn = socket.getfqdn()
616 self.num_bytes = 0
617 try:
618 self.peer = conn.getpeername()
619 except socket.error as err:
620 # a race condition may occur if the other end is closing
621 # before we can get the peername
622 self.close()
623 if err.args[0] != errno.ENOTCONN:
624 raise
625 return
626 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
627 self.set_terminator(b'\r\n')
628
629
630class TestSMTPServer(smtpd.SMTPServer):
631 channel_class = TestSMTPChannel
632
633 def __init__(self, addr, handler, poll_interval, sockmap):
634 self._localaddr = addr
635 self._remoteaddr = None
636 self.sockmap = sockmap
637 asyncore.dispatcher.__init__(self, map=sockmap)
638 try:
639 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
640 sock.setblocking(0)
641 self.set_socket(sock, map=sockmap)
642 # try to re-use a server port if possible
643 self.set_reuse_addr()
644 self.bind(addr)
645 self.listen(5)
646 except:
647 self.close()
648 raise
649 self._handler = handler
650 self._thread = None
651 self.poll_interval = poll_interval
652
653 def handle_accepted(self, conn, addr):
654 print('Incoming connection from %s' % repr(addr), file=smtpd.DEBUGSTREAM)
655 channel = self.channel_class(self, conn, addr, self.sockmap)
656
657 def process_message(self, peer, mailfrom, rcpttos, data):
658 self._handler(peer, mailfrom, rcpttos, data)
659
660 def start(self):
661 self._thread = t = threading.Thread(target=self.serve_forever,
662 args=(self.poll_interval,))
663 t.setDaemon(True)
664 t.start()
665
666 def serve_forever(self, poll_interval):
667 asyncore.loop(poll_interval, map=self.sockmap)
668
669 def stop(self):
670 self.close()
671 self._thread.join()
672 self._thread = None
673
674
675class SMTPHandlerTest(BaseTest):
676 def test_basic(self):
677 addr = ('localhost', TEST_SMTP_PORT)
678 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
679 self.assertEqual(h.toaddrs, ['you'])
680 self.messages = []
681 sockmap = {}
682 server = TestSMTPServer(addr, self.process_message, 0.001, sockmap)
683 server.start()
684 r = logging.makeLogRecord({'msg': 'Hello'})
685 h.handle(r)
686 server.stop()
687 self.assertEqual(len(self.messages), 1)
688 peer, mailfrom, rcpttos, data = self.messages[0]
689 self.assertEqual(mailfrom, 'me')
690 self.assertEqual(rcpttos, ['you'])
691 self.assertTrue('\nSubject: Log\n' in data)
692 self.assertTrue(data.endswith('\n\nHello'))
693 h.close()
694
695 def process_message(self, *args):
696 self.messages.append(args)
697
698
Christian Heimes180510d2008-03-03 19:15:45 +0000699class MemoryHandlerTest(BaseTest):
700
701 """Tests for the MemoryHandler."""
702
703 # Do not bother with a logger name group.
704 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
705
706 def setUp(self):
707 BaseTest.setUp(self)
708 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
709 self.root_hdlr)
710 self.mem_logger = logging.getLogger('mem')
711 self.mem_logger.propagate = 0
712 self.mem_logger.addHandler(self.mem_hdlr)
713
714 def tearDown(self):
715 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000716 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000717
718 def test_flush(self):
719 # The memory handler flushes to its target handler based on specific
720 # criteria (message count and message level).
721 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000722 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000723 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000724 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000725 # This will flush because the level is >= logging.WARNING
726 self.mem_logger.warn(self.next_message())
727 lines = [
728 ('DEBUG', '1'),
729 ('INFO', '2'),
730 ('WARNING', '3'),
731 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000732 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000733 for n in (4, 14):
734 for i in range(9):
735 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000736 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000737 # This will flush because it's the 10th message since the last
738 # flush.
739 self.mem_logger.debug(self.next_message())
740 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000741 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000742
743 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000744 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000745
746
747class ExceptionFormatter(logging.Formatter):
748 """A special exception formatter."""
749 def formatException(self, ei):
750 return "Got a [%s]" % ei[0].__name__
751
752
753class ConfigFileTest(BaseTest):
754
755 """Reading logging config from a .ini-style config file."""
756
757 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
758
759 # config0 is a standard configuration.
760 config0 = """
761 [loggers]
762 keys=root
763
764 [handlers]
765 keys=hand1
766
767 [formatters]
768 keys=form1
769
770 [logger_root]
771 level=WARNING
772 handlers=hand1
773
774 [handler_hand1]
775 class=StreamHandler
776 level=NOTSET
777 formatter=form1
778 args=(sys.stdout,)
779
780 [formatter_form1]
781 format=%(levelname)s ++ %(message)s
782 datefmt=
783 """
784
785 # config1 adds a little to the standard configuration.
786 config1 = """
787 [loggers]
788 keys=root,parser
789
790 [handlers]
791 keys=hand1
792
793 [formatters]
794 keys=form1
795
796 [logger_root]
797 level=WARNING
798 handlers=
799
800 [logger_parser]
801 level=DEBUG
802 handlers=hand1
803 propagate=1
804 qualname=compiler.parser
805
806 [handler_hand1]
807 class=StreamHandler
808 level=NOTSET
809 formatter=form1
810 args=(sys.stdout,)
811
812 [formatter_form1]
813 format=%(levelname)s ++ %(message)s
814 datefmt=
815 """
816
Vinay Sajip3f84b072011-03-07 17:49:33 +0000817 # config1a moves the handler to the root.
818 config1a = """
819 [loggers]
820 keys=root,parser
821
822 [handlers]
823 keys=hand1
824
825 [formatters]
826 keys=form1
827
828 [logger_root]
829 level=WARNING
830 handlers=hand1
831
832 [logger_parser]
833 level=DEBUG
834 handlers=
835 propagate=1
836 qualname=compiler.parser
837
838 [handler_hand1]
839 class=StreamHandler
840 level=NOTSET
841 formatter=form1
842 args=(sys.stdout,)
843
844 [formatter_form1]
845 format=%(levelname)s ++ %(message)s
846 datefmt=
847 """
848
Christian Heimes180510d2008-03-03 19:15:45 +0000849 # config2 has a subtle configuration error that should be reported
850 config2 = config1.replace("sys.stdout", "sys.stbout")
851
852 # config3 has a less subtle configuration error
853 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
854
855 # config4 specifies a custom formatter class to be loaded
856 config4 = """
857 [loggers]
858 keys=root
859
860 [handlers]
861 keys=hand1
862
863 [formatters]
864 keys=form1
865
866 [logger_root]
867 level=NOTSET
868 handlers=hand1
869
870 [handler_hand1]
871 class=StreamHandler
872 level=NOTSET
873 formatter=form1
874 args=(sys.stdout,)
875
876 [formatter_form1]
877 class=""" + __name__ + """.ExceptionFormatter
878 format=%(levelname)s:%(name)s:%(message)s
879 datefmt=
880 """
881
Georg Brandl3dbca812008-07-23 16:10:53 +0000882 # config5 specifies a custom handler class to be loaded
883 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
884
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000885 # config6 uses ', ' delimiters in the handlers and formatters sections
886 config6 = """
887 [loggers]
888 keys=root,parser
889
890 [handlers]
891 keys=hand1, hand2
892
893 [formatters]
894 keys=form1, form2
895
896 [logger_root]
897 level=WARNING
898 handlers=
899
900 [logger_parser]
901 level=DEBUG
902 handlers=hand1
903 propagate=1
904 qualname=compiler.parser
905
906 [handler_hand1]
907 class=StreamHandler
908 level=NOTSET
909 formatter=form1
910 args=(sys.stdout,)
911
912 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000913 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000914 level=NOTSET
915 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000916 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000917
918 [formatter_form1]
919 format=%(levelname)s ++ %(message)s
920 datefmt=
921
922 [formatter_form2]
923 format=%(message)s
924 datefmt=
925 """
926
Vinay Sajip3f84b072011-03-07 17:49:33 +0000927 # config7 adds a compiler logger.
928 config7 = """
929 [loggers]
930 keys=root,parser,compiler
931
932 [handlers]
933 keys=hand1
934
935 [formatters]
936 keys=form1
937
938 [logger_root]
939 level=WARNING
940 handlers=hand1
941
942 [logger_compiler]
943 level=DEBUG
944 handlers=
945 propagate=1
946 qualname=compiler
947
948 [logger_parser]
949 level=DEBUG
950 handlers=
951 propagate=1
952 qualname=compiler.parser
953
954 [handler_hand1]
955 class=StreamHandler
956 level=NOTSET
957 formatter=form1
958 args=(sys.stdout,)
959
960 [formatter_form1]
961 format=%(levelname)s ++ %(message)s
962 datefmt=
963 """
964
Christian Heimes180510d2008-03-03 19:15:45 +0000965 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000966 file = io.StringIO(textwrap.dedent(conf))
967 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000968
969 def test_config0_ok(self):
970 # A simple config file which overrides the default settings.
971 with captured_stdout() as output:
972 self.apply_config(self.config0)
973 logger = logging.getLogger()
974 # Won't output anything
975 logger.info(self.next_message())
976 # Outputs a message
977 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000978 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000979 ('ERROR', '2'),
980 ], stream=output)
981 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000982 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000983
Georg Brandl3dbca812008-07-23 16:10:53 +0000984 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000985 # A config file defining a sub-parser as well.
986 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000987 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000988 logger = logging.getLogger("compiler.parser")
989 # Both will output a message
990 logger.info(self.next_message())
991 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000992 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000993 ('INFO', '1'),
994 ('ERROR', '2'),
995 ], stream=output)
996 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000997 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000998
999 def test_config2_failure(self):
1000 # A simple config file which overrides the default settings.
1001 self.assertRaises(Exception, self.apply_config, self.config2)
1002
1003 def test_config3_failure(self):
1004 # A simple config file which overrides the default settings.
1005 self.assertRaises(Exception, self.apply_config, self.config3)
1006
1007 def test_config4_ok(self):
1008 # A config file specifying a custom formatter class.
1009 with captured_stdout() as output:
1010 self.apply_config(self.config4)
1011 logger = logging.getLogger()
1012 try:
1013 raise RuntimeError()
1014 except RuntimeError:
1015 logging.exception("just testing")
1016 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001017 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001018 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1019 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001020 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001021
Georg Brandl3dbca812008-07-23 16:10:53 +00001022 def test_config5_ok(self):
1023 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001024
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001025 def test_config6_ok(self):
1026 self.test_config1_ok(config=self.config6)
1027
Vinay Sajip3f84b072011-03-07 17:49:33 +00001028 def test_config7_ok(self):
1029 with captured_stdout() as output:
1030 self.apply_config(self.config1a)
1031 logger = logging.getLogger("compiler.parser")
1032 # See issue #11424. compiler-hyphenated sorts
1033 # between compiler and compiler.xyz and this
1034 # was preventing compiler.xyz from being included
1035 # in the child loggers of compiler because of an
1036 # overzealous loop termination condition.
1037 hyphenated = logging.getLogger('compiler-hyphenated')
1038 # All will output a message
1039 logger.info(self.next_message())
1040 logger.error(self.next_message())
1041 hyphenated.critical(self.next_message())
1042 self.assert_log_lines([
1043 ('INFO', '1'),
1044 ('ERROR', '2'),
1045 ('CRITICAL', '3'),
1046 ], stream=output)
1047 # Original logger output is empty.
1048 self.assert_log_lines([])
1049 with captured_stdout() as output:
1050 self.apply_config(self.config7)
1051 logger = logging.getLogger("compiler.parser")
1052 self.assertFalse(logger.disabled)
1053 # Both will output a message
1054 logger.info(self.next_message())
1055 logger.error(self.next_message())
1056 logger = logging.getLogger("compiler.lexer")
1057 # Both will output a message
1058 logger.info(self.next_message())
1059 logger.error(self.next_message())
1060 # Will not appear
1061 hyphenated.critical(self.next_message())
1062 self.assert_log_lines([
1063 ('INFO', '4'),
1064 ('ERROR', '5'),
1065 ('INFO', '6'),
1066 ('ERROR', '7'),
1067 ], stream=output)
1068 # Original logger output is empty.
1069 self.assert_log_lines([])
1070
Christian Heimes180510d2008-03-03 19:15:45 +00001071class LogRecordStreamHandler(StreamRequestHandler):
1072
1073 """Handler for a streaming logging request. It saves the log message in the
1074 TCP server's 'log_output' attribute."""
1075
1076 TCP_LOG_END = "!!!END!!!"
1077
1078 def handle(self):
1079 """Handle multiple requests - each expected to be of 4-byte length,
1080 followed by the LogRecord in pickle format. Logs the record
1081 according to whatever policy is configured locally."""
1082 while True:
1083 chunk = self.connection.recv(4)
1084 if len(chunk) < 4:
1085 break
1086 slen = struct.unpack(">L", chunk)[0]
1087 chunk = self.connection.recv(slen)
1088 while len(chunk) < slen:
1089 chunk = chunk + self.connection.recv(slen - len(chunk))
1090 obj = self.unpickle(chunk)
1091 record = logging.makeLogRecord(obj)
1092 self.handle_log_record(record)
1093
1094 def unpickle(self, data):
1095 return pickle.loads(data)
1096
1097 def handle_log_record(self, record):
1098 # If the end-of-messages sentinel is seen, tell the server to
1099 # terminate.
1100 if self.TCP_LOG_END in record.msg:
1101 self.server.abort = 1
1102 return
1103 self.server.log_output += record.msg + "\n"
1104
Guido van Rossum376e6362003-04-25 14:22:00 +00001105
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001106class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +00001107
1108 """A simple-minded TCP socket-based logging receiver suitable for test
1109 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001110
1111 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +00001112 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001113
1114 def __init__(self, host='localhost',
1115 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
1116 handler=LogRecordStreamHandler):
1117 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +00001118 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +00001119 self.timeout = 0.1
1120 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001121
1122 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +00001123 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +00001124 rd, wr, ex = select.select([self.socket.fileno()], [], [],
1125 self.timeout)
1126 if rd:
1127 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +00001128 # Notify the main thread that we're about to exit
1129 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +00001130 # close the listen socket
1131 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001132
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001133
Victor Stinner45df8202010-04-28 22:31:17 +00001134@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001135class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001136
Christian Heimes180510d2008-03-03 19:15:45 +00001137 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001138
Christian Heimes180510d2008-03-03 19:15:45 +00001139 def setUp(self):
1140 """Set up a TCP server to receive log messages, and a SocketHandler
1141 pointing to that server's address and port."""
1142 BaseTest.setUp(self)
1143 self.tcpserver = LogRecordSocketReceiver(port=0)
1144 self.port = self.tcpserver.socket.getsockname()[1]
1145 self.threads = [
1146 threading.Thread(target=self.tcpserver.serve_until_stopped)]
1147 for thread in self.threads:
1148 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001149
Christian Heimes180510d2008-03-03 19:15:45 +00001150 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
1151 self.sock_hdlr.setFormatter(self.root_formatter)
1152 self.root_logger.removeHandler(self.root_logger.handlers[0])
1153 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001154
Christian Heimes180510d2008-03-03 19:15:45 +00001155 def tearDown(self):
1156 """Shutdown the TCP server."""
1157 try:
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001158 if hasattr(self, 'tcpserver'):
1159 self.tcpserver.abort = True
1160 del self.tcpserver
Christian Heimes180510d2008-03-03 19:15:45 +00001161 self.root_logger.removeHandler(self.sock_hdlr)
1162 self.sock_hdlr.close()
1163 for thread in self.threads:
1164 thread.join(2.0)
1165 finally:
1166 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001167
Christian Heimes180510d2008-03-03 19:15:45 +00001168 def get_output(self):
1169 """Get the log output as received by the TCP server."""
1170 # Signal the TCP receiver and wait for it to terminate.
1171 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
1172 self.tcpserver.finished.wait(2.0)
1173 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001174
Christian Heimes180510d2008-03-03 19:15:45 +00001175 def test_output(self):
1176 # The log message sent to the SocketHandler is properly received.
1177 logger = logging.getLogger("tcp")
1178 logger.error("spam")
1179 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +00001180 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001181
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001182 def test_noserver(self):
1183 # Kill the server
1184 self.tcpserver.abort = True
1185 del self.tcpserver
1186 for thread in self.threads:
1187 thread.join(2.0)
1188 #The logging call should try to connect, which should fail
1189 try:
1190 raise RuntimeError('Deliberate mistake')
1191 except RuntimeError:
1192 self.root_logger.exception('Never sent')
1193 self.root_logger.error('Never sent, either')
1194 now = time.time()
1195 self.assertTrue(self.sock_hdlr.retryTime > now)
1196 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1197 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001198
Christian Heimes180510d2008-03-03 19:15:45 +00001199class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001200
Christian Heimes180510d2008-03-03 19:15:45 +00001201 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001202
Christian Heimes180510d2008-03-03 19:15:45 +00001203 def setUp(self):
1204 """Create a dict to remember potentially destroyed objects."""
1205 BaseTest.setUp(self)
1206 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001207
Christian Heimes180510d2008-03-03 19:15:45 +00001208 def _watch_for_survival(self, *args):
1209 """Watch the given objects for survival, by creating weakrefs to
1210 them."""
1211 for obj in args:
1212 key = id(obj), repr(obj)
1213 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001214
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001215 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001216 """Assert that all objects watched for survival have survived."""
1217 # Trigger cycle breaking.
1218 gc.collect()
1219 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001220 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001221 if ref() is None:
1222 dead.append(repr_)
1223 if dead:
1224 self.fail("%d objects should have survived "
1225 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001226
Christian Heimes180510d2008-03-03 19:15:45 +00001227 def test_persistent_loggers(self):
1228 # Logger objects are persistent and retain their configuration, even
1229 # if visible references are destroyed.
1230 self.root_logger.setLevel(logging.INFO)
1231 foo = logging.getLogger("foo")
1232 self._watch_for_survival(foo)
1233 foo.setLevel(logging.DEBUG)
1234 self.root_logger.debug(self.next_message())
1235 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001236 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001237 ('foo', 'DEBUG', '2'),
1238 ])
1239 del foo
1240 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001241 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001242 # foo has retained its settings.
1243 bar = logging.getLogger("foo")
1244 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001245 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001246 ('foo', 'DEBUG', '2'),
1247 ('foo', 'DEBUG', '3'),
1248 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001249
Benjamin Petersonf91df042009-02-13 02:50:59 +00001250
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001251class EncodingTest(BaseTest):
1252 def test_encoding_plain_file(self):
1253 # In Python 2.x, a plain file object is treated as having no encoding.
1254 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001255 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1256 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001257 # the non-ascii data we write to the log.
1258 data = "foo\x80"
1259 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001260 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001261 log.addHandler(handler)
1262 try:
1263 # write non-ascii data to the log.
1264 log.warning(data)
1265 finally:
1266 log.removeHandler(handler)
1267 handler.close()
1268 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001269 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001270 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001271 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001272 finally:
1273 f.close()
1274 finally:
1275 if os.path.isfile(fn):
1276 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001277
Benjamin Petersonf91df042009-02-13 02:50:59 +00001278 def test_encoding_cyrillic_unicode(self):
1279 log = logging.getLogger("test")
1280 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1281 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1282 #Ensure it's written in a Cyrillic encoding
1283 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001284 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001285 stream = io.BytesIO()
1286 writer = writer_class(stream, 'strict')
1287 handler = logging.StreamHandler(writer)
1288 log.addHandler(handler)
1289 try:
1290 log.warning(message)
1291 finally:
1292 log.removeHandler(handler)
1293 handler.close()
1294 # check we wrote exactly those bytes, ignoring trailing \n etc
1295 s = stream.getvalue()
1296 #Compare against what the data should be when encoded in CP-1251
1297 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1298
1299
Georg Brandlf9734072008-12-07 15:30:06 +00001300class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001301
Georg Brandlf9734072008-12-07 15:30:06 +00001302 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001303 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001304 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001305 self.addCleanup(lambda: logging.captureWarnings(False))
1306 warnings.filterwarnings("always", category=UserWarning)
1307 stream = io.StringIO()
1308 h = logging.StreamHandler(stream)
1309 logger = logging.getLogger("py.warnings")
1310 logger.addHandler(h)
1311 warnings.warn("I'm warning you...")
1312 logger.removeHandler(h)
1313 s = stream.getvalue()
1314 h.close()
1315 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001316
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001317 #See if an explicit file uses the original implementation
1318 a_file = io.StringIO()
1319 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1320 a_file, "Dummy line")
1321 s = a_file.getvalue()
1322 a_file.close()
1323 self.assertEqual(s,
1324 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1325
1326 def test_warnings_no_handlers(self):
1327 with warnings.catch_warnings():
1328 logging.captureWarnings(True)
1329 self.addCleanup(lambda: logging.captureWarnings(False))
1330
1331 # confirm our assumption: no loggers are set
1332 logger = logging.getLogger("py.warnings")
1333 assert logger.handlers == []
1334
1335 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1336 self.assertTrue(len(logger.handlers) == 1)
1337 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001338
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001339
1340def formatFunc(format, datefmt=None):
1341 return logging.Formatter(format, datefmt)
1342
1343def handlerFunc():
1344 return logging.StreamHandler()
1345
1346class CustomHandler(logging.StreamHandler):
1347 pass
1348
1349class ConfigDictTest(BaseTest):
1350
1351 """Reading logging config from a dictionary."""
1352
1353 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1354
1355 # config0 is a standard configuration.
1356 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001357 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001358 'formatters': {
1359 'form1' : {
1360 'format' : '%(levelname)s ++ %(message)s',
1361 },
1362 },
1363 'handlers' : {
1364 'hand1' : {
1365 'class' : 'logging.StreamHandler',
1366 'formatter' : 'form1',
1367 'level' : 'NOTSET',
1368 'stream' : 'ext://sys.stdout',
1369 },
1370 },
1371 'root' : {
1372 'level' : 'WARNING',
1373 'handlers' : ['hand1'],
1374 },
1375 }
1376
1377 # config1 adds a little to the standard configuration.
1378 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001379 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001380 'formatters': {
1381 'form1' : {
1382 'format' : '%(levelname)s ++ %(message)s',
1383 },
1384 },
1385 'handlers' : {
1386 'hand1' : {
1387 'class' : 'logging.StreamHandler',
1388 'formatter' : 'form1',
1389 'level' : 'NOTSET',
1390 'stream' : 'ext://sys.stdout',
1391 },
1392 },
1393 'loggers' : {
1394 'compiler.parser' : {
1395 'level' : 'DEBUG',
1396 'handlers' : ['hand1'],
1397 },
1398 },
1399 'root' : {
1400 'level' : 'WARNING',
1401 },
1402 }
1403
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001404 # config1a moves the handler to the root. Used with config8a
1405 config1a = {
1406 'version': 1,
1407 'formatters': {
1408 'form1' : {
1409 'format' : '%(levelname)s ++ %(message)s',
1410 },
1411 },
1412 'handlers' : {
1413 'hand1' : {
1414 'class' : 'logging.StreamHandler',
1415 'formatter' : 'form1',
1416 'level' : 'NOTSET',
1417 'stream' : 'ext://sys.stdout',
1418 },
1419 },
1420 'loggers' : {
1421 'compiler.parser' : {
1422 'level' : 'DEBUG',
1423 },
1424 },
1425 'root' : {
1426 'level' : 'WARNING',
1427 'handlers' : ['hand1'],
1428 },
1429 }
1430
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001431 # config2 has a subtle configuration error that should be reported
1432 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001433 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001434 'formatters': {
1435 'form1' : {
1436 'format' : '%(levelname)s ++ %(message)s',
1437 },
1438 },
1439 'handlers' : {
1440 'hand1' : {
1441 'class' : 'logging.StreamHandler',
1442 'formatter' : 'form1',
1443 'level' : 'NOTSET',
1444 'stream' : 'ext://sys.stdbout',
1445 },
1446 },
1447 'loggers' : {
1448 'compiler.parser' : {
1449 'level' : 'DEBUG',
1450 'handlers' : ['hand1'],
1451 },
1452 },
1453 'root' : {
1454 'level' : 'WARNING',
1455 },
1456 }
1457
1458 #As config1 but with a misspelt level on a handler
1459 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001460 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001461 'formatters': {
1462 'form1' : {
1463 'format' : '%(levelname)s ++ %(message)s',
1464 },
1465 },
1466 'handlers' : {
1467 'hand1' : {
1468 'class' : 'logging.StreamHandler',
1469 'formatter' : 'form1',
1470 'level' : 'NTOSET',
1471 'stream' : 'ext://sys.stdout',
1472 },
1473 },
1474 'loggers' : {
1475 'compiler.parser' : {
1476 'level' : 'DEBUG',
1477 'handlers' : ['hand1'],
1478 },
1479 },
1480 'root' : {
1481 'level' : 'WARNING',
1482 },
1483 }
1484
1485
1486 #As config1 but with a misspelt level on a logger
1487 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001488 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001489 'formatters': {
1490 'form1' : {
1491 'format' : '%(levelname)s ++ %(message)s',
1492 },
1493 },
1494 'handlers' : {
1495 'hand1' : {
1496 'class' : 'logging.StreamHandler',
1497 'formatter' : 'form1',
1498 'level' : 'NOTSET',
1499 'stream' : 'ext://sys.stdout',
1500 },
1501 },
1502 'loggers' : {
1503 'compiler.parser' : {
1504 'level' : 'DEBUG',
1505 'handlers' : ['hand1'],
1506 },
1507 },
1508 'root' : {
1509 'level' : 'WRANING',
1510 },
1511 }
1512
1513 # config3 has a less subtle configuration error
1514 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001515 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001516 'formatters': {
1517 'form1' : {
1518 'format' : '%(levelname)s ++ %(message)s',
1519 },
1520 },
1521 'handlers' : {
1522 'hand1' : {
1523 'class' : 'logging.StreamHandler',
1524 'formatter' : 'misspelled_name',
1525 'level' : 'NOTSET',
1526 'stream' : 'ext://sys.stdout',
1527 },
1528 },
1529 'loggers' : {
1530 'compiler.parser' : {
1531 'level' : 'DEBUG',
1532 'handlers' : ['hand1'],
1533 },
1534 },
1535 'root' : {
1536 'level' : 'WARNING',
1537 },
1538 }
1539
1540 # config4 specifies a custom formatter class to be loaded
1541 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001542 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001543 'formatters': {
1544 'form1' : {
1545 '()' : __name__ + '.ExceptionFormatter',
1546 'format' : '%(levelname)s:%(name)s:%(message)s',
1547 },
1548 },
1549 'handlers' : {
1550 'hand1' : {
1551 'class' : 'logging.StreamHandler',
1552 'formatter' : 'form1',
1553 'level' : 'NOTSET',
1554 'stream' : 'ext://sys.stdout',
1555 },
1556 },
1557 'root' : {
1558 'level' : 'NOTSET',
1559 'handlers' : ['hand1'],
1560 },
1561 }
1562
1563 # As config4 but using an actual callable rather than a string
1564 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001565 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001566 'formatters': {
1567 'form1' : {
1568 '()' : ExceptionFormatter,
1569 'format' : '%(levelname)s:%(name)s:%(message)s',
1570 },
1571 'form2' : {
1572 '()' : __name__ + '.formatFunc',
1573 'format' : '%(levelname)s:%(name)s:%(message)s',
1574 },
1575 'form3' : {
1576 '()' : formatFunc,
1577 'format' : '%(levelname)s:%(name)s:%(message)s',
1578 },
1579 },
1580 'handlers' : {
1581 'hand1' : {
1582 'class' : 'logging.StreamHandler',
1583 'formatter' : 'form1',
1584 'level' : 'NOTSET',
1585 'stream' : 'ext://sys.stdout',
1586 },
1587 'hand2' : {
1588 '()' : handlerFunc,
1589 },
1590 },
1591 'root' : {
1592 'level' : 'NOTSET',
1593 'handlers' : ['hand1'],
1594 },
1595 }
1596
1597 # config5 specifies a custom handler class to be loaded
1598 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001599 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001600 'formatters': {
1601 'form1' : {
1602 'format' : '%(levelname)s ++ %(message)s',
1603 },
1604 },
1605 'handlers' : {
1606 'hand1' : {
1607 'class' : __name__ + '.CustomHandler',
1608 'formatter' : 'form1',
1609 'level' : 'NOTSET',
1610 'stream' : 'ext://sys.stdout',
1611 },
1612 },
1613 'loggers' : {
1614 'compiler.parser' : {
1615 'level' : 'DEBUG',
1616 'handlers' : ['hand1'],
1617 },
1618 },
1619 'root' : {
1620 'level' : 'WARNING',
1621 },
1622 }
1623
1624 # config6 specifies a custom handler class to be loaded
1625 # but has bad arguments
1626 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001627 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001628 'formatters': {
1629 'form1' : {
1630 'format' : '%(levelname)s ++ %(message)s',
1631 },
1632 },
1633 'handlers' : {
1634 'hand1' : {
1635 'class' : __name__ + '.CustomHandler',
1636 'formatter' : 'form1',
1637 'level' : 'NOTSET',
1638 'stream' : 'ext://sys.stdout',
1639 '9' : 'invalid parameter name',
1640 },
1641 },
1642 'loggers' : {
1643 'compiler.parser' : {
1644 'level' : 'DEBUG',
1645 'handlers' : ['hand1'],
1646 },
1647 },
1648 'root' : {
1649 'level' : 'WARNING',
1650 },
1651 }
1652
1653 #config 7 does not define compiler.parser but defines compiler.lexer
1654 #so compiler.parser should be disabled after applying it
1655 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001656 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001657 'formatters': {
1658 'form1' : {
1659 'format' : '%(levelname)s ++ %(message)s',
1660 },
1661 },
1662 'handlers' : {
1663 'hand1' : {
1664 'class' : 'logging.StreamHandler',
1665 'formatter' : 'form1',
1666 'level' : 'NOTSET',
1667 'stream' : 'ext://sys.stdout',
1668 },
1669 },
1670 'loggers' : {
1671 'compiler.lexer' : {
1672 'level' : 'DEBUG',
1673 'handlers' : ['hand1'],
1674 },
1675 },
1676 'root' : {
1677 'level' : 'WARNING',
1678 },
1679 }
1680
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001681 # config8 defines both compiler and compiler.lexer
1682 # so compiler.parser should not be disabled (since
1683 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001684 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001685 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001686 'disable_existing_loggers' : False,
1687 'formatters': {
1688 'form1' : {
1689 'format' : '%(levelname)s ++ %(message)s',
1690 },
1691 },
1692 'handlers' : {
1693 'hand1' : {
1694 'class' : 'logging.StreamHandler',
1695 'formatter' : 'form1',
1696 'level' : 'NOTSET',
1697 'stream' : 'ext://sys.stdout',
1698 },
1699 },
1700 'loggers' : {
1701 'compiler' : {
1702 'level' : 'DEBUG',
1703 'handlers' : ['hand1'],
1704 },
1705 'compiler.lexer' : {
1706 },
1707 },
1708 'root' : {
1709 'level' : 'WARNING',
1710 },
1711 }
1712
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001713 # config8a disables existing loggers
1714 config8a = {
1715 'version': 1,
1716 'disable_existing_loggers' : True,
1717 'formatters': {
1718 'form1' : {
1719 'format' : '%(levelname)s ++ %(message)s',
1720 },
1721 },
1722 'handlers' : {
1723 'hand1' : {
1724 'class' : 'logging.StreamHandler',
1725 'formatter' : 'form1',
1726 'level' : 'NOTSET',
1727 'stream' : 'ext://sys.stdout',
1728 },
1729 },
1730 'loggers' : {
1731 'compiler' : {
1732 'level' : 'DEBUG',
1733 'handlers' : ['hand1'],
1734 },
1735 'compiler.lexer' : {
1736 },
1737 },
1738 'root' : {
1739 'level' : 'WARNING',
1740 },
1741 }
1742
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001743 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001744 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001745 'formatters': {
1746 'form1' : {
1747 'format' : '%(levelname)s ++ %(message)s',
1748 },
1749 },
1750 'handlers' : {
1751 'hand1' : {
1752 'class' : 'logging.StreamHandler',
1753 'formatter' : 'form1',
1754 'level' : 'WARNING',
1755 'stream' : 'ext://sys.stdout',
1756 },
1757 },
1758 'loggers' : {
1759 'compiler.parser' : {
1760 'level' : 'WARNING',
1761 'handlers' : ['hand1'],
1762 },
1763 },
1764 'root' : {
1765 'level' : 'NOTSET',
1766 },
1767 }
1768
1769 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001770 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001771 'incremental' : True,
1772 'handlers' : {
1773 'hand1' : {
1774 'level' : 'WARNING',
1775 },
1776 },
1777 'loggers' : {
1778 'compiler.parser' : {
1779 'level' : 'INFO',
1780 },
1781 },
1782 }
1783
1784 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001785 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001786 'incremental' : True,
1787 'handlers' : {
1788 'hand1' : {
1789 'level' : 'INFO',
1790 },
1791 },
1792 'loggers' : {
1793 'compiler.parser' : {
1794 'level' : 'INFO',
1795 },
1796 },
1797 }
1798
1799 #As config1 but with a filter added
1800 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001801 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001802 'formatters': {
1803 'form1' : {
1804 'format' : '%(levelname)s ++ %(message)s',
1805 },
1806 },
1807 'filters' : {
1808 'filt1' : {
1809 'name' : 'compiler.parser',
1810 },
1811 },
1812 'handlers' : {
1813 'hand1' : {
1814 'class' : 'logging.StreamHandler',
1815 'formatter' : 'form1',
1816 'level' : 'NOTSET',
1817 'stream' : 'ext://sys.stdout',
1818 'filters' : ['filt1'],
1819 },
1820 },
1821 'loggers' : {
1822 'compiler.parser' : {
1823 'level' : 'DEBUG',
1824 'filters' : ['filt1'],
1825 },
1826 },
1827 'root' : {
1828 'level' : 'WARNING',
1829 'handlers' : ['hand1'],
1830 },
1831 }
1832
1833 #As config1 but using cfg:// references
1834 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001835 'version': 1,
1836 'true_formatters': {
1837 'form1' : {
1838 'format' : '%(levelname)s ++ %(message)s',
1839 },
1840 },
1841 'handler_configs': {
1842 'hand1' : {
1843 'class' : 'logging.StreamHandler',
1844 'formatter' : 'form1',
1845 'level' : 'NOTSET',
1846 'stream' : 'ext://sys.stdout',
1847 },
1848 },
1849 'formatters' : 'cfg://true_formatters',
1850 'handlers' : {
1851 'hand1' : 'cfg://handler_configs[hand1]',
1852 },
1853 'loggers' : {
1854 'compiler.parser' : {
1855 'level' : 'DEBUG',
1856 'handlers' : ['hand1'],
1857 },
1858 },
1859 'root' : {
1860 'level' : 'WARNING',
1861 },
1862 }
1863
1864 #As config11 but missing the version key
1865 config12 = {
1866 'true_formatters': {
1867 'form1' : {
1868 'format' : '%(levelname)s ++ %(message)s',
1869 },
1870 },
1871 'handler_configs': {
1872 'hand1' : {
1873 'class' : 'logging.StreamHandler',
1874 'formatter' : 'form1',
1875 'level' : 'NOTSET',
1876 'stream' : 'ext://sys.stdout',
1877 },
1878 },
1879 'formatters' : 'cfg://true_formatters',
1880 'handlers' : {
1881 'hand1' : 'cfg://handler_configs[hand1]',
1882 },
1883 'loggers' : {
1884 'compiler.parser' : {
1885 'level' : 'DEBUG',
1886 'handlers' : ['hand1'],
1887 },
1888 },
1889 'root' : {
1890 'level' : 'WARNING',
1891 },
1892 }
1893
1894 #As config11 but using an unsupported version
1895 config13 = {
1896 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001897 'true_formatters': {
1898 'form1' : {
1899 'format' : '%(levelname)s ++ %(message)s',
1900 },
1901 },
1902 'handler_configs': {
1903 'hand1' : {
1904 'class' : 'logging.StreamHandler',
1905 'formatter' : 'form1',
1906 'level' : 'NOTSET',
1907 'stream' : 'ext://sys.stdout',
1908 },
1909 },
1910 'formatters' : 'cfg://true_formatters',
1911 'handlers' : {
1912 'hand1' : 'cfg://handler_configs[hand1]',
1913 },
1914 'loggers' : {
1915 'compiler.parser' : {
1916 'level' : 'DEBUG',
1917 'handlers' : ['hand1'],
1918 },
1919 },
1920 'root' : {
1921 'level' : 'WARNING',
1922 },
1923 }
1924
1925 def apply_config(self, conf):
1926 logging.config.dictConfig(conf)
1927
1928 def test_config0_ok(self):
1929 # A simple config which overrides the default settings.
1930 with captured_stdout() as output:
1931 self.apply_config(self.config0)
1932 logger = logging.getLogger()
1933 # Won't output anything
1934 logger.info(self.next_message())
1935 # Outputs a message
1936 logger.error(self.next_message())
1937 self.assert_log_lines([
1938 ('ERROR', '2'),
1939 ], stream=output)
1940 # Original logger output is empty.
1941 self.assert_log_lines([])
1942
1943 def test_config1_ok(self, config=config1):
1944 # A config defining a sub-parser as well.
1945 with captured_stdout() as output:
1946 self.apply_config(config)
1947 logger = logging.getLogger("compiler.parser")
1948 # Both will output a message
1949 logger.info(self.next_message())
1950 logger.error(self.next_message())
1951 self.assert_log_lines([
1952 ('INFO', '1'),
1953 ('ERROR', '2'),
1954 ], stream=output)
1955 # Original logger output is empty.
1956 self.assert_log_lines([])
1957
1958 def test_config2_failure(self):
1959 # A simple config which overrides the default settings.
1960 self.assertRaises(Exception, self.apply_config, self.config2)
1961
1962 def test_config2a_failure(self):
1963 # A simple config which overrides the default settings.
1964 self.assertRaises(Exception, self.apply_config, self.config2a)
1965
1966 def test_config2b_failure(self):
1967 # A simple config which overrides the default settings.
1968 self.assertRaises(Exception, self.apply_config, self.config2b)
1969
1970 def test_config3_failure(self):
1971 # A simple config which overrides the default settings.
1972 self.assertRaises(Exception, self.apply_config, self.config3)
1973
1974 def test_config4_ok(self):
1975 # A config specifying a custom formatter class.
1976 with captured_stdout() as output:
1977 self.apply_config(self.config4)
1978 #logger = logging.getLogger()
1979 try:
1980 raise RuntimeError()
1981 except RuntimeError:
1982 logging.exception("just testing")
1983 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001984 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001985 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1986 # Original logger output is empty
1987 self.assert_log_lines([])
1988
1989 def test_config4a_ok(self):
1990 # A config specifying a custom formatter class.
1991 with captured_stdout() as output:
1992 self.apply_config(self.config4a)
1993 #logger = logging.getLogger()
1994 try:
1995 raise RuntimeError()
1996 except RuntimeError:
1997 logging.exception("just testing")
1998 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001999 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002000 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2001 # Original logger output is empty
2002 self.assert_log_lines([])
2003
2004 def test_config5_ok(self):
2005 self.test_config1_ok(config=self.config5)
2006
2007 def test_config6_failure(self):
2008 self.assertRaises(Exception, self.apply_config, self.config6)
2009
2010 def test_config7_ok(self):
2011 with captured_stdout() as output:
2012 self.apply_config(self.config1)
2013 logger = logging.getLogger("compiler.parser")
2014 # Both will output a message
2015 logger.info(self.next_message())
2016 logger.error(self.next_message())
2017 self.assert_log_lines([
2018 ('INFO', '1'),
2019 ('ERROR', '2'),
2020 ], stream=output)
2021 # Original logger output is empty.
2022 self.assert_log_lines([])
2023 with captured_stdout() as output:
2024 self.apply_config(self.config7)
2025 logger = logging.getLogger("compiler.parser")
2026 self.assertTrue(logger.disabled)
2027 logger = logging.getLogger("compiler.lexer")
2028 # Both will output a message
2029 logger.info(self.next_message())
2030 logger.error(self.next_message())
2031 self.assert_log_lines([
2032 ('INFO', '3'),
2033 ('ERROR', '4'),
2034 ], stream=output)
2035 # Original logger output is empty.
2036 self.assert_log_lines([])
2037
2038 #Same as test_config_7_ok but don't disable old loggers.
2039 def test_config_8_ok(self):
2040 with captured_stdout() as output:
2041 self.apply_config(self.config1)
2042 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002043 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002044 logger.info(self.next_message())
2045 logger.error(self.next_message())
2046 self.assert_log_lines([
2047 ('INFO', '1'),
2048 ('ERROR', '2'),
2049 ], stream=output)
2050 # Original logger output is empty.
2051 self.assert_log_lines([])
2052 with captured_stdout() as output:
2053 self.apply_config(self.config8)
2054 logger = logging.getLogger("compiler.parser")
2055 self.assertFalse(logger.disabled)
2056 # Both will output a message
2057 logger.info(self.next_message())
2058 logger.error(self.next_message())
2059 logger = logging.getLogger("compiler.lexer")
2060 # Both will output a message
2061 logger.info(self.next_message())
2062 logger.error(self.next_message())
2063 self.assert_log_lines([
2064 ('INFO', '3'),
2065 ('ERROR', '4'),
2066 ('INFO', '5'),
2067 ('ERROR', '6'),
2068 ], stream=output)
2069 # Original logger output is empty.
2070 self.assert_log_lines([])
2071
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002072 def test_config_8a_ok(self):
2073 with captured_stdout() as output:
2074 self.apply_config(self.config1a)
2075 logger = logging.getLogger("compiler.parser")
2076 # See issue #11424. compiler-hyphenated sorts
2077 # between compiler and compiler.xyz and this
2078 # was preventing compiler.xyz from being included
2079 # in the child loggers of compiler because of an
2080 # overzealous loop termination condition.
2081 hyphenated = logging.getLogger('compiler-hyphenated')
2082 # All will output a message
2083 logger.info(self.next_message())
2084 logger.error(self.next_message())
2085 hyphenated.critical(self.next_message())
2086 self.assert_log_lines([
2087 ('INFO', '1'),
2088 ('ERROR', '2'),
2089 ('CRITICAL', '3'),
2090 ], stream=output)
2091 # Original logger output is empty.
2092 self.assert_log_lines([])
2093 with captured_stdout() as output:
2094 self.apply_config(self.config8a)
2095 logger = logging.getLogger("compiler.parser")
2096 self.assertFalse(logger.disabled)
2097 # Both will output a message
2098 logger.info(self.next_message())
2099 logger.error(self.next_message())
2100 logger = logging.getLogger("compiler.lexer")
2101 # Both will output a message
2102 logger.info(self.next_message())
2103 logger.error(self.next_message())
2104 # Will not appear
2105 hyphenated.critical(self.next_message())
2106 self.assert_log_lines([
2107 ('INFO', '4'),
2108 ('ERROR', '5'),
2109 ('INFO', '6'),
2110 ('ERROR', '7'),
2111 ], stream=output)
2112 # Original logger output is empty.
2113 self.assert_log_lines([])
2114
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002115 def test_config_9_ok(self):
2116 with captured_stdout() as output:
2117 self.apply_config(self.config9)
2118 logger = logging.getLogger("compiler.parser")
2119 #Nothing will be output since both handler and logger are set to WARNING
2120 logger.info(self.next_message())
2121 self.assert_log_lines([], stream=output)
2122 self.apply_config(self.config9a)
2123 #Nothing will be output since both handler is still set to WARNING
2124 logger.info(self.next_message())
2125 self.assert_log_lines([], stream=output)
2126 self.apply_config(self.config9b)
2127 #Message should now be output
2128 logger.info(self.next_message())
2129 self.assert_log_lines([
2130 ('INFO', '3'),
2131 ], stream=output)
2132
2133 def test_config_10_ok(self):
2134 with captured_stdout() as output:
2135 self.apply_config(self.config10)
2136 logger = logging.getLogger("compiler.parser")
2137 logger.warning(self.next_message())
2138 logger = logging.getLogger('compiler')
2139 #Not output, because filtered
2140 logger.warning(self.next_message())
2141 logger = logging.getLogger('compiler.lexer')
2142 #Not output, because filtered
2143 logger.warning(self.next_message())
2144 logger = logging.getLogger("compiler.parser.codegen")
2145 #Output, as not filtered
2146 logger.error(self.next_message())
2147 self.assert_log_lines([
2148 ('WARNING', '1'),
2149 ('ERROR', '4'),
2150 ], stream=output)
2151
2152 def test_config11_ok(self):
2153 self.test_config1_ok(self.config11)
2154
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002155 def test_config12_failure(self):
2156 self.assertRaises(Exception, self.apply_config, self.config12)
2157
2158 def test_config13_failure(self):
2159 self.assertRaises(Exception, self.apply_config, self.config13)
2160
Victor Stinner45df8202010-04-28 22:31:17 +00002161 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002162 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002163 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002164 # Ask for a randomly assigned port (by using port 0)
2165 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002166 t.start()
2167 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002168 # Now get the port allocated
2169 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002170 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002171 try:
2172 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2173 sock.settimeout(2.0)
2174 sock.connect(('localhost', port))
2175
2176 slen = struct.pack('>L', len(text))
2177 s = slen + text
2178 sentsofar = 0
2179 left = len(s)
2180 while left > 0:
2181 sent = sock.send(s[sentsofar:])
2182 sentsofar += sent
2183 left -= sent
2184 sock.close()
2185 finally:
2186 t.ready.wait(2.0)
2187 logging.config.stopListening()
2188 t.join(2.0)
2189
2190 def test_listen_config_10_ok(self):
2191 with captured_stdout() as output:
2192 self.setup_via_listener(json.dumps(self.config10))
2193 logger = logging.getLogger("compiler.parser")
2194 logger.warning(self.next_message())
2195 logger = logging.getLogger('compiler')
2196 #Not output, because filtered
2197 logger.warning(self.next_message())
2198 logger = logging.getLogger('compiler.lexer')
2199 #Not output, because filtered
2200 logger.warning(self.next_message())
2201 logger = logging.getLogger("compiler.parser.codegen")
2202 #Output, as not filtered
2203 logger.error(self.next_message())
2204 self.assert_log_lines([
2205 ('WARNING', '1'),
2206 ('ERROR', '4'),
2207 ], stream=output)
2208
2209 def test_listen_config_1_ok(self):
2210 with captured_stdout() as output:
2211 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2212 logger = logging.getLogger("compiler.parser")
2213 # Both will output a message
2214 logger.info(self.next_message())
2215 logger.error(self.next_message())
2216 self.assert_log_lines([
2217 ('INFO', '1'),
2218 ('ERROR', '2'),
2219 ], stream=output)
2220 # Original logger output is empty.
2221 self.assert_log_lines([])
2222
Vinay Sajip373baef2011-04-26 20:05:24 +01002223 def test_baseconfig(self):
2224 d = {
2225 'atuple': (1, 2, 3),
2226 'alist': ['a', 'b', 'c'],
2227 'adict': {'d': 'e', 'f': 3 },
2228 'nest1': ('g', ('h', 'i'), 'j'),
2229 'nest2': ['k', ['l', 'm'], 'n'],
2230 'nest3': ['o', 'cfg://alist', 'p'],
2231 }
2232 bc = logging.config.BaseConfigurator(d)
2233 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2234 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2235 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2236 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2237 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2238 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2239 v = bc.convert('cfg://nest3')
2240 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2241 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2242 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2243 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002244
2245class ManagerTest(BaseTest):
2246 def test_manager_loggerclass(self):
2247 logged = []
2248
2249 class MyLogger(logging.Logger):
2250 def _log(self, level, msg, args, exc_info=None, extra=None):
2251 logged.append(msg)
2252
2253 man = logging.Manager(None)
2254 self.assertRaises(TypeError, man.setLoggerClass, int)
2255 man.setLoggerClass(MyLogger)
2256 logger = man.getLogger('test')
2257 logger.warning('should appear in logged')
2258 logging.warning('should not appear in logged')
2259
2260 self.assertEqual(logged, ['should appear in logged'])
2261
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002262 def test_set_log_record_factory(self):
2263 man = logging.Manager(None)
2264 expected = object()
2265 man.setLogRecordFactory(expected)
2266 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002267
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002268class ChildLoggerTest(BaseTest):
2269 def test_child_loggers(self):
2270 r = logging.getLogger()
2271 l1 = logging.getLogger('abc')
2272 l2 = logging.getLogger('def.ghi')
2273 c1 = r.getChild('xyz')
2274 c2 = r.getChild('uvw.xyz')
2275 self.assertTrue(c1 is logging.getLogger('xyz'))
2276 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2277 c1 = l1.getChild('def')
2278 c2 = c1.getChild('ghi')
2279 c3 = l1.getChild('def.ghi')
2280 self.assertTrue(c1 is logging.getLogger('abc.def'))
2281 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2282 self.assertTrue(c2 is c3)
2283
2284
Vinay Sajip6fac8172010-10-19 20:44:14 +00002285class DerivedLogRecord(logging.LogRecord):
2286 pass
2287
Vinay Sajip61561522010-12-03 11:50:38 +00002288class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002289
2290 def setUp(self):
2291 class CheckingFilter(logging.Filter):
2292 def __init__(self, cls):
2293 self.cls = cls
2294
2295 def filter(self, record):
2296 t = type(record)
2297 if t is not self.cls:
2298 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2299 self.cls)
2300 raise TypeError(msg)
2301 return True
2302
2303 BaseTest.setUp(self)
2304 self.filter = CheckingFilter(DerivedLogRecord)
2305 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002306 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002307
2308 def tearDown(self):
2309 self.root_logger.removeFilter(self.filter)
2310 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002311 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002312
2313 def test_logrecord_class(self):
2314 self.assertRaises(TypeError, self.root_logger.warning,
2315 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002316 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002317 self.root_logger.error(self.next_message())
2318 self.assert_log_lines([
2319 ('root', 'ERROR', '2'),
2320 ])
2321
2322
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002323class QueueHandlerTest(BaseTest):
2324 # Do not bother with a logger name group.
2325 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2326
2327 def setUp(self):
2328 BaseTest.setUp(self)
2329 self.queue = queue.Queue(-1)
2330 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2331 self.que_logger = logging.getLogger('que')
2332 self.que_logger.propagate = False
2333 self.que_logger.setLevel(logging.WARNING)
2334 self.que_logger.addHandler(self.que_hdlr)
2335
2336 def tearDown(self):
2337 self.que_hdlr.close()
2338 BaseTest.tearDown(self)
2339
2340 def test_queue_handler(self):
2341 self.que_logger.debug(self.next_message())
2342 self.assertRaises(queue.Empty, self.queue.get_nowait)
2343 self.que_logger.info(self.next_message())
2344 self.assertRaises(queue.Empty, self.queue.get_nowait)
2345 msg = self.next_message()
2346 self.que_logger.warning(msg)
2347 data = self.queue.get_nowait()
2348 self.assertTrue(isinstance(data, logging.LogRecord))
2349 self.assertEqual(data.name, self.que_logger.name)
2350 self.assertEqual((data.msg, data.args), (msg, None))
2351
Vinay Sajipe723e962011-04-15 22:27:17 +01002352 def test_queue_listener(self):
2353 handler = TestHandler(Matcher())
2354 listener = logging.handlers.QueueListener(self.queue, handler)
2355 listener.start()
2356 try:
2357 self.que_logger.warning(self.next_message())
2358 self.que_logger.error(self.next_message())
2359 self.que_logger.critical(self.next_message())
2360 finally:
2361 listener.stop()
2362 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2363 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2364 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2365
Vinay Sajip37eb3382011-04-26 20:26:41 +01002366ZERO = datetime.timedelta(0)
2367
2368class UTC(datetime.tzinfo):
2369 def utcoffset(self, dt):
2370 return ZERO
2371
2372 dst = utcoffset
2373
2374 def tzname(self, dt):
2375 return 'UTC'
2376
2377utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002378
Vinay Sajipa39c5712010-10-25 13:57:39 +00002379class FormatterTest(unittest.TestCase):
2380 def setUp(self):
2381 self.common = {
2382 'name': 'formatter.test',
2383 'level': logging.DEBUG,
2384 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2385 'lineno': 42,
2386 'exc_info': None,
2387 'func': None,
2388 'msg': 'Message with %d %s',
2389 'args': (2, 'placeholders'),
2390 }
2391 self.variants = {
2392 }
2393
2394 def get_record(self, name=None):
2395 result = dict(self.common)
2396 if name is not None:
2397 result.update(self.variants[name])
2398 return logging.makeLogRecord(result)
2399
2400 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002401 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002402 r = self.get_record()
2403 f = logging.Formatter('${%(message)s}')
2404 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2405 f = logging.Formatter('%(random)s')
2406 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002407 self.assertFalse(f.usesTime())
2408 f = logging.Formatter('%(asctime)s')
2409 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002410 f = logging.Formatter('%(asctime)-15s')
2411 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002412 f = logging.Formatter('asctime')
2413 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002414
2415 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002416 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002417 r = self.get_record()
2418 f = logging.Formatter('$%{message}%$', style='{')
2419 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2420 f = logging.Formatter('{random}', style='{')
2421 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002422 self.assertFalse(f.usesTime())
2423 f = logging.Formatter('{asctime}', style='{')
2424 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002425 f = logging.Formatter('{asctime!s:15}', style='{')
2426 self.assertTrue(f.usesTime())
2427 f = logging.Formatter('{asctime:15}', style='{')
2428 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002429 f = logging.Formatter('asctime', style='{')
2430 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002431
2432 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002433 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002434 r = self.get_record()
2435 f = logging.Formatter('$message', style='$')
2436 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2437 f = logging.Formatter('$$%${message}%$$', style='$')
2438 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2439 f = logging.Formatter('${random}', style='$')
2440 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002441 self.assertFalse(f.usesTime())
2442 f = logging.Formatter('${asctime}', style='$')
2443 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002444 f = logging.Formatter('${asctime', style='$')
2445 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002446 f = logging.Formatter('$asctime', style='$')
2447 self.assertTrue(f.usesTime())
2448 f = logging.Formatter('asctime', style='$')
2449 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002450
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002451 def test_invalid_style(self):
2452 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2453
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002454 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002455 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002456 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002457 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002458 r.msecs = 123
2459 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002460 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002461 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2462 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002463 f.format(r)
2464 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2465
2466class TestBufferingFormatter(logging.BufferingFormatter):
2467 def formatHeader(self, records):
2468 return '[(%d)' % len(records)
2469
2470 def formatFooter(self, records):
2471 return '(%d)]' % len(records)
2472
2473class BufferingFormatterTest(unittest.TestCase):
2474 def setUp(self):
2475 self.records = [
2476 logging.makeLogRecord({'msg': 'one'}),
2477 logging.makeLogRecord({'msg': 'two'}),
2478 ]
2479
2480 def test_default(self):
2481 f = logging.BufferingFormatter()
2482 self.assertEqual('', f.format([]))
2483 self.assertEqual('onetwo', f.format(self.records))
2484
2485 def test_custom(self):
2486 f = TestBufferingFormatter()
2487 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2488 lf = logging.Formatter('<%(message)s>')
2489 f = TestBufferingFormatter(lf)
2490 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002491
2492class ExceptionTest(BaseTest):
2493 def test_formatting(self):
2494 r = self.root_logger
2495 h = RecordingHandler()
2496 r.addHandler(h)
2497 try:
2498 raise RuntimeError('deliberate mistake')
2499 except:
2500 logging.exception('failed', stack_info=True)
2501 r.removeHandler(h)
2502 h.close()
2503 r = h.records[0]
2504 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2505 'call last):\n'))
2506 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2507 'deliberate mistake'))
2508 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2509 'call last):\n'))
2510 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2511 'stack_info=True)'))
2512
2513
Vinay Sajip5a27d402010-12-10 11:42:57 +00002514class LastResortTest(BaseTest):
2515 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002516 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002517 root = self.root_logger
2518 root.removeHandler(self.root_hdlr)
2519 old_stderr = sys.stderr
2520 old_lastresort = logging.lastResort
2521 old_raise_exceptions = logging.raiseExceptions
2522 try:
2523 sys.stderr = sio = io.StringIO()
2524 root.warning('This is your final chance!')
2525 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2526 #No handlers and no last resort, so 'No handlers' message
2527 logging.lastResort = None
2528 sys.stderr = sio = io.StringIO()
2529 root.warning('This is your final chance!')
2530 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2531 # 'No handlers' message only printed once
2532 sys.stderr = sio = io.StringIO()
2533 root.warning('This is your final chance!')
2534 self.assertEqual(sio.getvalue(), '')
2535 root.manager.emittedNoHandlerWarning = False
2536 #If raiseExceptions is False, no message is printed
2537 logging.raiseExceptions = False
2538 sys.stderr = sio = io.StringIO()
2539 root.warning('This is your final chance!')
2540 self.assertEqual(sio.getvalue(), '')
2541 finally:
2542 sys.stderr = old_stderr
2543 root.addHandler(self.root_hdlr)
2544 logging.lastResort = old_lastresort
2545 logging.raiseExceptions = old_raise_exceptions
2546
2547
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002548class FakeHandler:
2549
2550 def __init__(self, identifier, called):
2551 for method in ('acquire', 'flush', 'close', 'release'):
2552 setattr(self, method, self.record_call(identifier, method, called))
2553
2554 def record_call(self, identifier, method_name, called):
2555 def inner():
2556 called.append('{} - {}'.format(identifier, method_name))
2557 return inner
2558
2559
2560class RecordingHandler(logging.NullHandler):
2561
2562 def __init__(self, *args, **kwargs):
2563 super(RecordingHandler, self).__init__(*args, **kwargs)
2564 self.records = []
2565
2566 def handle(self, record):
2567 """Keep track of all the emitted records."""
2568 self.records.append(record)
2569
2570
2571class ShutdownTest(BaseTest):
2572
Vinay Sajip5e66b162011-04-20 15:41:14 +01002573 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002574
2575 def setUp(self):
2576 super(ShutdownTest, self).setUp()
2577 self.called = []
2578
2579 raise_exceptions = logging.raiseExceptions
2580 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2581
2582 def raise_error(self, error):
2583 def inner():
2584 raise error()
2585 return inner
2586
2587 def test_no_failure(self):
2588 # create some fake handlers
2589 handler0 = FakeHandler(0, self.called)
2590 handler1 = FakeHandler(1, self.called)
2591 handler2 = FakeHandler(2, self.called)
2592
2593 # create live weakref to those handlers
2594 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2595
2596 logging.shutdown(handlerList=list(handlers))
2597
2598 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2599 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2600 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2601 self.assertEqual(expected, self.called)
2602
2603 def _test_with_failure_in_method(self, method, error):
2604 handler = FakeHandler(0, self.called)
2605 setattr(handler, method, self.raise_error(error))
2606 handlers = [logging.weakref.ref(handler)]
2607
2608 logging.shutdown(handlerList=list(handlers))
2609
2610 self.assertEqual('0 - release', self.called[-1])
2611
2612 def test_with_ioerror_in_acquire(self):
2613 self._test_with_failure_in_method('acquire', IOError)
2614
2615 def test_with_ioerror_in_flush(self):
2616 self._test_with_failure_in_method('flush', IOError)
2617
2618 def test_with_ioerror_in_close(self):
2619 self._test_with_failure_in_method('close', IOError)
2620
2621 def test_with_valueerror_in_acquire(self):
2622 self._test_with_failure_in_method('acquire', ValueError)
2623
2624 def test_with_valueerror_in_flush(self):
2625 self._test_with_failure_in_method('flush', ValueError)
2626
2627 def test_with_valueerror_in_close(self):
2628 self._test_with_failure_in_method('close', ValueError)
2629
2630 def test_with_other_error_in_acquire_without_raise(self):
2631 logging.raiseExceptions = False
2632 self._test_with_failure_in_method('acquire', IndexError)
2633
2634 def test_with_other_error_in_flush_without_raise(self):
2635 logging.raiseExceptions = False
2636 self._test_with_failure_in_method('flush', IndexError)
2637
2638 def test_with_other_error_in_close_without_raise(self):
2639 logging.raiseExceptions = False
2640 self._test_with_failure_in_method('close', IndexError)
2641
2642 def test_with_other_error_in_acquire_with_raise(self):
2643 logging.raiseExceptions = True
2644 self.assertRaises(IndexError, self._test_with_failure_in_method,
2645 'acquire', IndexError)
2646
2647 def test_with_other_error_in_flush_with_raise(self):
2648 logging.raiseExceptions = True
2649 self.assertRaises(IndexError, self._test_with_failure_in_method,
2650 'flush', IndexError)
2651
2652 def test_with_other_error_in_close_with_raise(self):
2653 logging.raiseExceptions = True
2654 self.assertRaises(IndexError, self._test_with_failure_in_method,
2655 'close', IndexError)
2656
2657
2658class ModuleLevelMiscTest(BaseTest):
2659
Vinay Sajip5e66b162011-04-20 15:41:14 +01002660 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002661
2662 def test_disable(self):
2663 old_disable = logging.root.manager.disable
2664 # confirm our assumptions are correct
2665 assert old_disable == 0
2666 self.addCleanup(lambda: logging.disable(old_disable))
2667
2668 logging.disable(83)
2669 self.assertEqual(logging.root.manager.disable, 83)
2670
2671 def _test_log(self, method, level=None):
2672 called = []
2673 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002674 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002675
2676 recording = RecordingHandler()
2677 logging.root.addHandler(recording)
2678
2679 log_method = getattr(logging, method)
2680 if level is not None:
2681 log_method(level, "test me: %r", recording)
2682 else:
2683 log_method("test me: %r", recording)
2684
2685 self.assertEqual(len(recording.records), 1)
2686 record = recording.records[0]
2687 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2688
2689 expected_level = level if level is not None else getattr(logging, method.upper())
2690 self.assertEqual(record.levelno, expected_level)
2691
2692 # basicConfig was not called!
2693 self.assertEqual(called, [])
2694
2695 def test_log(self):
2696 self._test_log('log', logging.ERROR)
2697
2698 def test_debug(self):
2699 self._test_log('debug')
2700
2701 def test_info(self):
2702 self._test_log('info')
2703
2704 def test_warning(self):
2705 self._test_log('warning')
2706
2707 def test_error(self):
2708 self._test_log('error')
2709
2710 def test_critical(self):
2711 self._test_log('critical')
2712
2713 def test_set_logger_class(self):
2714 self.assertRaises(TypeError, logging.setLoggerClass, object)
2715
2716 class MyLogger(logging.Logger):
2717 pass
2718
2719 logging.setLoggerClass(MyLogger)
2720 self.assertEqual(logging.getLoggerClass(), MyLogger)
2721
2722 logging.setLoggerClass(logging.Logger)
2723 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2724
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002725class LogRecordTest(BaseTest):
2726 def test_str_rep(self):
2727 r = logging.makeLogRecord({})
2728 s = str(r)
2729 self.assertTrue(s.startswith('<LogRecord: '))
2730 self.assertTrue(s.endswith('>'))
2731
2732 def test_dict_arg(self):
2733 h = RecordingHandler()
2734 r = logging.getLogger()
2735 r.addHandler(h)
2736 d = {'less' : 'more' }
2737 logging.warning('less is %(less)s', d)
2738 self.assertIs(h.records[0].args, d)
2739 self.assertEqual(h.records[0].message, 'less is more')
2740 r.removeHandler(h)
2741 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002742
Vinay Sajip7fe1d512011-04-28 12:04:58 +01002743 def test_multiprocessing(self):
2744 r = logging.makeLogRecord({})
2745 self.assertEqual(r.processName, 'MainProcess')
2746 import multiprocessing as mp
2747 r = logging.makeLogRecord({})
2748 self.assertEqual(r.processName, mp.current_process().name)
2749
2750 def test_optional(self):
2751 r = logging.makeLogRecord({})
2752 NOT_NONE = self.assertIsNotNone
2753 NOT_NONE(r.thread)
2754 NOT_NONE(r.threadName)
2755 NOT_NONE(r.process)
2756 NOT_NONE(r.processName)
2757 log_threads = logging.logThreads
2758 log_processes = logging.logProcesses
2759 log_multiprocessing = logging.logMultiprocessing
2760 try:
2761 logging.logThreads = False
2762 logging.logProcesses = False
2763 logging.logMultiprocessing = False
2764 r = logging.makeLogRecord({})
2765 NONE = self.assertIsNone
2766 NONE(r.thread)
2767 NONE(r.threadName)
2768 NONE(r.process)
2769 NONE(r.processName)
2770 finally:
2771 logging.logThreads = log_threads
2772 logging.logProcesses = log_processes
2773 logging.logMultiprocessing = log_multiprocessing
2774
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002775class BasicConfigTest(unittest.TestCase):
2776
Vinay Sajip95bf5042011-04-20 11:50:56 +01002777 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002778
2779 def setUp(self):
2780 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01002781 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01002782 self.saved_handlers = logging._handlers.copy()
2783 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002784 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002785 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002786 logging.root.handlers = []
2787
2788 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01002789 for h in logging.root.handlers[:]:
2790 logging.root.removeHandler(h)
2791 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002792 super(BasicConfigTest, self).tearDown()
2793
Vinay Sajip3def7e02011-04-20 10:58:06 +01002794 def cleanup(self):
2795 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01002796 logging._handlers.clear()
2797 logging._handlers.update(self.saved_handlers)
2798 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002799 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002800
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002801 def test_no_kwargs(self):
2802 logging.basicConfig()
2803
2804 # handler defaults to a StreamHandler to sys.stderr
2805 self.assertEqual(len(logging.root.handlers), 1)
2806 handler = logging.root.handlers[0]
2807 self.assertIsInstance(handler, logging.StreamHandler)
2808 self.assertEqual(handler.stream, sys.stderr)
2809
2810 formatter = handler.formatter
2811 # format defaults to logging.BASIC_FORMAT
2812 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2813 # datefmt defaults to None
2814 self.assertIsNone(formatter.datefmt)
2815 # style defaults to %
2816 self.assertIsInstance(formatter._style, logging.PercentStyle)
2817
2818 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01002819 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002820
2821 def test_filename(self):
2822 logging.basicConfig(filename='test.log')
2823
2824 self.assertEqual(len(logging.root.handlers), 1)
2825 handler = logging.root.handlers[0]
2826 self.assertIsInstance(handler, logging.FileHandler)
2827
2828 expected = logging.FileHandler('test.log', 'a')
2829 self.addCleanup(expected.close)
2830 self.assertEqual(handler.stream.mode, expected.stream.mode)
2831 self.assertEqual(handler.stream.name, expected.stream.name)
2832
2833 def test_filemode(self):
2834 logging.basicConfig(filename='test.log', filemode='wb')
2835
2836 handler = logging.root.handlers[0]
2837 expected = logging.FileHandler('test.log', 'wb')
2838 self.addCleanup(expected.close)
2839 self.assertEqual(handler.stream.mode, expected.stream.mode)
2840
2841 def test_stream(self):
2842 stream = io.StringIO()
2843 self.addCleanup(stream.close)
2844 logging.basicConfig(stream=stream)
2845
2846 self.assertEqual(len(logging.root.handlers), 1)
2847 handler = logging.root.handlers[0]
2848 self.assertIsInstance(handler, logging.StreamHandler)
2849 self.assertEqual(handler.stream, stream)
2850
2851 def test_format(self):
2852 logging.basicConfig(format='foo')
2853
2854 formatter = logging.root.handlers[0].formatter
2855 self.assertEqual(formatter._style._fmt, 'foo')
2856
2857 def test_datefmt(self):
2858 logging.basicConfig(datefmt='bar')
2859
2860 formatter = logging.root.handlers[0].formatter
2861 self.assertEqual(formatter.datefmt, 'bar')
2862
2863 def test_style(self):
2864 logging.basicConfig(style='$')
2865
2866 formatter = logging.root.handlers[0].formatter
2867 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2868
2869 def test_level(self):
2870 old_level = logging.root.level
2871 self.addCleanup(lambda: logging.root.setLevel(old_level))
2872
2873 logging.basicConfig(level=57)
2874 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002875 # Test that second call has no effect
2876 logging.basicConfig(level=58)
2877 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002878
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002879 def test_incompatible(self):
2880 assertRaises = self.assertRaises
2881 handlers = [logging.StreamHandler()]
2882 stream = sys.stderr
2883 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2884 stream=stream)
2885 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2886 handlers=handlers)
2887 assertRaises(ValueError, logging.basicConfig, stream=stream,
2888 handlers=handlers)
2889
2890 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002891 handlers = [
2892 logging.StreamHandler(),
2893 logging.StreamHandler(sys.stdout),
2894 logging.StreamHandler(),
2895 ]
2896 f = logging.Formatter()
2897 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002898 logging.basicConfig(handlers=handlers)
2899 self.assertIs(handlers[0], logging.root.handlers[0])
2900 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002901 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002902 self.assertIsNotNone(handlers[0].formatter)
2903 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002904 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002905 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2906
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002907 def _test_log(self, method, level=None):
2908 # logging.root has no handlers so basicConfig should be called
2909 called = []
2910
2911 old_basic_config = logging.basicConfig
2912 def my_basic_config(*a, **kw):
2913 old_basic_config()
2914 old_level = logging.root.level
2915 logging.root.setLevel(100) # avoid having messages in stderr
2916 self.addCleanup(lambda: logging.root.setLevel(old_level))
2917 called.append((a, kw))
2918
2919 patch(self, logging, 'basicConfig', my_basic_config)
2920
2921 log_method = getattr(logging, method)
2922 if level is not None:
2923 log_method(level, "test me")
2924 else:
2925 log_method("test me")
2926
2927 # basicConfig was called with no arguments
2928 self.assertEqual(called, [((), {})])
2929
2930 def test_log(self):
2931 self._test_log('log', logging.WARNING)
2932
2933 def test_debug(self):
2934 self._test_log('debug')
2935
2936 def test_info(self):
2937 self._test_log('info')
2938
2939 def test_warning(self):
2940 self._test_log('warning')
2941
2942 def test_error(self):
2943 self._test_log('error')
2944
2945 def test_critical(self):
2946 self._test_log('critical')
2947
2948
2949class LoggerAdapterTest(unittest.TestCase):
2950
2951 def setUp(self):
2952 super(LoggerAdapterTest, self).setUp()
2953 old_handler_list = logging._handlerList[:]
2954
2955 self.recording = RecordingHandler()
2956 self.logger = logging.root
2957 self.logger.addHandler(self.recording)
2958 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2959 self.addCleanup(self.recording.close)
2960
2961 def cleanup():
2962 logging._handlerList[:] = old_handler_list
2963
2964 self.addCleanup(cleanup)
2965 self.addCleanup(logging.shutdown)
2966 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2967
2968 def test_exception(self):
2969 msg = 'testing exception: %r'
2970 exc = None
2971 try:
2972 assert False
2973 except AssertionError as e:
2974 exc = e
2975 self.adapter.exception(msg, self.recording)
2976
2977 self.assertEqual(len(self.recording.records), 1)
2978 record = self.recording.records[0]
2979 self.assertEqual(record.levelno, logging.ERROR)
2980 self.assertEqual(record.msg, msg)
2981 self.assertEqual(record.args, (self.recording,))
2982 self.assertEqual(record.exc_info,
2983 (exc.__class__, exc, exc.__traceback__))
2984
2985 def test_critical(self):
2986 msg = 'critical test! %r'
2987 self.adapter.critical(msg, self.recording)
2988
2989 self.assertEqual(len(self.recording.records), 1)
2990 record = self.recording.records[0]
2991 self.assertEqual(record.levelno, logging.CRITICAL)
2992 self.assertEqual(record.msg, msg)
2993 self.assertEqual(record.args, (self.recording,))
2994
2995 def test_is_enabled_for(self):
2996 old_disable = self.adapter.logger.manager.disable
2997 self.adapter.logger.manager.disable = 33
2998 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2999 'disable', old_disable))
3000 self.assertFalse(self.adapter.isEnabledFor(32))
3001
3002 def test_has_handlers(self):
3003 self.assertTrue(self.adapter.hasHandlers())
3004
3005 for handler in self.logger.handlers:
3006 self.logger.removeHandler(handler)
3007 assert not self.logger.hasHandlers()
3008
3009 self.assertFalse(self.adapter.hasHandlers())
3010
3011
3012class LoggerTest(BaseTest):
3013
3014 def setUp(self):
3015 super(LoggerTest, self).setUp()
3016 self.recording = RecordingHandler()
3017 self.logger = logging.Logger(name='blah')
3018 self.logger.addHandler(self.recording)
3019 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3020 self.addCleanup(self.recording.close)
3021 self.addCleanup(logging.shutdown)
3022
3023 def test_set_invalid_level(self):
3024 self.assertRaises(TypeError, self.logger.setLevel, object())
3025
3026 def test_exception(self):
3027 msg = 'testing exception: %r'
3028 exc = None
3029 try:
3030 assert False
3031 except AssertionError as e:
3032 exc = e
3033 self.logger.exception(msg, self.recording)
3034
3035 self.assertEqual(len(self.recording.records), 1)
3036 record = self.recording.records[0]
3037 self.assertEqual(record.levelno, logging.ERROR)
3038 self.assertEqual(record.msg, msg)
3039 self.assertEqual(record.args, (self.recording,))
3040 self.assertEqual(record.exc_info,
3041 (exc.__class__, exc, exc.__traceback__))
3042
3043 def test_log_invalid_level_with_raise(self):
3044 old_raise = logging.raiseExceptions
3045 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3046
3047 logging.raiseExceptions = True
3048 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3049
3050 def test_log_invalid_level_no_raise(self):
3051 old_raise = logging.raiseExceptions
3052 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3053
3054 logging.raiseExceptions = False
3055 self.logger.log('10', 'test message') # no exception happens
3056
3057 def test_find_caller_with_stack_info(self):
3058 called = []
3059 patch(self, logging.traceback, 'print_stack',
3060 lambda f, file: called.append(file.getvalue()))
3061
3062 self.logger.findCaller(stack_info=True)
3063
3064 self.assertEqual(len(called), 1)
3065 self.assertEqual('Stack (most recent call last):\n', called[0])
3066
3067 def test_make_record_with_extra_overwrite(self):
3068 name = 'my record'
3069 level = 13
3070 fn = lno = msg = args = exc_info = func = sinfo = None
3071 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3072 exc_info, func, sinfo)
3073
3074 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3075 extra = {key: 'some value'}
3076 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3077 fn, lno, msg, args, exc_info,
3078 extra=extra, sinfo=sinfo)
3079
3080 def test_make_record_with_extra_no_overwrite(self):
3081 name = 'my record'
3082 level = 13
3083 fn = lno = msg = args = exc_info = func = sinfo = None
3084 extra = {'valid_key': 'some value'}
3085 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3086 exc_info, extra=extra, sinfo=sinfo)
3087 self.assertIn('valid_key', result.__dict__)
3088
3089 def test_has_handlers(self):
3090 self.assertTrue(self.logger.hasHandlers())
3091
3092 for handler in self.logger.handlers:
3093 self.logger.removeHandler(handler)
3094 assert not self.logger.hasHandlers()
3095
3096 self.assertFalse(self.logger.hasHandlers())
3097
3098 def test_has_handlers_no_propagate(self):
3099 child_logger = logging.getLogger('blah.child')
3100 child_logger.propagate = False
3101 assert child_logger.handlers == []
3102
3103 self.assertFalse(child_logger.hasHandlers())
3104
3105 def test_is_enabled_for(self):
3106 old_disable = self.logger.manager.disable
3107 self.logger.manager.disable = 23
3108 self.addCleanup(lambda: setattr(self.logger.manager,
3109 'disable', old_disable))
3110 self.assertFalse(self.logger.isEnabledFor(22))
3111
3112
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003113class BaseFileTest(BaseTest):
3114 "Base class for handler tests that write log files"
3115
3116 def setUp(self):
3117 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003118 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3119 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003120 self.rmfiles = []
3121
3122 def tearDown(self):
3123 for fn in self.rmfiles:
3124 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003125 if os.path.exists(self.fn):
3126 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003127 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003128
3129 def assertLogFile(self, filename):
3130 "Assert a log file is there and register it for deletion"
3131 self.assertTrue(os.path.exists(filename),
3132 msg="Log file %r does not exist")
3133 self.rmfiles.append(filename)
3134
3135
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003136class FileHandlerTest(BaseFileTest):
3137 def test_delay(self):
3138 os.unlink(self.fn)
3139 fh = logging.FileHandler(self.fn, delay=True)
3140 self.assertIsNone(fh.stream)
3141 self.assertFalse(os.path.exists(self.fn))
3142 fh.handle(logging.makeLogRecord({}))
3143 self.assertIsNotNone(fh.stream)
3144 self.assertTrue(os.path.exists(self.fn))
3145 fh.close()
3146
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003147class RotatingFileHandlerTest(BaseFileTest):
3148 def next_rec(self):
3149 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3150 self.next_message(), None, None, None)
3151
3152 def test_should_not_rollover(self):
3153 # If maxbytes is zero rollover never occurs
3154 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3155 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003156 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003157
3158 def test_should_rollover(self):
3159 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3160 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003161 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003162
3163 def test_file_created(self):
3164 # checks that the file is created and assumes it was created
3165 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003166 rh = logging.handlers.RotatingFileHandler(self.fn)
3167 rh.emit(self.next_rec())
3168 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003169 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003170
3171 def test_rollover_filenames(self):
3172 rh = logging.handlers.RotatingFileHandler(
3173 self.fn, backupCount=2, maxBytes=1)
3174 rh.emit(self.next_rec())
3175 self.assertLogFile(self.fn)
3176 rh.emit(self.next_rec())
3177 self.assertLogFile(self.fn + ".1")
3178 rh.emit(self.next_rec())
3179 self.assertLogFile(self.fn + ".2")
3180 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003181 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003182
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003183class TimedRotatingFileHandlerTest(BaseFileTest):
3184 # test methods added below
3185 pass
3186
3187def secs(**kw):
3188 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3189
3190for when, exp in (('S', 1),
3191 ('M', 60),
3192 ('H', 60 * 60),
3193 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003194 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003195 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003196 ('W0', secs(days=4, hours=24)),
3197 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003198 def test_compute_rollover(self, when=when, exp=exp):
3199 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003200 self.fn, when=when, interval=1, backupCount=0, utc=True)
3201 currentTime = 0.0
3202 actual = rh.computeRollover(currentTime)
3203 if exp != actual:
3204 # Failures occur on some systems for MIDNIGHT and W0.
3205 # Print detailed calculation for MIDNIGHT so we can try to see
3206 # what's going on
3207 import time
3208 if when == 'MIDNIGHT':
3209 try:
3210 if rh.utc:
3211 t = time.gmtime(currentTime)
3212 else:
3213 t = time.localtime(currentTime)
3214 currentHour = t[3]
3215 currentMinute = t[4]
3216 currentSecond = t[5]
3217 # r is the number of seconds left between now and midnight
3218 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3219 currentMinute) * 60 +
3220 currentSecond)
3221 result = currentTime + r
3222 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3223 print('currentHour: %s' % currentHour, file=sys.stderr)
3224 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3225 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3226 print('r: %s' % r, file=sys.stderr)
3227 print('result: %s' % result, file=sys.stderr)
3228 except Exception:
3229 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3230 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003231 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003232 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3233
Christian Heimes180510d2008-03-03 19:15:45 +00003234# Set the locale to the platform-dependent default. I have no idea
3235# why the test does this, but in any case we save the current locale
3236# first and restore it at the end.
3237@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003238def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003239 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003240 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003241 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003242 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip985ef872011-04-26 19:34:04 +01003243 FormatterTest, BufferingFormatterTest, StreamHandlerTest,
Vinay Sajip61561522010-12-03 11:50:38 +00003244 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003245 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
Vinay Sajipa463d252011-04-30 21:52:48 +01003246 LoggerAdapterTest, LoggerTest, SMTPHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003247 FileHandlerTest, RotatingFileHandlerTest,
3248 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003249 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003250 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003251
Christian Heimes180510d2008-03-03 19:15:45 +00003252if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003253 test_main()