blob: 96f82151b60b9c9efd93fca9c98afb4d5227b042 [file] [log] [blame]
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001#!/usr/bin/env python
Christian Heimes180510d2008-03-03 19:15:45 +00002#
Vinay Sajiped0473c2011-02-26 15:35:38 +00003# Copyright 2001-2011 by Vinay Sajip. All Rights Reserved.
Christian Heimes180510d2008-03-03 19:15:45 +00004#
5# Permission to use, copy, modify, and distribute this software and its
6# documentation for any purpose and without fee is hereby granted,
7# provided that the above copyright notice appear in all copies and that
8# both that copyright notice and this permission notice appear in
9# supporting documentation, and that the name of Vinay Sajip
10# not be used in advertising or publicity pertaining to distribution
11# of the software without specific, written prior permission.
12# VINAY SAJIP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
13# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14# VINAY SAJIP BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
15# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
16# IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19"""Test harness for the logging module. Run all tests.
20
Vinay Sajiped0473c2011-02-26 15:35:38 +000021Copyright (C) 2001-2011 Vinay Sajip. All Rights Reserved.
Neal Norwitzb4a2df02003-01-02 14:56:39 +000022"""
23
Christian Heimes180510d2008-03-03 19:15:45 +000024import logging
25import logging.handlers
26import logging.config
Christian Heimes18c66892008-02-17 13:31:39 +000027
Vinay Sajipa463d252011-04-30 21:52:48 +010028import asynchat
29import asyncore
Benjamin Petersonf91df042009-02-13 02:50:59 +000030import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000031import datetime
Vinay Sajipa463d252011-04-30 21:52:48 +010032import errno
Christian Heimes180510d2008-03-03 19:15:45 +000033import pickle
34import io
35import gc
Vinay Sajip7367d082011-05-02 13:17:27 +010036from http.server import HTTPServer, BaseHTTPRequestHandler
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000037import json
Christian Heimes180510d2008-03-03 19:15:45 +000038import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000039import queue
Christian Heimes180510d2008-03-03 19:15:45 +000040import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000041import select
Vinay Sajipa463d252011-04-30 21:52:48 +010042import smtpd
Christian Heimes180510d2008-03-03 19:15:45 +000043import socket
Vinay Sajip7367d082011-05-02 13:17:27 +010044from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
45 ThreadingTCPServer, StreamRequestHandler)
Christian Heimes180510d2008-03-03 19:15:45 +000046import struct
47import sys
48import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010049from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010050from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000051import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010052import time
Christian Heimes180510d2008-03-03 19:15:45 +000053import unittest
Vinay Sajip7367d082011-05-02 13:17:27 +010054from urllib.parse import urlparse, parse_qs
Georg Brandlf9734072008-12-07 15:30:06 +000055import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000056import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000057try:
58 import threading
59except ImportError:
60 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000061
62
Christian Heimes180510d2008-03-03 19:15:45 +000063class BaseTest(unittest.TestCase):
64
65 """Base class for logging tests."""
66
67 log_format = "%(name)s -> %(levelname)s: %(message)s"
68 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
69 message_num = 0
70
71 def setUp(self):
72 """Setup the default logging stream to an internal StringIO instance,
73 so that we can examine log output as we want."""
74 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000075 logging._acquireLock()
76 try:
Christian Heimes180510d2008-03-03 19:15:45 +000077 self.saved_handlers = logging._handlers.copy()
78 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000079 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000080 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000081 self.logger_states = logger_states = {}
82 for name in saved_loggers:
83 logger_states[name] = getattr(saved_loggers[name],
84 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000085 finally:
86 logging._releaseLock()
87
Benjamin Peterson22005fc2010-04-11 16:25:06 +000088 # Set two unused loggers: one non-ASCII and one Unicode.
89 # This is to test correct operation when sorting existing
90 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000091 self.logger1 = logging.getLogger("\xab\xd7\xbb")
92 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000093
Christian Heimes180510d2008-03-03 19:15:45 +000094 self.root_logger = logging.getLogger("")
95 self.original_logging_level = self.root_logger.getEffectiveLevel()
96
97 self.stream = io.StringIO()
98 self.root_logger.setLevel(logging.DEBUG)
99 self.root_hdlr = logging.StreamHandler(self.stream)
100 self.root_formatter = logging.Formatter(self.log_format)
101 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000102 if self.logger1.hasHandlers():
103 hlist = self.logger1.handlers + self.root_logger.handlers
104 raise AssertionError('Unexpected handlers: %s' % hlist)
105 if self.logger2.hasHandlers():
106 hlist = self.logger2.handlers + self.root_logger.handlers
107 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000108 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000109 self.assertTrue(self.logger1.hasHandlers())
110 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000111
112 def tearDown(self):
113 """Remove our logging stream, and restore the original logging
114 level."""
115 self.stream.close()
116 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000117 while self.root_logger.handlers:
118 h = self.root_logger.handlers[0]
119 self.root_logger.removeHandler(h)
120 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000121 self.root_logger.setLevel(self.original_logging_level)
122 logging._acquireLock()
123 try:
124 logging._levelNames.clear()
125 logging._levelNames.update(self.saved_level_names)
126 logging._handlers.clear()
127 logging._handlers.update(self.saved_handlers)
128 logging._handlerList[:] = self.saved_handler_list
129 loggerDict = logging.getLogger().manager.loggerDict
130 loggerDict.clear()
131 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000132 logger_states = self.logger_states
133 for name in self.logger_states:
134 if logger_states[name] is not None:
135 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000136 finally:
137 logging._releaseLock()
138
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000139 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000140 """Match the collected log lines against the regular expression
141 self.expected_log_pat, and compare the extracted group values to
142 the expected_values list of tuples."""
143 stream = stream or self.stream
144 pat = re.compile(self.expected_log_pat)
145 try:
146 stream.reset()
147 actual_lines = stream.readlines()
148 except AttributeError:
149 # StringIO.StringIO lacks a reset() method.
150 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000151 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000152 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000153 for actual, expected in zip(actual_lines, expected_values):
154 match = pat.search(actual)
155 if not match:
156 self.fail("Log line does not match expected pattern:\n" +
157 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000158 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000159 s = stream.read()
160 if s:
161 self.fail("Remaining output at end of log stream:\n" + s)
162
163 def next_message(self):
164 """Generate a message consisting solely of an auto-incrementing
165 integer."""
166 self.message_num += 1
167 return "%d" % self.message_num
168
169
170class BuiltinLevelsTest(BaseTest):
171 """Test builtin levels and their inheritance."""
172
173 def test_flat(self):
174 #Logging levels in a flat logger namespace.
175 m = self.next_message
176
177 ERR = logging.getLogger("ERR")
178 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000179 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000180 INF.setLevel(logging.INFO)
181 DEB = logging.getLogger("DEB")
182 DEB.setLevel(logging.DEBUG)
183
184 # These should log.
185 ERR.log(logging.CRITICAL, m())
186 ERR.error(m())
187
188 INF.log(logging.CRITICAL, m())
189 INF.error(m())
190 INF.warn(m())
191 INF.info(m())
192
193 DEB.log(logging.CRITICAL, m())
194 DEB.error(m())
195 DEB.warn (m())
196 DEB.info (m())
197 DEB.debug(m())
198
199 # These should not log.
200 ERR.warn(m())
201 ERR.info(m())
202 ERR.debug(m())
203
204 INF.debug(m())
205
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000206 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000207 ('ERR', 'CRITICAL', '1'),
208 ('ERR', 'ERROR', '2'),
209 ('INF', 'CRITICAL', '3'),
210 ('INF', 'ERROR', '4'),
211 ('INF', 'WARNING', '5'),
212 ('INF', 'INFO', '6'),
213 ('DEB', 'CRITICAL', '7'),
214 ('DEB', 'ERROR', '8'),
215 ('DEB', 'WARNING', '9'),
216 ('DEB', 'INFO', '10'),
217 ('DEB', 'DEBUG', '11'),
218 ])
219
220 def test_nested_explicit(self):
221 # Logging levels in a nested namespace, all explicitly set.
222 m = self.next_message
223
224 INF = logging.getLogger("INF")
225 INF.setLevel(logging.INFO)
226 INF_ERR = logging.getLogger("INF.ERR")
227 INF_ERR.setLevel(logging.ERROR)
228
229 # These should log.
230 INF_ERR.log(logging.CRITICAL, m())
231 INF_ERR.error(m())
232
233 # These should not log.
234 INF_ERR.warn(m())
235 INF_ERR.info(m())
236 INF_ERR.debug(m())
237
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000238 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000239 ('INF.ERR', 'CRITICAL', '1'),
240 ('INF.ERR', 'ERROR', '2'),
241 ])
242
243 def test_nested_inherited(self):
244 #Logging levels in a nested namespace, inherited from parent loggers.
245 m = self.next_message
246
247 INF = logging.getLogger("INF")
248 INF.setLevel(logging.INFO)
249 INF_ERR = logging.getLogger("INF.ERR")
250 INF_ERR.setLevel(logging.ERROR)
251 INF_UNDEF = logging.getLogger("INF.UNDEF")
252 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
253 UNDEF = logging.getLogger("UNDEF")
254
255 # These should log.
256 INF_UNDEF.log(logging.CRITICAL, m())
257 INF_UNDEF.error(m())
258 INF_UNDEF.warn(m())
259 INF_UNDEF.info(m())
260 INF_ERR_UNDEF.log(logging.CRITICAL, m())
261 INF_ERR_UNDEF.error(m())
262
263 # These should not log.
264 INF_UNDEF.debug(m())
265 INF_ERR_UNDEF.warn(m())
266 INF_ERR_UNDEF.info(m())
267 INF_ERR_UNDEF.debug(m())
268
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000269 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000270 ('INF.UNDEF', 'CRITICAL', '1'),
271 ('INF.UNDEF', 'ERROR', '2'),
272 ('INF.UNDEF', 'WARNING', '3'),
273 ('INF.UNDEF', 'INFO', '4'),
274 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
275 ('INF.ERR.UNDEF', 'ERROR', '6'),
276 ])
277
278 def test_nested_with_virtual_parent(self):
279 # Logging levels when some parent does not exist yet.
280 m = self.next_message
281
282 INF = logging.getLogger("INF")
283 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
284 CHILD = logging.getLogger("INF.BADPARENT")
285 INF.setLevel(logging.INFO)
286
287 # These should log.
288 GRANDCHILD.log(logging.FATAL, m())
289 GRANDCHILD.info(m())
290 CHILD.log(logging.FATAL, m())
291 CHILD.info(m())
292
293 # These should not log.
294 GRANDCHILD.debug(m())
295 CHILD.debug(m())
296
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000297 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000298 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
299 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
300 ('INF.BADPARENT', 'CRITICAL', '3'),
301 ('INF.BADPARENT', 'INFO', '4'),
302 ])
303
304
305class BasicFilterTest(BaseTest):
306
307 """Test the bundled Filter class."""
308
309 def test_filter(self):
310 # Only messages satisfying the specified criteria pass through the
311 # filter.
312 filter_ = logging.Filter("spam.eggs")
313 handler = self.root_logger.handlers[0]
314 try:
315 handler.addFilter(filter_)
316 spam = logging.getLogger("spam")
317 spam_eggs = logging.getLogger("spam.eggs")
318 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
319 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
320
321 spam.info(self.next_message())
322 spam_eggs.info(self.next_message()) # Good.
323 spam_eggs_fish.info(self.next_message()) # Good.
324 spam_bakedbeans.info(self.next_message())
325
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000326 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000327 ('spam.eggs', 'INFO', '2'),
328 ('spam.eggs.fish', 'INFO', '3'),
329 ])
330 finally:
331 handler.removeFilter(filter_)
332
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000333 def test_callable_filter(self):
334 # Only messages satisfying the specified criteria pass through the
335 # filter.
336
337 def filterfunc(record):
338 parts = record.name.split('.')
339 prefix = '.'.join(parts[:2])
340 return prefix == 'spam.eggs'
341
342 handler = self.root_logger.handlers[0]
343 try:
344 handler.addFilter(filterfunc)
345 spam = logging.getLogger("spam")
346 spam_eggs = logging.getLogger("spam.eggs")
347 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
348 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
349
350 spam.info(self.next_message())
351 spam_eggs.info(self.next_message()) # Good.
352 spam_eggs_fish.info(self.next_message()) # Good.
353 spam_bakedbeans.info(self.next_message())
354
355 self.assert_log_lines([
356 ('spam.eggs', 'INFO', '2'),
357 ('spam.eggs.fish', 'INFO', '3'),
358 ])
359 finally:
360 handler.removeFilter(filterfunc)
361
Vinay Sajip985ef872011-04-26 19:34:04 +0100362 def test_empty_filter(self):
363 f = logging.Filter()
364 r = logging.makeLogRecord({'name': 'spam.eggs'})
365 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000366
367#
368# First, we define our levels. There can be as many as you want - the only
369# limitations are that they should be integers, the lowest should be > 0 and
370# larger values mean less information being logged. If you need specific
371# level values which do not fit into these limitations, you can use a
372# mapping dictionary to convert between your application levels and the
373# logging system.
374#
375SILENT = 120
376TACITURN = 119
377TERSE = 118
378EFFUSIVE = 117
379SOCIABLE = 116
380VERBOSE = 115
381TALKATIVE = 114
382GARRULOUS = 113
383CHATTERBOX = 112
384BORING = 111
385
386LEVEL_RANGE = range(BORING, SILENT + 1)
387
388#
389# Next, we define names for our levels. You don't need to do this - in which
390# case the system will use "Level n" to denote the text for the level.
391#
392my_logging_levels = {
393 SILENT : 'Silent',
394 TACITURN : 'Taciturn',
395 TERSE : 'Terse',
396 EFFUSIVE : 'Effusive',
397 SOCIABLE : 'Sociable',
398 VERBOSE : 'Verbose',
399 TALKATIVE : 'Talkative',
400 GARRULOUS : 'Garrulous',
401 CHATTERBOX : 'Chatterbox',
402 BORING : 'Boring',
403}
404
405class GarrulousFilter(logging.Filter):
406
407 """A filter which blocks garrulous messages."""
408
409 def filter(self, record):
410 return record.levelno != GARRULOUS
411
412class VerySpecificFilter(logging.Filter):
413
414 """A filter which blocks sociable and taciturn messages."""
415
416 def filter(self, record):
417 return record.levelno not in [SOCIABLE, TACITURN]
418
419
420class CustomLevelsAndFiltersTest(BaseTest):
421
422 """Test various filtering possibilities with custom logging levels."""
423
424 # Skip the logger name group.
425 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
426
427 def setUp(self):
428 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000429 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000430 logging.addLevelName(k, v)
431
432 def log_at_all_levels(self, logger):
433 for lvl in LEVEL_RANGE:
434 logger.log(lvl, self.next_message())
435
436 def test_logger_filter(self):
437 # Filter at logger level.
438 self.root_logger.setLevel(VERBOSE)
439 # Levels >= 'Verbose' are good.
440 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000441 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000442 ('Verbose', '5'),
443 ('Sociable', '6'),
444 ('Effusive', '7'),
445 ('Terse', '8'),
446 ('Taciturn', '9'),
447 ('Silent', '10'),
448 ])
449
450 def test_handler_filter(self):
451 # Filter at handler level.
452 self.root_logger.handlers[0].setLevel(SOCIABLE)
453 try:
454 # Levels >= 'Sociable' are good.
455 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000456 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000457 ('Sociable', '6'),
458 ('Effusive', '7'),
459 ('Terse', '8'),
460 ('Taciturn', '9'),
461 ('Silent', '10'),
462 ])
463 finally:
464 self.root_logger.handlers[0].setLevel(logging.NOTSET)
465
466 def test_specific_filters(self):
467 # Set a specific filter object on the handler, and then add another
468 # filter object on the logger itself.
469 handler = self.root_logger.handlers[0]
470 specific_filter = None
471 garr = GarrulousFilter()
472 handler.addFilter(garr)
473 try:
474 self.log_at_all_levels(self.root_logger)
475 first_lines = [
476 # Notice how 'Garrulous' is missing
477 ('Boring', '1'),
478 ('Chatterbox', '2'),
479 ('Talkative', '4'),
480 ('Verbose', '5'),
481 ('Sociable', '6'),
482 ('Effusive', '7'),
483 ('Terse', '8'),
484 ('Taciturn', '9'),
485 ('Silent', '10'),
486 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000487 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000488
489 specific_filter = VerySpecificFilter()
490 self.root_logger.addFilter(specific_filter)
491 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000492 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000493 # Not only 'Garrulous' is still missing, but also 'Sociable'
494 # and 'Taciturn'
495 ('Boring', '11'),
496 ('Chatterbox', '12'),
497 ('Talkative', '14'),
498 ('Verbose', '15'),
499 ('Effusive', '17'),
500 ('Terse', '18'),
501 ('Silent', '20'),
502 ])
503 finally:
504 if specific_filter:
505 self.root_logger.removeFilter(specific_filter)
506 handler.removeFilter(garr)
507
508
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100509class HandlerTest(BaseTest):
510 def test_name(self):
511 h = logging.Handler()
512 h.name = 'generic'
513 self.assertEqual(h.name, 'generic')
514 h.name = 'anothergeneric'
515 self.assertEqual(h.name, 'anothergeneric')
516 self.assertRaises(NotImplementedError, h.emit, None)
517
Vinay Sajip5a35b062011-04-27 11:31:14 +0100518 def test_builtin_handlers(self):
519 # We can't actually *use* too many handlers in the tests,
520 # but we can try instantiating them with various options
521 if sys.platform in ('linux2', 'darwin'):
522 for existing in (True, False):
523 fd, fn = tempfile.mkstemp()
524 os.close(fd)
525 if not existing:
526 os.unlink(fn)
527 h = logging.handlers.WatchedFileHandler(fn, delay=True)
528 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100529 dev, ino = h.dev, h.ino
530 self.assertNotEqual(dev, -1)
531 self.assertNotEqual(ino, -1)
532 r = logging.makeLogRecord({'msg': 'Test'})
533 h.handle(r)
534 # Now remove the file.
535 os.unlink(fn)
536 self.assertFalse(os.path.exists(fn))
537 # The next call should recreate the file.
538 h.handle(r)
539 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100540 else:
541 self.assertEqual(h.dev, -1)
542 self.assertEqual(h.ino, -1)
543 h.close()
544 if existing:
545 os.unlink(fn)
546 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100547 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100548 else:
549 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100550 try:
551 h = logging.handlers.SysLogHandler(sockname)
552 self.assertEqual(h.facility, h.LOG_USER)
553 self.assertTrue(h.unixsocket)
554 h.close()
555 except socket.error: # syslogd might not be available
556 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100557 for method in ('GET', 'POST', 'PUT'):
558 if method == 'PUT':
559 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
560 'localhost', '/log', method)
561 else:
562 h = logging.handlers.HTTPHandler('localhost', '/log', method)
563 h.close()
564 h = logging.handlers.BufferingHandler(0)
565 r = logging.makeLogRecord({})
566 self.assertTrue(h.shouldFlush(r))
567 h.close()
568 h = logging.handlers.BufferingHandler(1)
569 self.assertFalse(h.shouldFlush(r))
570 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100571
572class BadStream(object):
573 def write(self, data):
574 raise RuntimeError('deliberate mistake')
575
576class TestStreamHandler(logging.StreamHandler):
577 def handleError(self, record):
578 self.error_record = record
579
580class StreamHandlerTest(BaseTest):
581 def test_error_handling(self):
582 h = TestStreamHandler(BadStream())
583 r = logging.makeLogRecord({})
584 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100585 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100586 try:
587 h.handle(r)
588 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100589 h = logging.StreamHandler(BadStream())
590 sys.stderr = sio = io.StringIO()
591 h.handle(r)
592 self.assertTrue('\nRuntimeError: '
593 'deliberate mistake\n' in sio.getvalue())
594 logging.raiseExceptions = False
595 sys.stderr = sio = io.StringIO()
596 h.handle(r)
597 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100598 finally:
599 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100600 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100601
Vinay Sajip7367d082011-05-02 13:17:27 +0100602# -- The following section could be moved into a server_helper.py module
603# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100604
605class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100606 """
607 This derived class has had to be created because smtpd does not
608 support use of custom channel maps, although they are allowed by
609 asyncore's design. Issue #11959 has been raised to address this,
610 and if resolved satisfactorily, some of this code can be removed.
611 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100612 def __init__(self, server, conn, addr, sockmap):
613 asynchat.async_chat.__init__(self, conn, sockmap)
614 self.smtp_server = server
615 self.conn = conn
616 self.addr = addr
617 self.received_lines = []
618 self.smtp_state = self.COMMAND
619 self.seen_greeting = ''
620 self.mailfrom = None
621 self.rcpttos = []
622 self.received_data = ''
623 self.fqdn = socket.getfqdn()
624 self.num_bytes = 0
625 try:
626 self.peer = conn.getpeername()
627 except socket.error as err:
628 # a race condition may occur if the other end is closing
629 # before we can get the peername
630 self.close()
631 if err.args[0] != errno.ENOTCONN:
632 raise
633 return
634 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
635 self.set_terminator(b'\r\n')
636
637
638class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100639 """
640 This class implements a test SMTP server.
641
642 :param addr: A (host, port) tuple which the server listens on.
643 You can specify a port value of zero: the server's
644 *port* attribute will hold the actual port number
645 used, which can be used in client connections.
646 :param handler: A callable which will be called to process
647 incoming messages. The handler will be passed
648 the client address tuple, who the message is from,
649 a list of recipients and the message data.
650 :param poll_interval: The interval, in seconds, used in the underlying
651 :func:`select` or :func:`poll` call by
652 :func:`asyncore.loop`.
653 :param sockmap: A dictionary which will be used to hold
654 :class:`asyncore.dispatcher` instances used by
655 :func:`asyncore.loop`. This avoids changing the
656 :mod:`asyncore` module's global state.
657 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100658 channel_class = TestSMTPChannel
659
660 def __init__(self, addr, handler, poll_interval, sockmap):
661 self._localaddr = addr
662 self._remoteaddr = None
663 self.sockmap = sockmap
664 asyncore.dispatcher.__init__(self, map=sockmap)
665 try:
666 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
667 sock.setblocking(0)
668 self.set_socket(sock, map=sockmap)
669 # try to re-use a server port if possible
670 self.set_reuse_addr()
671 self.bind(addr)
Vinay Sajip7367d082011-05-02 13:17:27 +0100672 self.port = sock.getsockname()[1]
Vinay Sajipa463d252011-04-30 21:52:48 +0100673 self.listen(5)
674 except:
675 self.close()
676 raise
677 self._handler = handler
678 self._thread = None
679 self.poll_interval = poll_interval
680
681 def handle_accepted(self, conn, addr):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100682 """
683 Redefined only because the base class does not pass in a
684 map, forcing use of a global in :mod:`asyncore`.
685 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100686 channel = self.channel_class(self, conn, addr, self.sockmap)
687
688 def process_message(self, peer, mailfrom, rcpttos, data):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100689 """
690 Delegates to the handler passed in to the server's constructor.
691
692 Typically, this will be a test case method.
693 :param peer: The client (host, port) tuple.
694 :param mailfrom: The address of the sender.
695 :param rcpttos: The addresses of the recipients.
696 :param data: The message.
697 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100698 self._handler(peer, mailfrom, rcpttos, data)
699
700 def start(self):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100701 """
702 Start the server running on a separate daemon thread.
703 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100704 self._thread = t = threading.Thread(target=self.serve_forever,
705 args=(self.poll_interval,))
706 t.setDaemon(True)
707 t.start()
708
709 def serve_forever(self, poll_interval):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100710 """
711 Run the :mod:`asyncore` loop until normal termination
712 conditions arise.
713 :param poll_interval: The interval, in seconds, used in the underlying
714 :func:`select` or :func:`poll` call by
715 :func:`asyncore.loop`.
716 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100717 asyncore.loop(poll_interval, map=self.sockmap)
718
Vinay Sajip7367d082011-05-02 13:17:27 +0100719 def stop(self, timeout=None):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100720 """
721 Stop the thread by closing the server instance.
722 Wait for the server thread to terminate.
723
724 :param timeout: How long to wait for the server thread
725 to terminate.
726 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100727 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100728 self._thread.join(timeout)
Vinay Sajipa463d252011-04-30 21:52:48 +0100729 self._thread = None
730
Vinay Sajip7367d082011-05-02 13:17:27 +0100731class ControlMixin(object):
732 """
733 This mixin is used to start a server on a separate thread, and
734 shut it down programmatically. Request handling is simplified - instead
735 of needing to derive a suitable RequestHandler subclass, you just
736 provide a callable which will be passed each received request to be
737 processed.
738
739 :param handler: A handler callable which will be called with a
740 single parameter - the request - in order to
741 process the request. This handler is called on the
742 server thread, effectively meaning that requests are
743 processed serially. While not quite Web scale ;-),
744 this should be fine for testing applications.
745 :param poll_interval: The polling interval in seconds.
746 """
747 def __init__(self, handler, poll_interval):
748 self._thread = None
749 self.poll_interval = poll_interval
750 self._handler = handler
751 self.ready = threading.Event()
752
753 def start(self):
754 """
755 Create a daemon thread to run the server, and start it.
756 """
757 self._thread = t = threading.Thread(target=self.serve_forever,
758 args=(self.poll_interval,))
759 t.setDaemon(True)
760 t.start()
761
762 def serve_forever(self, poll_interval):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100763 """
764 Run the server. Set the ready flag before entering the
765 service loop.
766 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100767 self.ready.set()
768 super(ControlMixin, self).serve_forever(poll_interval)
769
770 def stop(self, timeout=None):
771 """
772 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100773
774 :param timeout: How long to wait for the server thread
775 to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100776 """
777 self.shutdown()
778 if self._thread is not None:
779 self._thread.join(timeout)
780 self._thread = None
781 self.server_close()
782 self.ready.clear()
783
784class TestHTTPServer(ControlMixin, HTTPServer):
785 """
786 An HTTP server which is controllable using :class:`ControlMixin`.
787
788 :param addr: A tuple with the IP address and port to listen on.
789 :param handler: A handler callable which will be called with a
790 single parameter - the request - in order to
791 process the request.
792 :param poll_interval: The polling interval in seconds.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100793 :param log: Pass ``True`` to enable log messages.
Vinay Sajip7367d082011-05-02 13:17:27 +0100794 """
795 def __init__(self, addr, handler, poll_interval=0.5, log=False):
796 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
797 def __getattr__(self, name, default=None):
798 if name.startswith('do_'):
799 return self.process_request
800 raise AttributeError(name)
801
802 def process_request(self):
803 self.server._handler(self)
804
805 def log_message(self, format, *args):
806 if log:
807 super(DelegatingHTTPRequestHandler,
808 self).log_message(format, *args)
809 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
810 ControlMixin.__init__(self, handler, poll_interval)
811
812class TestTCPServer(ControlMixin, ThreadingTCPServer):
813 """
814 A TCP server which is controllable using :class:`ControlMixin`.
815
816 :param addr: A tuple with the IP address and port to listen on.
817 :param handler: A handler callable which will be called with a single
818 parameter - the request - in order to process the request.
819 :param poll_interval: The polling interval in seconds.
820 :bind_and_activate: If True (the default), binds the server and starts it
821 listening. If False, you need to call
822 :meth:`server_bind` and :meth:`server_activate` at
823 some later time before calling :meth:`start`, so that
824 the server will set up the socket and listen on it.
825 """
826
827 allow_reuse_address = True
828
829 def __init__(self, addr, handler, poll_interval=0.5,
830 bind_and_activate=True):
831 class DelegatingTCPRequestHandler(StreamRequestHandler):
832
833 def handle(self):
834 self.server._handler(self)
835 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
836 bind_and_activate)
837 ControlMixin.__init__(self, handler, poll_interval)
838
839 def server_bind(self):
840 super(TestTCPServer, self).server_bind()
841 self.port = self.socket.getsockname()[1]
842
843class TestUDPServer(ControlMixin, ThreadingUDPServer):
844 """
845 A UDP server which is controllable using :class:`ControlMixin`.
846
847 :param addr: A tuple with the IP address and port to listen on.
848 :param handler: A handler callable which will be called with a
849 single parameter - the request - in order to
850 process the request.
851 :param poll_interval: The polling interval for shutdown requests,
852 in seconds.
853 :bind_and_activate: If True (the default), binds the server and
854 starts it listening. If False, you need to
855 call :meth:`server_bind` and
856 :meth:`server_activate` at some later time
857 before calling :meth:`start`, so that the server will
858 set up the socket and listen on it.
859 """
860 def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True):
861 class DelegatingUDPRequestHandler(DatagramRequestHandler):
862
863 def handle(self):
864 self.server._handler(self)
865 ThreadingUDPServer.__init__(self, addr, DelegatingUDPRequestHandler,
866 bind_and_activate)
867 ControlMixin.__init__(self, handler, poll_interval)
868
869 def server_bind(self):
870 super(TestUDPServer, self).server_bind()
871 self.port = self.socket.getsockname()[1]
872
873
874# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100875
876class SMTPHandlerTest(BaseTest):
877 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100878 sockmap = {}
879 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
880 sockmap)
881 server.start()
882 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100883 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
884 self.assertEqual(h.toaddrs, ['you'])
885 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100886 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100887 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100888 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100889 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100890 server.stop()
891 self.assertEqual(len(self.messages), 1)
892 peer, mailfrom, rcpttos, data = self.messages[0]
893 self.assertEqual(mailfrom, 'me')
894 self.assertEqual(rcpttos, ['you'])
895 self.assertTrue('\nSubject: Log\n' in data)
896 self.assertTrue(data.endswith('\n\nHello'))
897 h.close()
898
899 def process_message(self, *args):
900 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100901 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100902
Christian Heimes180510d2008-03-03 19:15:45 +0000903class MemoryHandlerTest(BaseTest):
904
905 """Tests for the MemoryHandler."""
906
907 # Do not bother with a logger name group.
908 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
909
910 def setUp(self):
911 BaseTest.setUp(self)
912 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
913 self.root_hdlr)
914 self.mem_logger = logging.getLogger('mem')
915 self.mem_logger.propagate = 0
916 self.mem_logger.addHandler(self.mem_hdlr)
917
918 def tearDown(self):
919 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000920 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000921
922 def test_flush(self):
923 # The memory handler flushes to its target handler based on specific
924 # criteria (message count and message level).
925 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000926 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000927 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000928 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000929 # This will flush because the level is >= logging.WARNING
930 self.mem_logger.warn(self.next_message())
931 lines = [
932 ('DEBUG', '1'),
933 ('INFO', '2'),
934 ('WARNING', '3'),
935 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000936 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000937 for n in (4, 14):
938 for i in range(9):
939 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000940 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000941 # This will flush because it's the 10th message since the last
942 # flush.
943 self.mem_logger.debug(self.next_message())
944 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000945 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000946
947 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000948 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000949
950
951class ExceptionFormatter(logging.Formatter):
952 """A special exception formatter."""
953 def formatException(self, ei):
954 return "Got a [%s]" % ei[0].__name__
955
956
957class ConfigFileTest(BaseTest):
958
959 """Reading logging config from a .ini-style config file."""
960
961 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
962
963 # config0 is a standard configuration.
964 config0 = """
965 [loggers]
966 keys=root
967
968 [handlers]
969 keys=hand1
970
971 [formatters]
972 keys=form1
973
974 [logger_root]
975 level=WARNING
976 handlers=hand1
977
978 [handler_hand1]
979 class=StreamHandler
980 level=NOTSET
981 formatter=form1
982 args=(sys.stdout,)
983
984 [formatter_form1]
985 format=%(levelname)s ++ %(message)s
986 datefmt=
987 """
988
989 # config1 adds a little to the standard configuration.
990 config1 = """
991 [loggers]
992 keys=root,parser
993
994 [handlers]
995 keys=hand1
996
997 [formatters]
998 keys=form1
999
1000 [logger_root]
1001 level=WARNING
1002 handlers=
1003
1004 [logger_parser]
1005 level=DEBUG
1006 handlers=hand1
1007 propagate=1
1008 qualname=compiler.parser
1009
1010 [handler_hand1]
1011 class=StreamHandler
1012 level=NOTSET
1013 formatter=form1
1014 args=(sys.stdout,)
1015
1016 [formatter_form1]
1017 format=%(levelname)s ++ %(message)s
1018 datefmt=
1019 """
1020
Vinay Sajip3f84b072011-03-07 17:49:33 +00001021 # config1a moves the handler to the root.
1022 config1a = """
1023 [loggers]
1024 keys=root,parser
1025
1026 [handlers]
1027 keys=hand1
1028
1029 [formatters]
1030 keys=form1
1031
1032 [logger_root]
1033 level=WARNING
1034 handlers=hand1
1035
1036 [logger_parser]
1037 level=DEBUG
1038 handlers=
1039 propagate=1
1040 qualname=compiler.parser
1041
1042 [handler_hand1]
1043 class=StreamHandler
1044 level=NOTSET
1045 formatter=form1
1046 args=(sys.stdout,)
1047
1048 [formatter_form1]
1049 format=%(levelname)s ++ %(message)s
1050 datefmt=
1051 """
1052
Christian Heimes180510d2008-03-03 19:15:45 +00001053 # config2 has a subtle configuration error that should be reported
1054 config2 = config1.replace("sys.stdout", "sys.stbout")
1055
1056 # config3 has a less subtle configuration error
1057 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1058
1059 # config4 specifies a custom formatter class to be loaded
1060 config4 = """
1061 [loggers]
1062 keys=root
1063
1064 [handlers]
1065 keys=hand1
1066
1067 [formatters]
1068 keys=form1
1069
1070 [logger_root]
1071 level=NOTSET
1072 handlers=hand1
1073
1074 [handler_hand1]
1075 class=StreamHandler
1076 level=NOTSET
1077 formatter=form1
1078 args=(sys.stdout,)
1079
1080 [formatter_form1]
1081 class=""" + __name__ + """.ExceptionFormatter
1082 format=%(levelname)s:%(name)s:%(message)s
1083 datefmt=
1084 """
1085
Georg Brandl3dbca812008-07-23 16:10:53 +00001086 # config5 specifies a custom handler class to be loaded
1087 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1088
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001089 # config6 uses ', ' delimiters in the handlers and formatters sections
1090 config6 = """
1091 [loggers]
1092 keys=root,parser
1093
1094 [handlers]
1095 keys=hand1, hand2
1096
1097 [formatters]
1098 keys=form1, form2
1099
1100 [logger_root]
1101 level=WARNING
1102 handlers=
1103
1104 [logger_parser]
1105 level=DEBUG
1106 handlers=hand1
1107 propagate=1
1108 qualname=compiler.parser
1109
1110 [handler_hand1]
1111 class=StreamHandler
1112 level=NOTSET
1113 formatter=form1
1114 args=(sys.stdout,)
1115
1116 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001117 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001118 level=NOTSET
1119 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001120 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001121
1122 [formatter_form1]
1123 format=%(levelname)s ++ %(message)s
1124 datefmt=
1125
1126 [formatter_form2]
1127 format=%(message)s
1128 datefmt=
1129 """
1130
Vinay Sajip3f84b072011-03-07 17:49:33 +00001131 # config7 adds a compiler logger.
1132 config7 = """
1133 [loggers]
1134 keys=root,parser,compiler
1135
1136 [handlers]
1137 keys=hand1
1138
1139 [formatters]
1140 keys=form1
1141
1142 [logger_root]
1143 level=WARNING
1144 handlers=hand1
1145
1146 [logger_compiler]
1147 level=DEBUG
1148 handlers=
1149 propagate=1
1150 qualname=compiler
1151
1152 [logger_parser]
1153 level=DEBUG
1154 handlers=
1155 propagate=1
1156 qualname=compiler.parser
1157
1158 [handler_hand1]
1159 class=StreamHandler
1160 level=NOTSET
1161 formatter=form1
1162 args=(sys.stdout,)
1163
1164 [formatter_form1]
1165 format=%(levelname)s ++ %(message)s
1166 datefmt=
1167 """
1168
Christian Heimes180510d2008-03-03 19:15:45 +00001169 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001170 file = io.StringIO(textwrap.dedent(conf))
1171 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001172
1173 def test_config0_ok(self):
1174 # A simple config file which overrides the default settings.
1175 with captured_stdout() as output:
1176 self.apply_config(self.config0)
1177 logger = logging.getLogger()
1178 # Won't output anything
1179 logger.info(self.next_message())
1180 # Outputs a message
1181 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001182 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001183 ('ERROR', '2'),
1184 ], stream=output)
1185 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001186 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001187
Georg Brandl3dbca812008-07-23 16:10:53 +00001188 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001189 # A config file defining a sub-parser as well.
1190 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001191 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001192 logger = logging.getLogger("compiler.parser")
1193 # Both will output a message
1194 logger.info(self.next_message())
1195 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001196 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001197 ('INFO', '1'),
1198 ('ERROR', '2'),
1199 ], stream=output)
1200 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001201 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001202
1203 def test_config2_failure(self):
1204 # A simple config file which overrides the default settings.
1205 self.assertRaises(Exception, self.apply_config, self.config2)
1206
1207 def test_config3_failure(self):
1208 # A simple config file which overrides the default settings.
1209 self.assertRaises(Exception, self.apply_config, self.config3)
1210
1211 def test_config4_ok(self):
1212 # A config file specifying a custom formatter class.
1213 with captured_stdout() as output:
1214 self.apply_config(self.config4)
1215 logger = logging.getLogger()
1216 try:
1217 raise RuntimeError()
1218 except RuntimeError:
1219 logging.exception("just testing")
1220 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001221 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001222 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1223 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001224 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001225
Georg Brandl3dbca812008-07-23 16:10:53 +00001226 def test_config5_ok(self):
1227 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001228
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001229 def test_config6_ok(self):
1230 self.test_config1_ok(config=self.config6)
1231
Vinay Sajip3f84b072011-03-07 17:49:33 +00001232 def test_config7_ok(self):
1233 with captured_stdout() as output:
1234 self.apply_config(self.config1a)
1235 logger = logging.getLogger("compiler.parser")
1236 # See issue #11424. compiler-hyphenated sorts
1237 # between compiler and compiler.xyz and this
1238 # was preventing compiler.xyz from being included
1239 # in the child loggers of compiler because of an
1240 # overzealous loop termination condition.
1241 hyphenated = logging.getLogger('compiler-hyphenated')
1242 # All will output a message
1243 logger.info(self.next_message())
1244 logger.error(self.next_message())
1245 hyphenated.critical(self.next_message())
1246 self.assert_log_lines([
1247 ('INFO', '1'),
1248 ('ERROR', '2'),
1249 ('CRITICAL', '3'),
1250 ], stream=output)
1251 # Original logger output is empty.
1252 self.assert_log_lines([])
1253 with captured_stdout() as output:
1254 self.apply_config(self.config7)
1255 logger = logging.getLogger("compiler.parser")
1256 self.assertFalse(logger.disabled)
1257 # Both will output a message
1258 logger.info(self.next_message())
1259 logger.error(self.next_message())
1260 logger = logging.getLogger("compiler.lexer")
1261 # Both will output a message
1262 logger.info(self.next_message())
1263 logger.error(self.next_message())
1264 # Will not appear
1265 hyphenated.critical(self.next_message())
1266 self.assert_log_lines([
1267 ('INFO', '4'),
1268 ('ERROR', '5'),
1269 ('INFO', '6'),
1270 ('ERROR', '7'),
1271 ], stream=output)
1272 # Original logger output is empty.
1273 self.assert_log_lines([])
1274
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001275
Victor Stinner45df8202010-04-28 22:31:17 +00001276@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001277class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001278
Christian Heimes180510d2008-03-03 19:15:45 +00001279 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001280
Christian Heimes180510d2008-03-03 19:15:45 +00001281 def setUp(self):
1282 """Set up a TCP server to receive log messages, and a SocketHandler
1283 pointing to that server's address and port."""
1284 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001285 addr = ('localhost', 0)
1286 self.server = server = TestTCPServer(addr, self.handle_socket,
1287 0.01)
1288 server.start()
1289 server.ready.wait()
1290 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1291 server.port)
1292 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001293 self.root_logger.removeHandler(self.root_logger.handlers[0])
1294 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001295 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001296
Christian Heimes180510d2008-03-03 19:15:45 +00001297 def tearDown(self):
1298 """Shutdown the TCP server."""
1299 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001300 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001301 self.root_logger.removeHandler(self.sock_hdlr)
1302 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001303 finally:
1304 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001305
Vinay Sajip7367d082011-05-02 13:17:27 +01001306 def handle_socket(self, request):
1307 conn = request.connection
1308 while True:
1309 chunk = conn.recv(4)
1310 if len(chunk) < 4:
1311 break
1312 slen = struct.unpack(">L", chunk)[0]
1313 chunk = conn.recv(slen)
1314 while len(chunk) < slen:
1315 chunk = chunk + conn.recv(slen - len(chunk))
1316 obj = pickle.loads(chunk)
1317 record = logging.makeLogRecord(obj)
1318 self.log_output += record.msg + '\n'
1319 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001320
Christian Heimes180510d2008-03-03 19:15:45 +00001321 def test_output(self):
1322 # The log message sent to the SocketHandler is properly received.
1323 logger = logging.getLogger("tcp")
1324 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001325 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001326 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001327 self.handled.acquire()
1328 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001329
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001330 def test_noserver(self):
1331 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001332 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001333 #The logging call should try to connect, which should fail
1334 try:
1335 raise RuntimeError('Deliberate mistake')
1336 except RuntimeError:
1337 self.root_logger.exception('Never sent')
1338 self.root_logger.error('Never sent, either')
1339 now = time.time()
1340 self.assertTrue(self.sock_hdlr.retryTime > now)
1341 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1342 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001343
Vinay Sajip7367d082011-05-02 13:17:27 +01001344
1345@unittest.skipUnless(threading, 'Threading required for this test.')
1346class DatagramHandlerTest(BaseTest):
1347
1348 """Test for DatagramHandler."""
1349
1350 def setUp(self):
1351 """Set up a UDP server to receive log messages, and a DatagramHandler
1352 pointing to that server's address and port."""
1353 BaseTest.setUp(self)
1354 addr = ('localhost', 0)
1355 self.server = server = TestUDPServer(addr, self.handle_datagram,
1356 0.01)
1357 server.start()
1358 server.ready.wait()
1359 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1360 server.port)
1361 self.log_output = ''
1362 self.root_logger.removeHandler(self.root_logger.handlers[0])
1363 self.root_logger.addHandler(self.sock_hdlr)
1364 self.handled = threading.Event()
1365
1366 def tearDown(self):
1367 """Shutdown the UDP server."""
1368 try:
1369 self.server.stop(2.0)
1370 self.root_logger.removeHandler(self.sock_hdlr)
1371 self.sock_hdlr.close()
1372 finally:
1373 BaseTest.tearDown(self)
1374
1375 def handle_datagram(self, request):
1376 slen = struct.pack('>L', 0) # length of prefix
1377 packet = request.packet[len(slen):]
1378 obj = pickle.loads(packet)
1379 record = logging.makeLogRecord(obj)
1380 self.log_output += record.msg + '\n'
1381 self.handled.set()
1382
1383 def test_output(self):
1384 # The log message sent to the DatagramHandler is properly received.
1385 logger = logging.getLogger("udp")
1386 logger.error("spam")
1387 self.handled.wait()
1388 self.assertEqual(self.log_output, "spam\n")
1389
1390
1391@unittest.skipUnless(threading, 'Threading required for this test.')
1392class SysLogHandlerTest(BaseTest):
1393
1394 """Test for SysLogHandler using UDP."""
1395
1396 def setUp(self):
1397 """Set up a UDP server to receive log messages, and a SysLogHandler
1398 pointing to that server's address and port."""
1399 BaseTest.setUp(self)
1400 addr = ('localhost', 0)
1401 self.server = server = TestUDPServer(addr, self.handle_datagram,
1402 0.01)
1403 server.start()
1404 server.ready.wait()
1405 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1406 server.port))
1407 self.log_output = ''
1408 self.root_logger.removeHandler(self.root_logger.handlers[0])
1409 self.root_logger.addHandler(self.sl_hdlr)
1410 self.handled = threading.Event()
1411
1412 def tearDown(self):
1413 """Shutdown the UDP server."""
1414 try:
1415 self.server.stop(2.0)
1416 self.root_logger.removeHandler(self.sl_hdlr)
1417 self.sl_hdlr.close()
1418 finally:
1419 BaseTest.tearDown(self)
1420
1421 def handle_datagram(self, request):
1422 self.log_output = request.packet
1423 self.handled.set()
1424
1425 def test_output(self):
1426 # The log message sent to the SysLogHandler is properly received.
1427 logger = logging.getLogger("slh")
1428 logger.error("sp\xe4m")
1429 self.handled.wait()
1430 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1431
1432
1433@unittest.skipUnless(threading, 'Threading required for this test.')
1434class HTTPHandlerTest(BaseTest):
1435
1436 """Test for HTTPHandler."""
1437
1438 def setUp(self):
1439 """Set up an HTTP server to receive log messages, and a HTTPHandler
1440 pointing to that server's address and port."""
1441 BaseTest.setUp(self)
1442 addr = ('localhost', 0)
1443 self.server = server = TestHTTPServer(addr, self.handle_request,
1444 0.01)
1445 server.start()
1446 server.ready.wait()
1447 host = 'localhost:%d' % server.server_port
1448 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob')
1449 self.log_data = None
1450 self.root_logger.removeHandler(self.root_logger.handlers[0])
1451 self.root_logger.addHandler(self.h_hdlr)
1452 self.handled = threading.Event()
1453
1454 def tearDown(self):
1455 """Shutdown the UDP server."""
1456 try:
1457 self.server.stop(2.0)
1458 self.root_logger.removeHandler(self.h_hdlr)
1459 self.h_hdlr.close()
1460 finally:
1461 BaseTest.tearDown(self)
1462
1463 def handle_request(self, request):
1464 self.log_data = urlparse(request.path)
1465 request.send_response(200)
1466 self.handled.set()
1467
1468 def test_output(self):
1469 # The log message sent to the SysLogHandler is properly received.
1470 logger = logging.getLogger("http")
1471 msg = "sp\xe4m"
1472 logger.error(msg)
1473 self.handled.wait()
1474 self.assertEqual(self.log_data.path, '/frob')
1475 d = parse_qs(self.log_data.query)
1476 self.assertEqual(d['name'], ['http'])
1477 self.assertEqual(d['funcName'], ['test_output'])
1478 self.assertEqual(d['msg'], [msg])
1479
1480
Christian Heimes180510d2008-03-03 19:15:45 +00001481class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001482
Christian Heimes180510d2008-03-03 19:15:45 +00001483 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001484
Christian Heimes180510d2008-03-03 19:15:45 +00001485 def setUp(self):
1486 """Create a dict to remember potentially destroyed objects."""
1487 BaseTest.setUp(self)
1488 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001489
Christian Heimes180510d2008-03-03 19:15:45 +00001490 def _watch_for_survival(self, *args):
1491 """Watch the given objects for survival, by creating weakrefs to
1492 them."""
1493 for obj in args:
1494 key = id(obj), repr(obj)
1495 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001496
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001497 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001498 """Assert that all objects watched for survival have survived."""
1499 # Trigger cycle breaking.
1500 gc.collect()
1501 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001502 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001503 if ref() is None:
1504 dead.append(repr_)
1505 if dead:
1506 self.fail("%d objects should have survived "
1507 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001508
Christian Heimes180510d2008-03-03 19:15:45 +00001509 def test_persistent_loggers(self):
1510 # Logger objects are persistent and retain their configuration, even
1511 # if visible references are destroyed.
1512 self.root_logger.setLevel(logging.INFO)
1513 foo = logging.getLogger("foo")
1514 self._watch_for_survival(foo)
1515 foo.setLevel(logging.DEBUG)
1516 self.root_logger.debug(self.next_message())
1517 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001518 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001519 ('foo', 'DEBUG', '2'),
1520 ])
1521 del foo
1522 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001523 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001524 # foo has retained its settings.
1525 bar = logging.getLogger("foo")
1526 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001527 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001528 ('foo', 'DEBUG', '2'),
1529 ('foo', 'DEBUG', '3'),
1530 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001531
Benjamin Petersonf91df042009-02-13 02:50:59 +00001532
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001533class EncodingTest(BaseTest):
1534 def test_encoding_plain_file(self):
1535 # In Python 2.x, a plain file object is treated as having no encoding.
1536 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001537 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1538 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001539 # the non-ascii data we write to the log.
1540 data = "foo\x80"
1541 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001542 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001543 log.addHandler(handler)
1544 try:
1545 # write non-ascii data to the log.
1546 log.warning(data)
1547 finally:
1548 log.removeHandler(handler)
1549 handler.close()
1550 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001551 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001552 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001553 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001554 finally:
1555 f.close()
1556 finally:
1557 if os.path.isfile(fn):
1558 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001559
Benjamin Petersonf91df042009-02-13 02:50:59 +00001560 def test_encoding_cyrillic_unicode(self):
1561 log = logging.getLogger("test")
1562 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1563 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1564 #Ensure it's written in a Cyrillic encoding
1565 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001566 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001567 stream = io.BytesIO()
1568 writer = writer_class(stream, 'strict')
1569 handler = logging.StreamHandler(writer)
1570 log.addHandler(handler)
1571 try:
1572 log.warning(message)
1573 finally:
1574 log.removeHandler(handler)
1575 handler.close()
1576 # check we wrote exactly those bytes, ignoring trailing \n etc
1577 s = stream.getvalue()
1578 #Compare against what the data should be when encoded in CP-1251
1579 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1580
1581
Georg Brandlf9734072008-12-07 15:30:06 +00001582class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001583
Georg Brandlf9734072008-12-07 15:30:06 +00001584 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001585 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001586 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001587 self.addCleanup(lambda: logging.captureWarnings(False))
1588 warnings.filterwarnings("always", category=UserWarning)
1589 stream = io.StringIO()
1590 h = logging.StreamHandler(stream)
1591 logger = logging.getLogger("py.warnings")
1592 logger.addHandler(h)
1593 warnings.warn("I'm warning you...")
1594 logger.removeHandler(h)
1595 s = stream.getvalue()
1596 h.close()
1597 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001598
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001599 #See if an explicit file uses the original implementation
1600 a_file = io.StringIO()
1601 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1602 a_file, "Dummy line")
1603 s = a_file.getvalue()
1604 a_file.close()
1605 self.assertEqual(s,
1606 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1607
1608 def test_warnings_no_handlers(self):
1609 with warnings.catch_warnings():
1610 logging.captureWarnings(True)
1611 self.addCleanup(lambda: logging.captureWarnings(False))
1612
1613 # confirm our assumption: no loggers are set
1614 logger = logging.getLogger("py.warnings")
1615 assert logger.handlers == []
1616
1617 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1618 self.assertTrue(len(logger.handlers) == 1)
1619 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001620
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001621
1622def formatFunc(format, datefmt=None):
1623 return logging.Formatter(format, datefmt)
1624
1625def handlerFunc():
1626 return logging.StreamHandler()
1627
1628class CustomHandler(logging.StreamHandler):
1629 pass
1630
1631class ConfigDictTest(BaseTest):
1632
1633 """Reading logging config from a dictionary."""
1634
1635 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1636
1637 # config0 is a standard configuration.
1638 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001639 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001640 'formatters': {
1641 'form1' : {
1642 'format' : '%(levelname)s ++ %(message)s',
1643 },
1644 },
1645 'handlers' : {
1646 'hand1' : {
1647 'class' : 'logging.StreamHandler',
1648 'formatter' : 'form1',
1649 'level' : 'NOTSET',
1650 'stream' : 'ext://sys.stdout',
1651 },
1652 },
1653 'root' : {
1654 'level' : 'WARNING',
1655 'handlers' : ['hand1'],
1656 },
1657 }
1658
1659 # config1 adds a little to the standard configuration.
1660 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001661 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001662 'formatters': {
1663 'form1' : {
1664 'format' : '%(levelname)s ++ %(message)s',
1665 },
1666 },
1667 'handlers' : {
1668 'hand1' : {
1669 'class' : 'logging.StreamHandler',
1670 'formatter' : 'form1',
1671 'level' : 'NOTSET',
1672 'stream' : 'ext://sys.stdout',
1673 },
1674 },
1675 'loggers' : {
1676 'compiler.parser' : {
1677 'level' : 'DEBUG',
1678 'handlers' : ['hand1'],
1679 },
1680 },
1681 'root' : {
1682 'level' : 'WARNING',
1683 },
1684 }
1685
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001686 # config1a moves the handler to the root. Used with config8a
1687 config1a = {
1688 'version': 1,
1689 'formatters': {
1690 'form1' : {
1691 'format' : '%(levelname)s ++ %(message)s',
1692 },
1693 },
1694 'handlers' : {
1695 'hand1' : {
1696 'class' : 'logging.StreamHandler',
1697 'formatter' : 'form1',
1698 'level' : 'NOTSET',
1699 'stream' : 'ext://sys.stdout',
1700 },
1701 },
1702 'loggers' : {
1703 'compiler.parser' : {
1704 'level' : 'DEBUG',
1705 },
1706 },
1707 'root' : {
1708 'level' : 'WARNING',
1709 'handlers' : ['hand1'],
1710 },
1711 }
1712
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001713 # config2 has a subtle configuration error that should be reported
1714 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001715 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001716 'formatters': {
1717 'form1' : {
1718 'format' : '%(levelname)s ++ %(message)s',
1719 },
1720 },
1721 'handlers' : {
1722 'hand1' : {
1723 'class' : 'logging.StreamHandler',
1724 'formatter' : 'form1',
1725 'level' : 'NOTSET',
1726 'stream' : 'ext://sys.stdbout',
1727 },
1728 },
1729 'loggers' : {
1730 'compiler.parser' : {
1731 'level' : 'DEBUG',
1732 'handlers' : ['hand1'],
1733 },
1734 },
1735 'root' : {
1736 'level' : 'WARNING',
1737 },
1738 }
1739
1740 #As config1 but with a misspelt level on a handler
1741 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001742 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001743 'formatters': {
1744 'form1' : {
1745 'format' : '%(levelname)s ++ %(message)s',
1746 },
1747 },
1748 'handlers' : {
1749 'hand1' : {
1750 'class' : 'logging.StreamHandler',
1751 'formatter' : 'form1',
1752 'level' : 'NTOSET',
1753 'stream' : 'ext://sys.stdout',
1754 },
1755 },
1756 'loggers' : {
1757 'compiler.parser' : {
1758 'level' : 'DEBUG',
1759 'handlers' : ['hand1'],
1760 },
1761 },
1762 'root' : {
1763 'level' : 'WARNING',
1764 },
1765 }
1766
1767
1768 #As config1 but with a misspelt level on a logger
1769 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001770 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001771 'formatters': {
1772 'form1' : {
1773 'format' : '%(levelname)s ++ %(message)s',
1774 },
1775 },
1776 'handlers' : {
1777 'hand1' : {
1778 'class' : 'logging.StreamHandler',
1779 'formatter' : 'form1',
1780 'level' : 'NOTSET',
1781 'stream' : 'ext://sys.stdout',
1782 },
1783 },
1784 'loggers' : {
1785 'compiler.parser' : {
1786 'level' : 'DEBUG',
1787 'handlers' : ['hand1'],
1788 },
1789 },
1790 'root' : {
1791 'level' : 'WRANING',
1792 },
1793 }
1794
1795 # config3 has a less subtle configuration error
1796 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001797 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001798 'formatters': {
1799 'form1' : {
1800 'format' : '%(levelname)s ++ %(message)s',
1801 },
1802 },
1803 'handlers' : {
1804 'hand1' : {
1805 'class' : 'logging.StreamHandler',
1806 'formatter' : 'misspelled_name',
1807 'level' : 'NOTSET',
1808 'stream' : 'ext://sys.stdout',
1809 },
1810 },
1811 'loggers' : {
1812 'compiler.parser' : {
1813 'level' : 'DEBUG',
1814 'handlers' : ['hand1'],
1815 },
1816 },
1817 'root' : {
1818 'level' : 'WARNING',
1819 },
1820 }
1821
1822 # config4 specifies a custom formatter class to be loaded
1823 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001824 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001825 'formatters': {
1826 'form1' : {
1827 '()' : __name__ + '.ExceptionFormatter',
1828 'format' : '%(levelname)s:%(name)s:%(message)s',
1829 },
1830 },
1831 'handlers' : {
1832 'hand1' : {
1833 'class' : 'logging.StreamHandler',
1834 'formatter' : 'form1',
1835 'level' : 'NOTSET',
1836 'stream' : 'ext://sys.stdout',
1837 },
1838 },
1839 'root' : {
1840 'level' : 'NOTSET',
1841 'handlers' : ['hand1'],
1842 },
1843 }
1844
1845 # As config4 but using an actual callable rather than a string
1846 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001847 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001848 'formatters': {
1849 'form1' : {
1850 '()' : ExceptionFormatter,
1851 'format' : '%(levelname)s:%(name)s:%(message)s',
1852 },
1853 'form2' : {
1854 '()' : __name__ + '.formatFunc',
1855 'format' : '%(levelname)s:%(name)s:%(message)s',
1856 },
1857 'form3' : {
1858 '()' : formatFunc,
1859 'format' : '%(levelname)s:%(name)s:%(message)s',
1860 },
1861 },
1862 'handlers' : {
1863 'hand1' : {
1864 'class' : 'logging.StreamHandler',
1865 'formatter' : 'form1',
1866 'level' : 'NOTSET',
1867 'stream' : 'ext://sys.stdout',
1868 },
1869 'hand2' : {
1870 '()' : handlerFunc,
1871 },
1872 },
1873 'root' : {
1874 'level' : 'NOTSET',
1875 'handlers' : ['hand1'],
1876 },
1877 }
1878
1879 # config5 specifies a custom handler class to be loaded
1880 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001881 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001882 'formatters': {
1883 'form1' : {
1884 'format' : '%(levelname)s ++ %(message)s',
1885 },
1886 },
1887 'handlers' : {
1888 'hand1' : {
1889 'class' : __name__ + '.CustomHandler',
1890 'formatter' : 'form1',
1891 'level' : 'NOTSET',
1892 'stream' : 'ext://sys.stdout',
1893 },
1894 },
1895 'loggers' : {
1896 'compiler.parser' : {
1897 'level' : 'DEBUG',
1898 'handlers' : ['hand1'],
1899 },
1900 },
1901 'root' : {
1902 'level' : 'WARNING',
1903 },
1904 }
1905
1906 # config6 specifies a custom handler class to be loaded
1907 # but has bad arguments
1908 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001909 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001910 'formatters': {
1911 'form1' : {
1912 'format' : '%(levelname)s ++ %(message)s',
1913 },
1914 },
1915 'handlers' : {
1916 'hand1' : {
1917 'class' : __name__ + '.CustomHandler',
1918 'formatter' : 'form1',
1919 'level' : 'NOTSET',
1920 'stream' : 'ext://sys.stdout',
1921 '9' : 'invalid parameter name',
1922 },
1923 },
1924 'loggers' : {
1925 'compiler.parser' : {
1926 'level' : 'DEBUG',
1927 'handlers' : ['hand1'],
1928 },
1929 },
1930 'root' : {
1931 'level' : 'WARNING',
1932 },
1933 }
1934
1935 #config 7 does not define compiler.parser but defines compiler.lexer
1936 #so compiler.parser should be disabled after applying it
1937 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001938 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001939 'formatters': {
1940 'form1' : {
1941 'format' : '%(levelname)s ++ %(message)s',
1942 },
1943 },
1944 'handlers' : {
1945 'hand1' : {
1946 'class' : 'logging.StreamHandler',
1947 'formatter' : 'form1',
1948 'level' : 'NOTSET',
1949 'stream' : 'ext://sys.stdout',
1950 },
1951 },
1952 'loggers' : {
1953 'compiler.lexer' : {
1954 'level' : 'DEBUG',
1955 'handlers' : ['hand1'],
1956 },
1957 },
1958 'root' : {
1959 'level' : 'WARNING',
1960 },
1961 }
1962
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001963 # config8 defines both compiler and compiler.lexer
1964 # so compiler.parser should not be disabled (since
1965 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001966 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001967 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001968 'disable_existing_loggers' : False,
1969 'formatters': {
1970 'form1' : {
1971 'format' : '%(levelname)s ++ %(message)s',
1972 },
1973 },
1974 'handlers' : {
1975 'hand1' : {
1976 'class' : 'logging.StreamHandler',
1977 'formatter' : 'form1',
1978 'level' : 'NOTSET',
1979 'stream' : 'ext://sys.stdout',
1980 },
1981 },
1982 'loggers' : {
1983 'compiler' : {
1984 'level' : 'DEBUG',
1985 'handlers' : ['hand1'],
1986 },
1987 'compiler.lexer' : {
1988 },
1989 },
1990 'root' : {
1991 'level' : 'WARNING',
1992 },
1993 }
1994
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001995 # config8a disables existing loggers
1996 config8a = {
1997 'version': 1,
1998 'disable_existing_loggers' : True,
1999 'formatters': {
2000 'form1' : {
2001 'format' : '%(levelname)s ++ %(message)s',
2002 },
2003 },
2004 'handlers' : {
2005 'hand1' : {
2006 'class' : 'logging.StreamHandler',
2007 'formatter' : 'form1',
2008 'level' : 'NOTSET',
2009 'stream' : 'ext://sys.stdout',
2010 },
2011 },
2012 'loggers' : {
2013 'compiler' : {
2014 'level' : 'DEBUG',
2015 'handlers' : ['hand1'],
2016 },
2017 'compiler.lexer' : {
2018 },
2019 },
2020 'root' : {
2021 'level' : 'WARNING',
2022 },
2023 }
2024
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002025 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002026 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002027 'formatters': {
2028 'form1' : {
2029 'format' : '%(levelname)s ++ %(message)s',
2030 },
2031 },
2032 'handlers' : {
2033 'hand1' : {
2034 'class' : 'logging.StreamHandler',
2035 'formatter' : 'form1',
2036 'level' : 'WARNING',
2037 'stream' : 'ext://sys.stdout',
2038 },
2039 },
2040 'loggers' : {
2041 'compiler.parser' : {
2042 'level' : 'WARNING',
2043 'handlers' : ['hand1'],
2044 },
2045 },
2046 'root' : {
2047 'level' : 'NOTSET',
2048 },
2049 }
2050
2051 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002052 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002053 'incremental' : True,
2054 'handlers' : {
2055 'hand1' : {
2056 'level' : 'WARNING',
2057 },
2058 },
2059 'loggers' : {
2060 'compiler.parser' : {
2061 'level' : 'INFO',
2062 },
2063 },
2064 }
2065
2066 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002067 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002068 'incremental' : True,
2069 'handlers' : {
2070 'hand1' : {
2071 'level' : 'INFO',
2072 },
2073 },
2074 'loggers' : {
2075 'compiler.parser' : {
2076 'level' : 'INFO',
2077 },
2078 },
2079 }
2080
2081 #As config1 but with a filter added
2082 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002083 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002084 'formatters': {
2085 'form1' : {
2086 'format' : '%(levelname)s ++ %(message)s',
2087 },
2088 },
2089 'filters' : {
2090 'filt1' : {
2091 'name' : 'compiler.parser',
2092 },
2093 },
2094 'handlers' : {
2095 'hand1' : {
2096 'class' : 'logging.StreamHandler',
2097 'formatter' : 'form1',
2098 'level' : 'NOTSET',
2099 'stream' : 'ext://sys.stdout',
2100 'filters' : ['filt1'],
2101 },
2102 },
2103 'loggers' : {
2104 'compiler.parser' : {
2105 'level' : 'DEBUG',
2106 'filters' : ['filt1'],
2107 },
2108 },
2109 'root' : {
2110 'level' : 'WARNING',
2111 'handlers' : ['hand1'],
2112 },
2113 }
2114
2115 #As config1 but using cfg:// references
2116 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002117 'version': 1,
2118 'true_formatters': {
2119 'form1' : {
2120 'format' : '%(levelname)s ++ %(message)s',
2121 },
2122 },
2123 'handler_configs': {
2124 'hand1' : {
2125 'class' : 'logging.StreamHandler',
2126 'formatter' : 'form1',
2127 'level' : 'NOTSET',
2128 'stream' : 'ext://sys.stdout',
2129 },
2130 },
2131 'formatters' : 'cfg://true_formatters',
2132 'handlers' : {
2133 'hand1' : 'cfg://handler_configs[hand1]',
2134 },
2135 'loggers' : {
2136 'compiler.parser' : {
2137 'level' : 'DEBUG',
2138 'handlers' : ['hand1'],
2139 },
2140 },
2141 'root' : {
2142 'level' : 'WARNING',
2143 },
2144 }
2145
2146 #As config11 but missing the version key
2147 config12 = {
2148 'true_formatters': {
2149 'form1' : {
2150 'format' : '%(levelname)s ++ %(message)s',
2151 },
2152 },
2153 'handler_configs': {
2154 'hand1' : {
2155 'class' : 'logging.StreamHandler',
2156 'formatter' : 'form1',
2157 'level' : 'NOTSET',
2158 'stream' : 'ext://sys.stdout',
2159 },
2160 },
2161 'formatters' : 'cfg://true_formatters',
2162 'handlers' : {
2163 'hand1' : 'cfg://handler_configs[hand1]',
2164 },
2165 'loggers' : {
2166 'compiler.parser' : {
2167 'level' : 'DEBUG',
2168 'handlers' : ['hand1'],
2169 },
2170 },
2171 'root' : {
2172 'level' : 'WARNING',
2173 },
2174 }
2175
2176 #As config11 but using an unsupported version
2177 config13 = {
2178 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002179 'true_formatters': {
2180 'form1' : {
2181 'format' : '%(levelname)s ++ %(message)s',
2182 },
2183 },
2184 'handler_configs': {
2185 'hand1' : {
2186 'class' : 'logging.StreamHandler',
2187 'formatter' : 'form1',
2188 'level' : 'NOTSET',
2189 'stream' : 'ext://sys.stdout',
2190 },
2191 },
2192 'formatters' : 'cfg://true_formatters',
2193 'handlers' : {
2194 'hand1' : 'cfg://handler_configs[hand1]',
2195 },
2196 'loggers' : {
2197 'compiler.parser' : {
2198 'level' : 'DEBUG',
2199 'handlers' : ['hand1'],
2200 },
2201 },
2202 'root' : {
2203 'level' : 'WARNING',
2204 },
2205 }
2206
2207 def apply_config(self, conf):
2208 logging.config.dictConfig(conf)
2209
2210 def test_config0_ok(self):
2211 # A simple config which overrides the default settings.
2212 with captured_stdout() as output:
2213 self.apply_config(self.config0)
2214 logger = logging.getLogger()
2215 # Won't output anything
2216 logger.info(self.next_message())
2217 # Outputs a message
2218 logger.error(self.next_message())
2219 self.assert_log_lines([
2220 ('ERROR', '2'),
2221 ], stream=output)
2222 # Original logger output is empty.
2223 self.assert_log_lines([])
2224
2225 def test_config1_ok(self, config=config1):
2226 # A config defining a sub-parser as well.
2227 with captured_stdout() as output:
2228 self.apply_config(config)
2229 logger = logging.getLogger("compiler.parser")
2230 # Both will output a message
2231 logger.info(self.next_message())
2232 logger.error(self.next_message())
2233 self.assert_log_lines([
2234 ('INFO', '1'),
2235 ('ERROR', '2'),
2236 ], stream=output)
2237 # Original logger output is empty.
2238 self.assert_log_lines([])
2239
2240 def test_config2_failure(self):
2241 # A simple config which overrides the default settings.
2242 self.assertRaises(Exception, self.apply_config, self.config2)
2243
2244 def test_config2a_failure(self):
2245 # A simple config which overrides the default settings.
2246 self.assertRaises(Exception, self.apply_config, self.config2a)
2247
2248 def test_config2b_failure(self):
2249 # A simple config which overrides the default settings.
2250 self.assertRaises(Exception, self.apply_config, self.config2b)
2251
2252 def test_config3_failure(self):
2253 # A simple config which overrides the default settings.
2254 self.assertRaises(Exception, self.apply_config, self.config3)
2255
2256 def test_config4_ok(self):
2257 # A config specifying a custom formatter class.
2258 with captured_stdout() as output:
2259 self.apply_config(self.config4)
2260 #logger = logging.getLogger()
2261 try:
2262 raise RuntimeError()
2263 except RuntimeError:
2264 logging.exception("just testing")
2265 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002266 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002267 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2268 # Original logger output is empty
2269 self.assert_log_lines([])
2270
2271 def test_config4a_ok(self):
2272 # A config specifying a custom formatter class.
2273 with captured_stdout() as output:
2274 self.apply_config(self.config4a)
2275 #logger = logging.getLogger()
2276 try:
2277 raise RuntimeError()
2278 except RuntimeError:
2279 logging.exception("just testing")
2280 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002281 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002282 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2283 # Original logger output is empty
2284 self.assert_log_lines([])
2285
2286 def test_config5_ok(self):
2287 self.test_config1_ok(config=self.config5)
2288
2289 def test_config6_failure(self):
2290 self.assertRaises(Exception, self.apply_config, self.config6)
2291
2292 def test_config7_ok(self):
2293 with captured_stdout() as output:
2294 self.apply_config(self.config1)
2295 logger = logging.getLogger("compiler.parser")
2296 # Both will output a message
2297 logger.info(self.next_message())
2298 logger.error(self.next_message())
2299 self.assert_log_lines([
2300 ('INFO', '1'),
2301 ('ERROR', '2'),
2302 ], stream=output)
2303 # Original logger output is empty.
2304 self.assert_log_lines([])
2305 with captured_stdout() as output:
2306 self.apply_config(self.config7)
2307 logger = logging.getLogger("compiler.parser")
2308 self.assertTrue(logger.disabled)
2309 logger = logging.getLogger("compiler.lexer")
2310 # Both will output a message
2311 logger.info(self.next_message())
2312 logger.error(self.next_message())
2313 self.assert_log_lines([
2314 ('INFO', '3'),
2315 ('ERROR', '4'),
2316 ], stream=output)
2317 # Original logger output is empty.
2318 self.assert_log_lines([])
2319
2320 #Same as test_config_7_ok but don't disable old loggers.
2321 def test_config_8_ok(self):
2322 with captured_stdout() as output:
2323 self.apply_config(self.config1)
2324 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002325 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002326 logger.info(self.next_message())
2327 logger.error(self.next_message())
2328 self.assert_log_lines([
2329 ('INFO', '1'),
2330 ('ERROR', '2'),
2331 ], stream=output)
2332 # Original logger output is empty.
2333 self.assert_log_lines([])
2334 with captured_stdout() as output:
2335 self.apply_config(self.config8)
2336 logger = logging.getLogger("compiler.parser")
2337 self.assertFalse(logger.disabled)
2338 # Both will output a message
2339 logger.info(self.next_message())
2340 logger.error(self.next_message())
2341 logger = logging.getLogger("compiler.lexer")
2342 # Both will output a message
2343 logger.info(self.next_message())
2344 logger.error(self.next_message())
2345 self.assert_log_lines([
2346 ('INFO', '3'),
2347 ('ERROR', '4'),
2348 ('INFO', '5'),
2349 ('ERROR', '6'),
2350 ], stream=output)
2351 # Original logger output is empty.
2352 self.assert_log_lines([])
2353
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002354 def test_config_8a_ok(self):
2355 with captured_stdout() as output:
2356 self.apply_config(self.config1a)
2357 logger = logging.getLogger("compiler.parser")
2358 # See issue #11424. compiler-hyphenated sorts
2359 # between compiler and compiler.xyz and this
2360 # was preventing compiler.xyz from being included
2361 # in the child loggers of compiler because of an
2362 # overzealous loop termination condition.
2363 hyphenated = logging.getLogger('compiler-hyphenated')
2364 # All will output a message
2365 logger.info(self.next_message())
2366 logger.error(self.next_message())
2367 hyphenated.critical(self.next_message())
2368 self.assert_log_lines([
2369 ('INFO', '1'),
2370 ('ERROR', '2'),
2371 ('CRITICAL', '3'),
2372 ], stream=output)
2373 # Original logger output is empty.
2374 self.assert_log_lines([])
2375 with captured_stdout() as output:
2376 self.apply_config(self.config8a)
2377 logger = logging.getLogger("compiler.parser")
2378 self.assertFalse(logger.disabled)
2379 # Both will output a message
2380 logger.info(self.next_message())
2381 logger.error(self.next_message())
2382 logger = logging.getLogger("compiler.lexer")
2383 # Both will output a message
2384 logger.info(self.next_message())
2385 logger.error(self.next_message())
2386 # Will not appear
2387 hyphenated.critical(self.next_message())
2388 self.assert_log_lines([
2389 ('INFO', '4'),
2390 ('ERROR', '5'),
2391 ('INFO', '6'),
2392 ('ERROR', '7'),
2393 ], stream=output)
2394 # Original logger output is empty.
2395 self.assert_log_lines([])
2396
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002397 def test_config_9_ok(self):
2398 with captured_stdout() as output:
2399 self.apply_config(self.config9)
2400 logger = logging.getLogger("compiler.parser")
2401 #Nothing will be output since both handler and logger are set to WARNING
2402 logger.info(self.next_message())
2403 self.assert_log_lines([], stream=output)
2404 self.apply_config(self.config9a)
2405 #Nothing will be output since both handler is still set to WARNING
2406 logger.info(self.next_message())
2407 self.assert_log_lines([], stream=output)
2408 self.apply_config(self.config9b)
2409 #Message should now be output
2410 logger.info(self.next_message())
2411 self.assert_log_lines([
2412 ('INFO', '3'),
2413 ], stream=output)
2414
2415 def test_config_10_ok(self):
2416 with captured_stdout() as output:
2417 self.apply_config(self.config10)
2418 logger = logging.getLogger("compiler.parser")
2419 logger.warning(self.next_message())
2420 logger = logging.getLogger('compiler')
2421 #Not output, because filtered
2422 logger.warning(self.next_message())
2423 logger = logging.getLogger('compiler.lexer')
2424 #Not output, because filtered
2425 logger.warning(self.next_message())
2426 logger = logging.getLogger("compiler.parser.codegen")
2427 #Output, as not filtered
2428 logger.error(self.next_message())
2429 self.assert_log_lines([
2430 ('WARNING', '1'),
2431 ('ERROR', '4'),
2432 ], stream=output)
2433
2434 def test_config11_ok(self):
2435 self.test_config1_ok(self.config11)
2436
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002437 def test_config12_failure(self):
2438 self.assertRaises(Exception, self.apply_config, self.config12)
2439
2440 def test_config13_failure(self):
2441 self.assertRaises(Exception, self.apply_config, self.config13)
2442
Victor Stinner45df8202010-04-28 22:31:17 +00002443 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002444 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002445 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002446 # Ask for a randomly assigned port (by using port 0)
2447 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002448 t.start()
2449 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002450 # Now get the port allocated
2451 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002452 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002453 try:
2454 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2455 sock.settimeout(2.0)
2456 sock.connect(('localhost', port))
2457
2458 slen = struct.pack('>L', len(text))
2459 s = slen + text
2460 sentsofar = 0
2461 left = len(s)
2462 while left > 0:
2463 sent = sock.send(s[sentsofar:])
2464 sentsofar += sent
2465 left -= sent
2466 sock.close()
2467 finally:
2468 t.ready.wait(2.0)
2469 logging.config.stopListening()
2470 t.join(2.0)
2471
2472 def test_listen_config_10_ok(self):
2473 with captured_stdout() as output:
2474 self.setup_via_listener(json.dumps(self.config10))
2475 logger = logging.getLogger("compiler.parser")
2476 logger.warning(self.next_message())
2477 logger = logging.getLogger('compiler')
2478 #Not output, because filtered
2479 logger.warning(self.next_message())
2480 logger = logging.getLogger('compiler.lexer')
2481 #Not output, because filtered
2482 logger.warning(self.next_message())
2483 logger = logging.getLogger("compiler.parser.codegen")
2484 #Output, as not filtered
2485 logger.error(self.next_message())
2486 self.assert_log_lines([
2487 ('WARNING', '1'),
2488 ('ERROR', '4'),
2489 ], stream=output)
2490
2491 def test_listen_config_1_ok(self):
2492 with captured_stdout() as output:
2493 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2494 logger = logging.getLogger("compiler.parser")
2495 # Both will output a message
2496 logger.info(self.next_message())
2497 logger.error(self.next_message())
2498 self.assert_log_lines([
2499 ('INFO', '1'),
2500 ('ERROR', '2'),
2501 ], stream=output)
2502 # Original logger output is empty.
2503 self.assert_log_lines([])
2504
Vinay Sajip373baef2011-04-26 20:05:24 +01002505 def test_baseconfig(self):
2506 d = {
2507 'atuple': (1, 2, 3),
2508 'alist': ['a', 'b', 'c'],
2509 'adict': {'d': 'e', 'f': 3 },
2510 'nest1': ('g', ('h', 'i'), 'j'),
2511 'nest2': ['k', ['l', 'm'], 'n'],
2512 'nest3': ['o', 'cfg://alist', 'p'],
2513 }
2514 bc = logging.config.BaseConfigurator(d)
2515 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2516 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2517 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2518 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2519 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2520 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2521 v = bc.convert('cfg://nest3')
2522 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2523 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2524 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2525 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002526
2527class ManagerTest(BaseTest):
2528 def test_manager_loggerclass(self):
2529 logged = []
2530
2531 class MyLogger(logging.Logger):
2532 def _log(self, level, msg, args, exc_info=None, extra=None):
2533 logged.append(msg)
2534
2535 man = logging.Manager(None)
2536 self.assertRaises(TypeError, man.setLoggerClass, int)
2537 man.setLoggerClass(MyLogger)
2538 logger = man.getLogger('test')
2539 logger.warning('should appear in logged')
2540 logging.warning('should not appear in logged')
2541
2542 self.assertEqual(logged, ['should appear in logged'])
2543
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002544 def test_set_log_record_factory(self):
2545 man = logging.Manager(None)
2546 expected = object()
2547 man.setLogRecordFactory(expected)
2548 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002549
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002550class ChildLoggerTest(BaseTest):
2551 def test_child_loggers(self):
2552 r = logging.getLogger()
2553 l1 = logging.getLogger('abc')
2554 l2 = logging.getLogger('def.ghi')
2555 c1 = r.getChild('xyz')
2556 c2 = r.getChild('uvw.xyz')
2557 self.assertTrue(c1 is logging.getLogger('xyz'))
2558 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2559 c1 = l1.getChild('def')
2560 c2 = c1.getChild('ghi')
2561 c3 = l1.getChild('def.ghi')
2562 self.assertTrue(c1 is logging.getLogger('abc.def'))
2563 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2564 self.assertTrue(c2 is c3)
2565
2566
Vinay Sajip6fac8172010-10-19 20:44:14 +00002567class DerivedLogRecord(logging.LogRecord):
2568 pass
2569
Vinay Sajip61561522010-12-03 11:50:38 +00002570class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002571
2572 def setUp(self):
2573 class CheckingFilter(logging.Filter):
2574 def __init__(self, cls):
2575 self.cls = cls
2576
2577 def filter(self, record):
2578 t = type(record)
2579 if t is not self.cls:
2580 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2581 self.cls)
2582 raise TypeError(msg)
2583 return True
2584
2585 BaseTest.setUp(self)
2586 self.filter = CheckingFilter(DerivedLogRecord)
2587 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002588 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002589
2590 def tearDown(self):
2591 self.root_logger.removeFilter(self.filter)
2592 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002593 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002594
2595 def test_logrecord_class(self):
2596 self.assertRaises(TypeError, self.root_logger.warning,
2597 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002598 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002599 self.root_logger.error(self.next_message())
2600 self.assert_log_lines([
2601 ('root', 'ERROR', '2'),
2602 ])
2603
2604
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002605class QueueHandlerTest(BaseTest):
2606 # Do not bother with a logger name group.
2607 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2608
2609 def setUp(self):
2610 BaseTest.setUp(self)
2611 self.queue = queue.Queue(-1)
2612 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2613 self.que_logger = logging.getLogger('que')
2614 self.que_logger.propagate = False
2615 self.que_logger.setLevel(logging.WARNING)
2616 self.que_logger.addHandler(self.que_hdlr)
2617
2618 def tearDown(self):
2619 self.que_hdlr.close()
2620 BaseTest.tearDown(self)
2621
2622 def test_queue_handler(self):
2623 self.que_logger.debug(self.next_message())
2624 self.assertRaises(queue.Empty, self.queue.get_nowait)
2625 self.que_logger.info(self.next_message())
2626 self.assertRaises(queue.Empty, self.queue.get_nowait)
2627 msg = self.next_message()
2628 self.que_logger.warning(msg)
2629 data = self.queue.get_nowait()
2630 self.assertTrue(isinstance(data, logging.LogRecord))
2631 self.assertEqual(data.name, self.que_logger.name)
2632 self.assertEqual((data.msg, data.args), (msg, None))
2633
Vinay Sajipe723e962011-04-15 22:27:17 +01002634 def test_queue_listener(self):
2635 handler = TestHandler(Matcher())
2636 listener = logging.handlers.QueueListener(self.queue, handler)
2637 listener.start()
2638 try:
2639 self.que_logger.warning(self.next_message())
2640 self.que_logger.error(self.next_message())
2641 self.que_logger.critical(self.next_message())
2642 finally:
2643 listener.stop()
2644 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2645 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2646 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2647
Vinay Sajip37eb3382011-04-26 20:26:41 +01002648ZERO = datetime.timedelta(0)
2649
2650class UTC(datetime.tzinfo):
2651 def utcoffset(self, dt):
2652 return ZERO
2653
2654 dst = utcoffset
2655
2656 def tzname(self, dt):
2657 return 'UTC'
2658
2659utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002660
Vinay Sajipa39c5712010-10-25 13:57:39 +00002661class FormatterTest(unittest.TestCase):
2662 def setUp(self):
2663 self.common = {
2664 'name': 'formatter.test',
2665 'level': logging.DEBUG,
2666 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2667 'lineno': 42,
2668 'exc_info': None,
2669 'func': None,
2670 'msg': 'Message with %d %s',
2671 'args': (2, 'placeholders'),
2672 }
2673 self.variants = {
2674 }
2675
2676 def get_record(self, name=None):
2677 result = dict(self.common)
2678 if name is not None:
2679 result.update(self.variants[name])
2680 return logging.makeLogRecord(result)
2681
2682 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002683 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002684 r = self.get_record()
2685 f = logging.Formatter('${%(message)s}')
2686 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2687 f = logging.Formatter('%(random)s')
2688 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002689 self.assertFalse(f.usesTime())
2690 f = logging.Formatter('%(asctime)s')
2691 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002692 f = logging.Formatter('%(asctime)-15s')
2693 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002694 f = logging.Formatter('asctime')
2695 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002696
2697 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002698 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002699 r = self.get_record()
2700 f = logging.Formatter('$%{message}%$', style='{')
2701 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2702 f = logging.Formatter('{random}', style='{')
2703 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002704 self.assertFalse(f.usesTime())
2705 f = logging.Formatter('{asctime}', style='{')
2706 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002707 f = logging.Formatter('{asctime!s:15}', style='{')
2708 self.assertTrue(f.usesTime())
2709 f = logging.Formatter('{asctime:15}', style='{')
2710 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002711 f = logging.Formatter('asctime', style='{')
2712 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002713
2714 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002715 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002716 r = self.get_record()
2717 f = logging.Formatter('$message', style='$')
2718 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2719 f = logging.Formatter('$$%${message}%$$', style='$')
2720 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2721 f = logging.Formatter('${random}', style='$')
2722 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002723 self.assertFalse(f.usesTime())
2724 f = logging.Formatter('${asctime}', style='$')
2725 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002726 f = logging.Formatter('${asctime', style='$')
2727 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002728 f = logging.Formatter('$asctime', style='$')
2729 self.assertTrue(f.usesTime())
2730 f = logging.Formatter('asctime', style='$')
2731 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002732
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002733 def test_invalid_style(self):
2734 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2735
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002736 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002737 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002738 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002739 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002740 r.msecs = 123
2741 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002742 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002743 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2744 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002745 f.format(r)
2746 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2747
2748class TestBufferingFormatter(logging.BufferingFormatter):
2749 def formatHeader(self, records):
2750 return '[(%d)' % len(records)
2751
2752 def formatFooter(self, records):
2753 return '(%d)]' % len(records)
2754
2755class BufferingFormatterTest(unittest.TestCase):
2756 def setUp(self):
2757 self.records = [
2758 logging.makeLogRecord({'msg': 'one'}),
2759 logging.makeLogRecord({'msg': 'two'}),
2760 ]
2761
2762 def test_default(self):
2763 f = logging.BufferingFormatter()
2764 self.assertEqual('', f.format([]))
2765 self.assertEqual('onetwo', f.format(self.records))
2766
2767 def test_custom(self):
2768 f = TestBufferingFormatter()
2769 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2770 lf = logging.Formatter('<%(message)s>')
2771 f = TestBufferingFormatter(lf)
2772 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002773
2774class ExceptionTest(BaseTest):
2775 def test_formatting(self):
2776 r = self.root_logger
2777 h = RecordingHandler()
2778 r.addHandler(h)
2779 try:
2780 raise RuntimeError('deliberate mistake')
2781 except:
2782 logging.exception('failed', stack_info=True)
2783 r.removeHandler(h)
2784 h.close()
2785 r = h.records[0]
2786 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2787 'call last):\n'))
2788 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2789 'deliberate mistake'))
2790 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2791 'call last):\n'))
2792 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2793 'stack_info=True)'))
2794
2795
Vinay Sajip5a27d402010-12-10 11:42:57 +00002796class LastResortTest(BaseTest):
2797 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002798 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002799 root = self.root_logger
2800 root.removeHandler(self.root_hdlr)
2801 old_stderr = sys.stderr
2802 old_lastresort = logging.lastResort
2803 old_raise_exceptions = logging.raiseExceptions
2804 try:
2805 sys.stderr = sio = io.StringIO()
2806 root.warning('This is your final chance!')
2807 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2808 #No handlers and no last resort, so 'No handlers' message
2809 logging.lastResort = None
2810 sys.stderr = sio = io.StringIO()
2811 root.warning('This is your final chance!')
2812 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2813 # 'No handlers' message only printed once
2814 sys.stderr = sio = io.StringIO()
2815 root.warning('This is your final chance!')
2816 self.assertEqual(sio.getvalue(), '')
2817 root.manager.emittedNoHandlerWarning = False
2818 #If raiseExceptions is False, no message is printed
2819 logging.raiseExceptions = False
2820 sys.stderr = sio = io.StringIO()
2821 root.warning('This is your final chance!')
2822 self.assertEqual(sio.getvalue(), '')
2823 finally:
2824 sys.stderr = old_stderr
2825 root.addHandler(self.root_hdlr)
2826 logging.lastResort = old_lastresort
2827 logging.raiseExceptions = old_raise_exceptions
2828
2829
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002830class FakeHandler:
2831
2832 def __init__(self, identifier, called):
2833 for method in ('acquire', 'flush', 'close', 'release'):
2834 setattr(self, method, self.record_call(identifier, method, called))
2835
2836 def record_call(self, identifier, method_name, called):
2837 def inner():
2838 called.append('{} - {}'.format(identifier, method_name))
2839 return inner
2840
2841
2842class RecordingHandler(logging.NullHandler):
2843
2844 def __init__(self, *args, **kwargs):
2845 super(RecordingHandler, self).__init__(*args, **kwargs)
2846 self.records = []
2847
2848 def handle(self, record):
2849 """Keep track of all the emitted records."""
2850 self.records.append(record)
2851
2852
2853class ShutdownTest(BaseTest):
2854
Vinay Sajip5e66b162011-04-20 15:41:14 +01002855 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002856
2857 def setUp(self):
2858 super(ShutdownTest, self).setUp()
2859 self.called = []
2860
2861 raise_exceptions = logging.raiseExceptions
2862 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2863
2864 def raise_error(self, error):
2865 def inner():
2866 raise error()
2867 return inner
2868
2869 def test_no_failure(self):
2870 # create some fake handlers
2871 handler0 = FakeHandler(0, self.called)
2872 handler1 = FakeHandler(1, self.called)
2873 handler2 = FakeHandler(2, self.called)
2874
2875 # create live weakref to those handlers
2876 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2877
2878 logging.shutdown(handlerList=list(handlers))
2879
2880 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2881 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2882 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2883 self.assertEqual(expected, self.called)
2884
2885 def _test_with_failure_in_method(self, method, error):
2886 handler = FakeHandler(0, self.called)
2887 setattr(handler, method, self.raise_error(error))
2888 handlers = [logging.weakref.ref(handler)]
2889
2890 logging.shutdown(handlerList=list(handlers))
2891
2892 self.assertEqual('0 - release', self.called[-1])
2893
2894 def test_with_ioerror_in_acquire(self):
2895 self._test_with_failure_in_method('acquire', IOError)
2896
2897 def test_with_ioerror_in_flush(self):
2898 self._test_with_failure_in_method('flush', IOError)
2899
2900 def test_with_ioerror_in_close(self):
2901 self._test_with_failure_in_method('close', IOError)
2902
2903 def test_with_valueerror_in_acquire(self):
2904 self._test_with_failure_in_method('acquire', ValueError)
2905
2906 def test_with_valueerror_in_flush(self):
2907 self._test_with_failure_in_method('flush', ValueError)
2908
2909 def test_with_valueerror_in_close(self):
2910 self._test_with_failure_in_method('close', ValueError)
2911
2912 def test_with_other_error_in_acquire_without_raise(self):
2913 logging.raiseExceptions = False
2914 self._test_with_failure_in_method('acquire', IndexError)
2915
2916 def test_with_other_error_in_flush_without_raise(self):
2917 logging.raiseExceptions = False
2918 self._test_with_failure_in_method('flush', IndexError)
2919
2920 def test_with_other_error_in_close_without_raise(self):
2921 logging.raiseExceptions = False
2922 self._test_with_failure_in_method('close', IndexError)
2923
2924 def test_with_other_error_in_acquire_with_raise(self):
2925 logging.raiseExceptions = True
2926 self.assertRaises(IndexError, self._test_with_failure_in_method,
2927 'acquire', IndexError)
2928
2929 def test_with_other_error_in_flush_with_raise(self):
2930 logging.raiseExceptions = True
2931 self.assertRaises(IndexError, self._test_with_failure_in_method,
2932 'flush', IndexError)
2933
2934 def test_with_other_error_in_close_with_raise(self):
2935 logging.raiseExceptions = True
2936 self.assertRaises(IndexError, self._test_with_failure_in_method,
2937 'close', IndexError)
2938
2939
2940class ModuleLevelMiscTest(BaseTest):
2941
Vinay Sajip5e66b162011-04-20 15:41:14 +01002942 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002943
2944 def test_disable(self):
2945 old_disable = logging.root.manager.disable
2946 # confirm our assumptions are correct
2947 assert old_disable == 0
2948 self.addCleanup(lambda: logging.disable(old_disable))
2949
2950 logging.disable(83)
2951 self.assertEqual(logging.root.manager.disable, 83)
2952
2953 def _test_log(self, method, level=None):
2954 called = []
2955 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002956 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002957
2958 recording = RecordingHandler()
2959 logging.root.addHandler(recording)
2960
2961 log_method = getattr(logging, method)
2962 if level is not None:
2963 log_method(level, "test me: %r", recording)
2964 else:
2965 log_method("test me: %r", recording)
2966
2967 self.assertEqual(len(recording.records), 1)
2968 record = recording.records[0]
2969 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2970
2971 expected_level = level if level is not None else getattr(logging, method.upper())
2972 self.assertEqual(record.levelno, expected_level)
2973
2974 # basicConfig was not called!
2975 self.assertEqual(called, [])
2976
2977 def test_log(self):
2978 self._test_log('log', logging.ERROR)
2979
2980 def test_debug(self):
2981 self._test_log('debug')
2982
2983 def test_info(self):
2984 self._test_log('info')
2985
2986 def test_warning(self):
2987 self._test_log('warning')
2988
2989 def test_error(self):
2990 self._test_log('error')
2991
2992 def test_critical(self):
2993 self._test_log('critical')
2994
2995 def test_set_logger_class(self):
2996 self.assertRaises(TypeError, logging.setLoggerClass, object)
2997
2998 class MyLogger(logging.Logger):
2999 pass
3000
3001 logging.setLoggerClass(MyLogger)
3002 self.assertEqual(logging.getLoggerClass(), MyLogger)
3003
3004 logging.setLoggerClass(logging.Logger)
3005 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3006
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003007class LogRecordTest(BaseTest):
3008 def test_str_rep(self):
3009 r = logging.makeLogRecord({})
3010 s = str(r)
3011 self.assertTrue(s.startswith('<LogRecord: '))
3012 self.assertTrue(s.endswith('>'))
3013
3014 def test_dict_arg(self):
3015 h = RecordingHandler()
3016 r = logging.getLogger()
3017 r.addHandler(h)
3018 d = {'less' : 'more' }
3019 logging.warning('less is %(less)s', d)
3020 self.assertIs(h.records[0].args, d)
3021 self.assertEqual(h.records[0].message, 'less is more')
3022 r.removeHandler(h)
3023 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003024
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003025 def test_multiprocessing(self):
3026 r = logging.makeLogRecord({})
3027 self.assertEqual(r.processName, 'MainProcess')
3028 import multiprocessing as mp
3029 r = logging.makeLogRecord({})
3030 self.assertEqual(r.processName, mp.current_process().name)
3031
3032 def test_optional(self):
3033 r = logging.makeLogRecord({})
3034 NOT_NONE = self.assertIsNotNone
3035 NOT_NONE(r.thread)
3036 NOT_NONE(r.threadName)
3037 NOT_NONE(r.process)
3038 NOT_NONE(r.processName)
3039 log_threads = logging.logThreads
3040 log_processes = logging.logProcesses
3041 log_multiprocessing = logging.logMultiprocessing
3042 try:
3043 logging.logThreads = False
3044 logging.logProcesses = False
3045 logging.logMultiprocessing = False
3046 r = logging.makeLogRecord({})
3047 NONE = self.assertIsNone
3048 NONE(r.thread)
3049 NONE(r.threadName)
3050 NONE(r.process)
3051 NONE(r.processName)
3052 finally:
3053 logging.logThreads = log_threads
3054 logging.logProcesses = log_processes
3055 logging.logMultiprocessing = log_multiprocessing
3056
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003057class BasicConfigTest(unittest.TestCase):
3058
Vinay Sajip95bf5042011-04-20 11:50:56 +01003059 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003060
3061 def setUp(self):
3062 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003063 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003064 self.saved_handlers = logging._handlers.copy()
3065 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003066 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003067 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003068 logging.root.handlers = []
3069
3070 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003071 for h in logging.root.handlers[:]:
3072 logging.root.removeHandler(h)
3073 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003074 super(BasicConfigTest, self).tearDown()
3075
Vinay Sajip3def7e02011-04-20 10:58:06 +01003076 def cleanup(self):
3077 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003078 logging._handlers.clear()
3079 logging._handlers.update(self.saved_handlers)
3080 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003081 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003082
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003083 def test_no_kwargs(self):
3084 logging.basicConfig()
3085
3086 # handler defaults to a StreamHandler to sys.stderr
3087 self.assertEqual(len(logging.root.handlers), 1)
3088 handler = logging.root.handlers[0]
3089 self.assertIsInstance(handler, logging.StreamHandler)
3090 self.assertEqual(handler.stream, sys.stderr)
3091
3092 formatter = handler.formatter
3093 # format defaults to logging.BASIC_FORMAT
3094 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3095 # datefmt defaults to None
3096 self.assertIsNone(formatter.datefmt)
3097 # style defaults to %
3098 self.assertIsInstance(formatter._style, logging.PercentStyle)
3099
3100 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003101 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003102
3103 def test_filename(self):
3104 logging.basicConfig(filename='test.log')
3105
3106 self.assertEqual(len(logging.root.handlers), 1)
3107 handler = logging.root.handlers[0]
3108 self.assertIsInstance(handler, logging.FileHandler)
3109
3110 expected = logging.FileHandler('test.log', 'a')
3111 self.addCleanup(expected.close)
3112 self.assertEqual(handler.stream.mode, expected.stream.mode)
3113 self.assertEqual(handler.stream.name, expected.stream.name)
3114
3115 def test_filemode(self):
3116 logging.basicConfig(filename='test.log', filemode='wb')
3117
3118 handler = logging.root.handlers[0]
3119 expected = logging.FileHandler('test.log', 'wb')
3120 self.addCleanup(expected.close)
3121 self.assertEqual(handler.stream.mode, expected.stream.mode)
3122
3123 def test_stream(self):
3124 stream = io.StringIO()
3125 self.addCleanup(stream.close)
3126 logging.basicConfig(stream=stream)
3127
3128 self.assertEqual(len(logging.root.handlers), 1)
3129 handler = logging.root.handlers[0]
3130 self.assertIsInstance(handler, logging.StreamHandler)
3131 self.assertEqual(handler.stream, stream)
3132
3133 def test_format(self):
3134 logging.basicConfig(format='foo')
3135
3136 formatter = logging.root.handlers[0].formatter
3137 self.assertEqual(formatter._style._fmt, 'foo')
3138
3139 def test_datefmt(self):
3140 logging.basicConfig(datefmt='bar')
3141
3142 formatter = logging.root.handlers[0].formatter
3143 self.assertEqual(formatter.datefmt, 'bar')
3144
3145 def test_style(self):
3146 logging.basicConfig(style='$')
3147
3148 formatter = logging.root.handlers[0].formatter
3149 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3150
3151 def test_level(self):
3152 old_level = logging.root.level
3153 self.addCleanup(lambda: logging.root.setLevel(old_level))
3154
3155 logging.basicConfig(level=57)
3156 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003157 # Test that second call has no effect
3158 logging.basicConfig(level=58)
3159 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003160
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003161 def test_incompatible(self):
3162 assertRaises = self.assertRaises
3163 handlers = [logging.StreamHandler()]
3164 stream = sys.stderr
3165 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3166 stream=stream)
3167 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3168 handlers=handlers)
3169 assertRaises(ValueError, logging.basicConfig, stream=stream,
3170 handlers=handlers)
3171
3172 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003173 handlers = [
3174 logging.StreamHandler(),
3175 logging.StreamHandler(sys.stdout),
3176 logging.StreamHandler(),
3177 ]
3178 f = logging.Formatter()
3179 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003180 logging.basicConfig(handlers=handlers)
3181 self.assertIs(handlers[0], logging.root.handlers[0])
3182 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003183 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003184 self.assertIsNotNone(handlers[0].formatter)
3185 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003186 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003187 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3188
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003189 def _test_log(self, method, level=None):
3190 # logging.root has no handlers so basicConfig should be called
3191 called = []
3192
3193 old_basic_config = logging.basicConfig
3194 def my_basic_config(*a, **kw):
3195 old_basic_config()
3196 old_level = logging.root.level
3197 logging.root.setLevel(100) # avoid having messages in stderr
3198 self.addCleanup(lambda: logging.root.setLevel(old_level))
3199 called.append((a, kw))
3200
3201 patch(self, logging, 'basicConfig', my_basic_config)
3202
3203 log_method = getattr(logging, method)
3204 if level is not None:
3205 log_method(level, "test me")
3206 else:
3207 log_method("test me")
3208
3209 # basicConfig was called with no arguments
3210 self.assertEqual(called, [((), {})])
3211
3212 def test_log(self):
3213 self._test_log('log', logging.WARNING)
3214
3215 def test_debug(self):
3216 self._test_log('debug')
3217
3218 def test_info(self):
3219 self._test_log('info')
3220
3221 def test_warning(self):
3222 self._test_log('warning')
3223
3224 def test_error(self):
3225 self._test_log('error')
3226
3227 def test_critical(self):
3228 self._test_log('critical')
3229
3230
3231class LoggerAdapterTest(unittest.TestCase):
3232
3233 def setUp(self):
3234 super(LoggerAdapterTest, self).setUp()
3235 old_handler_list = logging._handlerList[:]
3236
3237 self.recording = RecordingHandler()
3238 self.logger = logging.root
3239 self.logger.addHandler(self.recording)
3240 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3241 self.addCleanup(self.recording.close)
3242
3243 def cleanup():
3244 logging._handlerList[:] = old_handler_list
3245
3246 self.addCleanup(cleanup)
3247 self.addCleanup(logging.shutdown)
3248 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3249
3250 def test_exception(self):
3251 msg = 'testing exception: %r'
3252 exc = None
3253 try:
3254 assert False
3255 except AssertionError as e:
3256 exc = e
3257 self.adapter.exception(msg, self.recording)
3258
3259 self.assertEqual(len(self.recording.records), 1)
3260 record = self.recording.records[0]
3261 self.assertEqual(record.levelno, logging.ERROR)
3262 self.assertEqual(record.msg, msg)
3263 self.assertEqual(record.args, (self.recording,))
3264 self.assertEqual(record.exc_info,
3265 (exc.__class__, exc, exc.__traceback__))
3266
3267 def test_critical(self):
3268 msg = 'critical test! %r'
3269 self.adapter.critical(msg, self.recording)
3270
3271 self.assertEqual(len(self.recording.records), 1)
3272 record = self.recording.records[0]
3273 self.assertEqual(record.levelno, logging.CRITICAL)
3274 self.assertEqual(record.msg, msg)
3275 self.assertEqual(record.args, (self.recording,))
3276
3277 def test_is_enabled_for(self):
3278 old_disable = self.adapter.logger.manager.disable
3279 self.adapter.logger.manager.disable = 33
3280 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3281 'disable', old_disable))
3282 self.assertFalse(self.adapter.isEnabledFor(32))
3283
3284 def test_has_handlers(self):
3285 self.assertTrue(self.adapter.hasHandlers())
3286
3287 for handler in self.logger.handlers:
3288 self.logger.removeHandler(handler)
3289 assert not self.logger.hasHandlers()
3290
3291 self.assertFalse(self.adapter.hasHandlers())
3292
3293
3294class LoggerTest(BaseTest):
3295
3296 def setUp(self):
3297 super(LoggerTest, self).setUp()
3298 self.recording = RecordingHandler()
3299 self.logger = logging.Logger(name='blah')
3300 self.logger.addHandler(self.recording)
3301 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3302 self.addCleanup(self.recording.close)
3303 self.addCleanup(logging.shutdown)
3304
3305 def test_set_invalid_level(self):
3306 self.assertRaises(TypeError, self.logger.setLevel, object())
3307
3308 def test_exception(self):
3309 msg = 'testing exception: %r'
3310 exc = None
3311 try:
3312 assert False
3313 except AssertionError as e:
3314 exc = e
3315 self.logger.exception(msg, self.recording)
3316
3317 self.assertEqual(len(self.recording.records), 1)
3318 record = self.recording.records[0]
3319 self.assertEqual(record.levelno, logging.ERROR)
3320 self.assertEqual(record.msg, msg)
3321 self.assertEqual(record.args, (self.recording,))
3322 self.assertEqual(record.exc_info,
3323 (exc.__class__, exc, exc.__traceback__))
3324
3325 def test_log_invalid_level_with_raise(self):
3326 old_raise = logging.raiseExceptions
3327 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3328
3329 logging.raiseExceptions = True
3330 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3331
3332 def test_log_invalid_level_no_raise(self):
3333 old_raise = logging.raiseExceptions
3334 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3335
3336 logging.raiseExceptions = False
3337 self.logger.log('10', 'test message') # no exception happens
3338
3339 def test_find_caller_with_stack_info(self):
3340 called = []
3341 patch(self, logging.traceback, 'print_stack',
3342 lambda f, file: called.append(file.getvalue()))
3343
3344 self.logger.findCaller(stack_info=True)
3345
3346 self.assertEqual(len(called), 1)
3347 self.assertEqual('Stack (most recent call last):\n', called[0])
3348
3349 def test_make_record_with_extra_overwrite(self):
3350 name = 'my record'
3351 level = 13
3352 fn = lno = msg = args = exc_info = func = sinfo = None
3353 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3354 exc_info, func, sinfo)
3355
3356 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3357 extra = {key: 'some value'}
3358 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3359 fn, lno, msg, args, exc_info,
3360 extra=extra, sinfo=sinfo)
3361
3362 def test_make_record_with_extra_no_overwrite(self):
3363 name = 'my record'
3364 level = 13
3365 fn = lno = msg = args = exc_info = func = sinfo = None
3366 extra = {'valid_key': 'some value'}
3367 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3368 exc_info, extra=extra, sinfo=sinfo)
3369 self.assertIn('valid_key', result.__dict__)
3370
3371 def test_has_handlers(self):
3372 self.assertTrue(self.logger.hasHandlers())
3373
3374 for handler in self.logger.handlers:
3375 self.logger.removeHandler(handler)
3376 assert not self.logger.hasHandlers()
3377
3378 self.assertFalse(self.logger.hasHandlers())
3379
3380 def test_has_handlers_no_propagate(self):
3381 child_logger = logging.getLogger('blah.child')
3382 child_logger.propagate = False
3383 assert child_logger.handlers == []
3384
3385 self.assertFalse(child_logger.hasHandlers())
3386
3387 def test_is_enabled_for(self):
3388 old_disable = self.logger.manager.disable
3389 self.logger.manager.disable = 23
3390 self.addCleanup(lambda: setattr(self.logger.manager,
3391 'disable', old_disable))
3392 self.assertFalse(self.logger.isEnabledFor(22))
3393
3394
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003395class BaseFileTest(BaseTest):
3396 "Base class for handler tests that write log files"
3397
3398 def setUp(self):
3399 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003400 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3401 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003402 self.rmfiles = []
3403
3404 def tearDown(self):
3405 for fn in self.rmfiles:
3406 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003407 if os.path.exists(self.fn):
3408 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003409 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003410
3411 def assertLogFile(self, filename):
3412 "Assert a log file is there and register it for deletion"
3413 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003414 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003415 self.rmfiles.append(filename)
3416
3417
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003418class FileHandlerTest(BaseFileTest):
3419 def test_delay(self):
3420 os.unlink(self.fn)
3421 fh = logging.FileHandler(self.fn, delay=True)
3422 self.assertIsNone(fh.stream)
3423 self.assertFalse(os.path.exists(self.fn))
3424 fh.handle(logging.makeLogRecord({}))
3425 self.assertIsNotNone(fh.stream)
3426 self.assertTrue(os.path.exists(self.fn))
3427 fh.close()
3428
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003429class RotatingFileHandlerTest(BaseFileTest):
3430 def next_rec(self):
3431 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3432 self.next_message(), None, None, None)
3433
3434 def test_should_not_rollover(self):
3435 # If maxbytes is zero rollover never occurs
3436 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3437 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003438 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003439
3440 def test_should_rollover(self):
3441 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3442 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003443 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003444
3445 def test_file_created(self):
3446 # checks that the file is created and assumes it was created
3447 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003448 rh = logging.handlers.RotatingFileHandler(self.fn)
3449 rh.emit(self.next_rec())
3450 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003451 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003452
3453 def test_rollover_filenames(self):
3454 rh = logging.handlers.RotatingFileHandler(
3455 self.fn, backupCount=2, maxBytes=1)
3456 rh.emit(self.next_rec())
3457 self.assertLogFile(self.fn)
3458 rh.emit(self.next_rec())
3459 self.assertLogFile(self.fn + ".1")
3460 rh.emit(self.next_rec())
3461 self.assertLogFile(self.fn + ".2")
3462 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003463 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003464
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003465class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003466 # other test methods added below
3467 def test_rollover(self):
3468 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S')
3469 r = logging.makeLogRecord({'msg': 'testing'})
3470 fh.emit(r)
3471 self.assertLogFile(self.fn)
3472 time.sleep(1.0)
3473 fh.emit(r)
3474 now = datetime.datetime.now()
3475 prevsec = now - datetime.timedelta(seconds=1)
3476 suffix = prevsec.strftime(".%Y-%m-%d_%H-%M-%S")
3477 self.assertLogFile(self.fn + suffix)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003478
3479def secs(**kw):
3480 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3481
3482for when, exp in (('S', 1),
3483 ('M', 60),
3484 ('H', 60 * 60),
3485 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003486 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003487 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003488 ('W0', secs(days=4, hours=24)),
3489 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003490 def test_compute_rollover(self, when=when, exp=exp):
3491 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003492 self.fn, when=when, interval=1, backupCount=0, utc=True)
3493 currentTime = 0.0
3494 actual = rh.computeRollover(currentTime)
3495 if exp != actual:
3496 # Failures occur on some systems for MIDNIGHT and W0.
3497 # Print detailed calculation for MIDNIGHT so we can try to see
3498 # what's going on
3499 import time
3500 if when == 'MIDNIGHT':
3501 try:
3502 if rh.utc:
3503 t = time.gmtime(currentTime)
3504 else:
3505 t = time.localtime(currentTime)
3506 currentHour = t[3]
3507 currentMinute = t[4]
3508 currentSecond = t[5]
3509 # r is the number of seconds left between now and midnight
3510 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3511 currentMinute) * 60 +
3512 currentSecond)
3513 result = currentTime + r
3514 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3515 print('currentHour: %s' % currentHour, file=sys.stderr)
3516 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3517 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3518 print('r: %s' % r, file=sys.stderr)
3519 print('result: %s' % result, file=sys.stderr)
3520 except Exception:
3521 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3522 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003523 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003524 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3525
Christian Heimes180510d2008-03-03 19:15:45 +00003526# Set the locale to the platform-dependent default. I have no idea
3527# why the test does this, but in any case we save the current locale
3528# first and restore it at the end.
3529@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003530def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003531 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003532 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003533 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3534 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3535 ManagerTest, FormatterTest, BufferingFormatterTest,
3536 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3537 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3538 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3539 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003540 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003541 SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003542 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003543 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003544
Christian Heimes180510d2008-03-03 19:15:45 +00003545if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003546 test_main()