blob: bf0ad77e39844391799429ad971a7c0b25cd0a66 [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()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001388 self.handled.clear()
1389 logger.error("eggs")
1390 self.handled.wait()
1391 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001392
1393
1394@unittest.skipUnless(threading, 'Threading required for this test.')
1395class SysLogHandlerTest(BaseTest):
1396
1397 """Test for SysLogHandler using UDP."""
1398
1399 def setUp(self):
1400 """Set up a UDP server to receive log messages, and a SysLogHandler
1401 pointing to that server's address and port."""
1402 BaseTest.setUp(self)
1403 addr = ('localhost', 0)
1404 self.server = server = TestUDPServer(addr, self.handle_datagram,
1405 0.01)
1406 server.start()
1407 server.ready.wait()
1408 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1409 server.port))
1410 self.log_output = ''
1411 self.root_logger.removeHandler(self.root_logger.handlers[0])
1412 self.root_logger.addHandler(self.sl_hdlr)
1413 self.handled = threading.Event()
1414
1415 def tearDown(self):
1416 """Shutdown the UDP server."""
1417 try:
1418 self.server.stop(2.0)
1419 self.root_logger.removeHandler(self.sl_hdlr)
1420 self.sl_hdlr.close()
1421 finally:
1422 BaseTest.tearDown(self)
1423
1424 def handle_datagram(self, request):
1425 self.log_output = request.packet
1426 self.handled.set()
1427
1428 def test_output(self):
1429 # The log message sent to the SysLogHandler is properly received.
1430 logger = logging.getLogger("slh")
1431 logger.error("sp\xe4m")
1432 self.handled.wait()
1433 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1434
1435
1436@unittest.skipUnless(threading, 'Threading required for this test.')
1437class HTTPHandlerTest(BaseTest):
1438
1439 """Test for HTTPHandler."""
1440
1441 def setUp(self):
1442 """Set up an HTTP server to receive log messages, and a HTTPHandler
1443 pointing to that server's address and port."""
1444 BaseTest.setUp(self)
1445 addr = ('localhost', 0)
1446 self.server = server = TestHTTPServer(addr, self.handle_request,
1447 0.01)
1448 server.start()
1449 server.ready.wait()
1450 host = 'localhost:%d' % server.server_port
1451 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob')
1452 self.log_data = None
1453 self.root_logger.removeHandler(self.root_logger.handlers[0])
1454 self.root_logger.addHandler(self.h_hdlr)
1455 self.handled = threading.Event()
1456
1457 def tearDown(self):
1458 """Shutdown the UDP server."""
1459 try:
1460 self.server.stop(2.0)
1461 self.root_logger.removeHandler(self.h_hdlr)
1462 self.h_hdlr.close()
1463 finally:
1464 BaseTest.tearDown(self)
1465
1466 def handle_request(self, request):
1467 self.log_data = urlparse(request.path)
1468 request.send_response(200)
1469 self.handled.set()
1470
1471 def test_output(self):
1472 # The log message sent to the SysLogHandler is properly received.
1473 logger = logging.getLogger("http")
1474 msg = "sp\xe4m"
1475 logger.error(msg)
1476 self.handled.wait()
1477 self.assertEqual(self.log_data.path, '/frob')
1478 d = parse_qs(self.log_data.query)
1479 self.assertEqual(d['name'], ['http'])
1480 self.assertEqual(d['funcName'], ['test_output'])
1481 self.assertEqual(d['msg'], [msg])
1482
1483
Christian Heimes180510d2008-03-03 19:15:45 +00001484class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001485
Christian Heimes180510d2008-03-03 19:15:45 +00001486 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001487
Christian Heimes180510d2008-03-03 19:15:45 +00001488 def setUp(self):
1489 """Create a dict to remember potentially destroyed objects."""
1490 BaseTest.setUp(self)
1491 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001492
Christian Heimes180510d2008-03-03 19:15:45 +00001493 def _watch_for_survival(self, *args):
1494 """Watch the given objects for survival, by creating weakrefs to
1495 them."""
1496 for obj in args:
1497 key = id(obj), repr(obj)
1498 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001499
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001500 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001501 """Assert that all objects watched for survival have survived."""
1502 # Trigger cycle breaking.
1503 gc.collect()
1504 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001505 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001506 if ref() is None:
1507 dead.append(repr_)
1508 if dead:
1509 self.fail("%d objects should have survived "
1510 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001511
Christian Heimes180510d2008-03-03 19:15:45 +00001512 def test_persistent_loggers(self):
1513 # Logger objects are persistent and retain their configuration, even
1514 # if visible references are destroyed.
1515 self.root_logger.setLevel(logging.INFO)
1516 foo = logging.getLogger("foo")
1517 self._watch_for_survival(foo)
1518 foo.setLevel(logging.DEBUG)
1519 self.root_logger.debug(self.next_message())
1520 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001521 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001522 ('foo', 'DEBUG', '2'),
1523 ])
1524 del foo
1525 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001526 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001527 # foo has retained its settings.
1528 bar = logging.getLogger("foo")
1529 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001530 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001531 ('foo', 'DEBUG', '2'),
1532 ('foo', 'DEBUG', '3'),
1533 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001534
Benjamin Petersonf91df042009-02-13 02:50:59 +00001535
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001536class EncodingTest(BaseTest):
1537 def test_encoding_plain_file(self):
1538 # In Python 2.x, a plain file object is treated as having no encoding.
1539 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001540 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1541 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001542 # the non-ascii data we write to the log.
1543 data = "foo\x80"
1544 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001545 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001546 log.addHandler(handler)
1547 try:
1548 # write non-ascii data to the log.
1549 log.warning(data)
1550 finally:
1551 log.removeHandler(handler)
1552 handler.close()
1553 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001554 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001555 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001556 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001557 finally:
1558 f.close()
1559 finally:
1560 if os.path.isfile(fn):
1561 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001562
Benjamin Petersonf91df042009-02-13 02:50:59 +00001563 def test_encoding_cyrillic_unicode(self):
1564 log = logging.getLogger("test")
1565 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1566 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1567 #Ensure it's written in a Cyrillic encoding
1568 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001569 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001570 stream = io.BytesIO()
1571 writer = writer_class(stream, 'strict')
1572 handler = logging.StreamHandler(writer)
1573 log.addHandler(handler)
1574 try:
1575 log.warning(message)
1576 finally:
1577 log.removeHandler(handler)
1578 handler.close()
1579 # check we wrote exactly those bytes, ignoring trailing \n etc
1580 s = stream.getvalue()
1581 #Compare against what the data should be when encoded in CP-1251
1582 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1583
1584
Georg Brandlf9734072008-12-07 15:30:06 +00001585class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001586
Georg Brandlf9734072008-12-07 15:30:06 +00001587 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001588 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001589 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001590 self.addCleanup(lambda: logging.captureWarnings(False))
1591 warnings.filterwarnings("always", category=UserWarning)
1592 stream = io.StringIO()
1593 h = logging.StreamHandler(stream)
1594 logger = logging.getLogger("py.warnings")
1595 logger.addHandler(h)
1596 warnings.warn("I'm warning you...")
1597 logger.removeHandler(h)
1598 s = stream.getvalue()
1599 h.close()
1600 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001601
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001602 #See if an explicit file uses the original implementation
1603 a_file = io.StringIO()
1604 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1605 a_file, "Dummy line")
1606 s = a_file.getvalue()
1607 a_file.close()
1608 self.assertEqual(s,
1609 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1610
1611 def test_warnings_no_handlers(self):
1612 with warnings.catch_warnings():
1613 logging.captureWarnings(True)
1614 self.addCleanup(lambda: logging.captureWarnings(False))
1615
1616 # confirm our assumption: no loggers are set
1617 logger = logging.getLogger("py.warnings")
1618 assert logger.handlers == []
1619
1620 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1621 self.assertTrue(len(logger.handlers) == 1)
1622 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001623
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001624
1625def formatFunc(format, datefmt=None):
1626 return logging.Formatter(format, datefmt)
1627
1628def handlerFunc():
1629 return logging.StreamHandler()
1630
1631class CustomHandler(logging.StreamHandler):
1632 pass
1633
1634class ConfigDictTest(BaseTest):
1635
1636 """Reading logging config from a dictionary."""
1637
1638 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1639
1640 # config0 is a standard configuration.
1641 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001642 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001643 'formatters': {
1644 'form1' : {
1645 'format' : '%(levelname)s ++ %(message)s',
1646 },
1647 },
1648 'handlers' : {
1649 'hand1' : {
1650 'class' : 'logging.StreamHandler',
1651 'formatter' : 'form1',
1652 'level' : 'NOTSET',
1653 'stream' : 'ext://sys.stdout',
1654 },
1655 },
1656 'root' : {
1657 'level' : 'WARNING',
1658 'handlers' : ['hand1'],
1659 },
1660 }
1661
1662 # config1 adds a little to the standard configuration.
1663 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001664 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001665 'formatters': {
1666 'form1' : {
1667 'format' : '%(levelname)s ++ %(message)s',
1668 },
1669 },
1670 'handlers' : {
1671 'hand1' : {
1672 'class' : 'logging.StreamHandler',
1673 'formatter' : 'form1',
1674 'level' : 'NOTSET',
1675 'stream' : 'ext://sys.stdout',
1676 },
1677 },
1678 'loggers' : {
1679 'compiler.parser' : {
1680 'level' : 'DEBUG',
1681 'handlers' : ['hand1'],
1682 },
1683 },
1684 'root' : {
1685 'level' : 'WARNING',
1686 },
1687 }
1688
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001689 # config1a moves the handler to the root. Used with config8a
1690 config1a = {
1691 'version': 1,
1692 'formatters': {
1693 'form1' : {
1694 'format' : '%(levelname)s ++ %(message)s',
1695 },
1696 },
1697 'handlers' : {
1698 'hand1' : {
1699 'class' : 'logging.StreamHandler',
1700 'formatter' : 'form1',
1701 'level' : 'NOTSET',
1702 'stream' : 'ext://sys.stdout',
1703 },
1704 },
1705 'loggers' : {
1706 'compiler.parser' : {
1707 'level' : 'DEBUG',
1708 },
1709 },
1710 'root' : {
1711 'level' : 'WARNING',
1712 'handlers' : ['hand1'],
1713 },
1714 }
1715
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001716 # config2 has a subtle configuration error that should be reported
1717 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001718 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001719 'formatters': {
1720 'form1' : {
1721 'format' : '%(levelname)s ++ %(message)s',
1722 },
1723 },
1724 'handlers' : {
1725 'hand1' : {
1726 'class' : 'logging.StreamHandler',
1727 'formatter' : 'form1',
1728 'level' : 'NOTSET',
1729 'stream' : 'ext://sys.stdbout',
1730 },
1731 },
1732 'loggers' : {
1733 'compiler.parser' : {
1734 'level' : 'DEBUG',
1735 'handlers' : ['hand1'],
1736 },
1737 },
1738 'root' : {
1739 'level' : 'WARNING',
1740 },
1741 }
1742
1743 #As config1 but with a misspelt level on a handler
1744 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001745 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001746 'formatters': {
1747 'form1' : {
1748 'format' : '%(levelname)s ++ %(message)s',
1749 },
1750 },
1751 'handlers' : {
1752 'hand1' : {
1753 'class' : 'logging.StreamHandler',
1754 'formatter' : 'form1',
1755 'level' : 'NTOSET',
1756 'stream' : 'ext://sys.stdout',
1757 },
1758 },
1759 'loggers' : {
1760 'compiler.parser' : {
1761 'level' : 'DEBUG',
1762 'handlers' : ['hand1'],
1763 },
1764 },
1765 'root' : {
1766 'level' : 'WARNING',
1767 },
1768 }
1769
1770
1771 #As config1 but with a misspelt level on a logger
1772 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001773 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001774 'formatters': {
1775 'form1' : {
1776 'format' : '%(levelname)s ++ %(message)s',
1777 },
1778 },
1779 'handlers' : {
1780 'hand1' : {
1781 'class' : 'logging.StreamHandler',
1782 'formatter' : 'form1',
1783 'level' : 'NOTSET',
1784 'stream' : 'ext://sys.stdout',
1785 },
1786 },
1787 'loggers' : {
1788 'compiler.parser' : {
1789 'level' : 'DEBUG',
1790 'handlers' : ['hand1'],
1791 },
1792 },
1793 'root' : {
1794 'level' : 'WRANING',
1795 },
1796 }
1797
1798 # config3 has a less subtle configuration error
1799 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001800 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001801 'formatters': {
1802 'form1' : {
1803 'format' : '%(levelname)s ++ %(message)s',
1804 },
1805 },
1806 'handlers' : {
1807 'hand1' : {
1808 'class' : 'logging.StreamHandler',
1809 'formatter' : 'misspelled_name',
1810 'level' : 'NOTSET',
1811 'stream' : 'ext://sys.stdout',
1812 },
1813 },
1814 'loggers' : {
1815 'compiler.parser' : {
1816 'level' : 'DEBUG',
1817 'handlers' : ['hand1'],
1818 },
1819 },
1820 'root' : {
1821 'level' : 'WARNING',
1822 },
1823 }
1824
1825 # config4 specifies a custom formatter class to be loaded
1826 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001827 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001828 'formatters': {
1829 'form1' : {
1830 '()' : __name__ + '.ExceptionFormatter',
1831 'format' : '%(levelname)s:%(name)s:%(message)s',
1832 },
1833 },
1834 'handlers' : {
1835 'hand1' : {
1836 'class' : 'logging.StreamHandler',
1837 'formatter' : 'form1',
1838 'level' : 'NOTSET',
1839 'stream' : 'ext://sys.stdout',
1840 },
1841 },
1842 'root' : {
1843 'level' : 'NOTSET',
1844 'handlers' : ['hand1'],
1845 },
1846 }
1847
1848 # As config4 but using an actual callable rather than a string
1849 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001850 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001851 'formatters': {
1852 'form1' : {
1853 '()' : ExceptionFormatter,
1854 'format' : '%(levelname)s:%(name)s:%(message)s',
1855 },
1856 'form2' : {
1857 '()' : __name__ + '.formatFunc',
1858 'format' : '%(levelname)s:%(name)s:%(message)s',
1859 },
1860 'form3' : {
1861 '()' : formatFunc,
1862 'format' : '%(levelname)s:%(name)s:%(message)s',
1863 },
1864 },
1865 'handlers' : {
1866 'hand1' : {
1867 'class' : 'logging.StreamHandler',
1868 'formatter' : 'form1',
1869 'level' : 'NOTSET',
1870 'stream' : 'ext://sys.stdout',
1871 },
1872 'hand2' : {
1873 '()' : handlerFunc,
1874 },
1875 },
1876 'root' : {
1877 'level' : 'NOTSET',
1878 'handlers' : ['hand1'],
1879 },
1880 }
1881
1882 # config5 specifies a custom handler class to be loaded
1883 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001884 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001885 'formatters': {
1886 'form1' : {
1887 'format' : '%(levelname)s ++ %(message)s',
1888 },
1889 },
1890 'handlers' : {
1891 'hand1' : {
1892 'class' : __name__ + '.CustomHandler',
1893 'formatter' : 'form1',
1894 'level' : 'NOTSET',
1895 'stream' : 'ext://sys.stdout',
1896 },
1897 },
1898 'loggers' : {
1899 'compiler.parser' : {
1900 'level' : 'DEBUG',
1901 'handlers' : ['hand1'],
1902 },
1903 },
1904 'root' : {
1905 'level' : 'WARNING',
1906 },
1907 }
1908
1909 # config6 specifies a custom handler class to be loaded
1910 # but has bad arguments
1911 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001912 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001913 'formatters': {
1914 'form1' : {
1915 'format' : '%(levelname)s ++ %(message)s',
1916 },
1917 },
1918 'handlers' : {
1919 'hand1' : {
1920 'class' : __name__ + '.CustomHandler',
1921 'formatter' : 'form1',
1922 'level' : 'NOTSET',
1923 'stream' : 'ext://sys.stdout',
1924 '9' : 'invalid parameter name',
1925 },
1926 },
1927 'loggers' : {
1928 'compiler.parser' : {
1929 'level' : 'DEBUG',
1930 'handlers' : ['hand1'],
1931 },
1932 },
1933 'root' : {
1934 'level' : 'WARNING',
1935 },
1936 }
1937
1938 #config 7 does not define compiler.parser but defines compiler.lexer
1939 #so compiler.parser should be disabled after applying it
1940 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001941 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001942 'formatters': {
1943 'form1' : {
1944 'format' : '%(levelname)s ++ %(message)s',
1945 },
1946 },
1947 'handlers' : {
1948 'hand1' : {
1949 'class' : 'logging.StreamHandler',
1950 'formatter' : 'form1',
1951 'level' : 'NOTSET',
1952 'stream' : 'ext://sys.stdout',
1953 },
1954 },
1955 'loggers' : {
1956 'compiler.lexer' : {
1957 'level' : 'DEBUG',
1958 'handlers' : ['hand1'],
1959 },
1960 },
1961 'root' : {
1962 'level' : 'WARNING',
1963 },
1964 }
1965
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001966 # config8 defines both compiler and compiler.lexer
1967 # so compiler.parser should not be disabled (since
1968 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001969 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001970 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001971 'disable_existing_loggers' : False,
1972 'formatters': {
1973 'form1' : {
1974 'format' : '%(levelname)s ++ %(message)s',
1975 },
1976 },
1977 'handlers' : {
1978 'hand1' : {
1979 'class' : 'logging.StreamHandler',
1980 'formatter' : 'form1',
1981 'level' : 'NOTSET',
1982 'stream' : 'ext://sys.stdout',
1983 },
1984 },
1985 'loggers' : {
1986 'compiler' : {
1987 'level' : 'DEBUG',
1988 'handlers' : ['hand1'],
1989 },
1990 'compiler.lexer' : {
1991 },
1992 },
1993 'root' : {
1994 'level' : 'WARNING',
1995 },
1996 }
1997
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001998 # config8a disables existing loggers
1999 config8a = {
2000 'version': 1,
2001 'disable_existing_loggers' : True,
2002 'formatters': {
2003 'form1' : {
2004 'format' : '%(levelname)s ++ %(message)s',
2005 },
2006 },
2007 'handlers' : {
2008 'hand1' : {
2009 'class' : 'logging.StreamHandler',
2010 'formatter' : 'form1',
2011 'level' : 'NOTSET',
2012 'stream' : 'ext://sys.stdout',
2013 },
2014 },
2015 'loggers' : {
2016 'compiler' : {
2017 'level' : 'DEBUG',
2018 'handlers' : ['hand1'],
2019 },
2020 'compiler.lexer' : {
2021 },
2022 },
2023 'root' : {
2024 'level' : 'WARNING',
2025 },
2026 }
2027
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002028 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002029 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002030 'formatters': {
2031 'form1' : {
2032 'format' : '%(levelname)s ++ %(message)s',
2033 },
2034 },
2035 'handlers' : {
2036 'hand1' : {
2037 'class' : 'logging.StreamHandler',
2038 'formatter' : 'form1',
2039 'level' : 'WARNING',
2040 'stream' : 'ext://sys.stdout',
2041 },
2042 },
2043 'loggers' : {
2044 'compiler.parser' : {
2045 'level' : 'WARNING',
2046 'handlers' : ['hand1'],
2047 },
2048 },
2049 'root' : {
2050 'level' : 'NOTSET',
2051 },
2052 }
2053
2054 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002055 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002056 'incremental' : True,
2057 'handlers' : {
2058 'hand1' : {
2059 'level' : 'WARNING',
2060 },
2061 },
2062 'loggers' : {
2063 'compiler.parser' : {
2064 'level' : 'INFO',
2065 },
2066 },
2067 }
2068
2069 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002070 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002071 'incremental' : True,
2072 'handlers' : {
2073 'hand1' : {
2074 'level' : 'INFO',
2075 },
2076 },
2077 'loggers' : {
2078 'compiler.parser' : {
2079 'level' : 'INFO',
2080 },
2081 },
2082 }
2083
2084 #As config1 but with a filter added
2085 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002086 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002087 'formatters': {
2088 'form1' : {
2089 'format' : '%(levelname)s ++ %(message)s',
2090 },
2091 },
2092 'filters' : {
2093 'filt1' : {
2094 'name' : 'compiler.parser',
2095 },
2096 },
2097 'handlers' : {
2098 'hand1' : {
2099 'class' : 'logging.StreamHandler',
2100 'formatter' : 'form1',
2101 'level' : 'NOTSET',
2102 'stream' : 'ext://sys.stdout',
2103 'filters' : ['filt1'],
2104 },
2105 },
2106 'loggers' : {
2107 'compiler.parser' : {
2108 'level' : 'DEBUG',
2109 'filters' : ['filt1'],
2110 },
2111 },
2112 'root' : {
2113 'level' : 'WARNING',
2114 'handlers' : ['hand1'],
2115 },
2116 }
2117
2118 #As config1 but using cfg:// references
2119 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002120 'version': 1,
2121 'true_formatters': {
2122 'form1' : {
2123 'format' : '%(levelname)s ++ %(message)s',
2124 },
2125 },
2126 'handler_configs': {
2127 'hand1' : {
2128 'class' : 'logging.StreamHandler',
2129 'formatter' : 'form1',
2130 'level' : 'NOTSET',
2131 'stream' : 'ext://sys.stdout',
2132 },
2133 },
2134 'formatters' : 'cfg://true_formatters',
2135 'handlers' : {
2136 'hand1' : 'cfg://handler_configs[hand1]',
2137 },
2138 'loggers' : {
2139 'compiler.parser' : {
2140 'level' : 'DEBUG',
2141 'handlers' : ['hand1'],
2142 },
2143 },
2144 'root' : {
2145 'level' : 'WARNING',
2146 },
2147 }
2148
2149 #As config11 but missing the version key
2150 config12 = {
2151 'true_formatters': {
2152 'form1' : {
2153 'format' : '%(levelname)s ++ %(message)s',
2154 },
2155 },
2156 'handler_configs': {
2157 'hand1' : {
2158 'class' : 'logging.StreamHandler',
2159 'formatter' : 'form1',
2160 'level' : 'NOTSET',
2161 'stream' : 'ext://sys.stdout',
2162 },
2163 },
2164 'formatters' : 'cfg://true_formatters',
2165 'handlers' : {
2166 'hand1' : 'cfg://handler_configs[hand1]',
2167 },
2168 'loggers' : {
2169 'compiler.parser' : {
2170 'level' : 'DEBUG',
2171 'handlers' : ['hand1'],
2172 },
2173 },
2174 'root' : {
2175 'level' : 'WARNING',
2176 },
2177 }
2178
2179 #As config11 but using an unsupported version
2180 config13 = {
2181 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002182 'true_formatters': {
2183 'form1' : {
2184 'format' : '%(levelname)s ++ %(message)s',
2185 },
2186 },
2187 'handler_configs': {
2188 'hand1' : {
2189 'class' : 'logging.StreamHandler',
2190 'formatter' : 'form1',
2191 'level' : 'NOTSET',
2192 'stream' : 'ext://sys.stdout',
2193 },
2194 },
2195 'formatters' : 'cfg://true_formatters',
2196 'handlers' : {
2197 'hand1' : 'cfg://handler_configs[hand1]',
2198 },
2199 'loggers' : {
2200 'compiler.parser' : {
2201 'level' : 'DEBUG',
2202 'handlers' : ['hand1'],
2203 },
2204 },
2205 'root' : {
2206 'level' : 'WARNING',
2207 },
2208 }
2209
2210 def apply_config(self, conf):
2211 logging.config.dictConfig(conf)
2212
2213 def test_config0_ok(self):
2214 # A simple config which overrides the default settings.
2215 with captured_stdout() as output:
2216 self.apply_config(self.config0)
2217 logger = logging.getLogger()
2218 # Won't output anything
2219 logger.info(self.next_message())
2220 # Outputs a message
2221 logger.error(self.next_message())
2222 self.assert_log_lines([
2223 ('ERROR', '2'),
2224 ], stream=output)
2225 # Original logger output is empty.
2226 self.assert_log_lines([])
2227
2228 def test_config1_ok(self, config=config1):
2229 # A config defining a sub-parser as well.
2230 with captured_stdout() as output:
2231 self.apply_config(config)
2232 logger = logging.getLogger("compiler.parser")
2233 # Both will output a message
2234 logger.info(self.next_message())
2235 logger.error(self.next_message())
2236 self.assert_log_lines([
2237 ('INFO', '1'),
2238 ('ERROR', '2'),
2239 ], stream=output)
2240 # Original logger output is empty.
2241 self.assert_log_lines([])
2242
2243 def test_config2_failure(self):
2244 # A simple config which overrides the default settings.
2245 self.assertRaises(Exception, self.apply_config, self.config2)
2246
2247 def test_config2a_failure(self):
2248 # A simple config which overrides the default settings.
2249 self.assertRaises(Exception, self.apply_config, self.config2a)
2250
2251 def test_config2b_failure(self):
2252 # A simple config which overrides the default settings.
2253 self.assertRaises(Exception, self.apply_config, self.config2b)
2254
2255 def test_config3_failure(self):
2256 # A simple config which overrides the default settings.
2257 self.assertRaises(Exception, self.apply_config, self.config3)
2258
2259 def test_config4_ok(self):
2260 # A config specifying a custom formatter class.
2261 with captured_stdout() as output:
2262 self.apply_config(self.config4)
2263 #logger = logging.getLogger()
2264 try:
2265 raise RuntimeError()
2266 except RuntimeError:
2267 logging.exception("just testing")
2268 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002269 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002270 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2271 # Original logger output is empty
2272 self.assert_log_lines([])
2273
2274 def test_config4a_ok(self):
2275 # A config specifying a custom formatter class.
2276 with captured_stdout() as output:
2277 self.apply_config(self.config4a)
2278 #logger = logging.getLogger()
2279 try:
2280 raise RuntimeError()
2281 except RuntimeError:
2282 logging.exception("just testing")
2283 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002284 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002285 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2286 # Original logger output is empty
2287 self.assert_log_lines([])
2288
2289 def test_config5_ok(self):
2290 self.test_config1_ok(config=self.config5)
2291
2292 def test_config6_failure(self):
2293 self.assertRaises(Exception, self.apply_config, self.config6)
2294
2295 def test_config7_ok(self):
2296 with captured_stdout() as output:
2297 self.apply_config(self.config1)
2298 logger = logging.getLogger("compiler.parser")
2299 # Both will output a message
2300 logger.info(self.next_message())
2301 logger.error(self.next_message())
2302 self.assert_log_lines([
2303 ('INFO', '1'),
2304 ('ERROR', '2'),
2305 ], stream=output)
2306 # Original logger output is empty.
2307 self.assert_log_lines([])
2308 with captured_stdout() as output:
2309 self.apply_config(self.config7)
2310 logger = logging.getLogger("compiler.parser")
2311 self.assertTrue(logger.disabled)
2312 logger = logging.getLogger("compiler.lexer")
2313 # Both will output a message
2314 logger.info(self.next_message())
2315 logger.error(self.next_message())
2316 self.assert_log_lines([
2317 ('INFO', '3'),
2318 ('ERROR', '4'),
2319 ], stream=output)
2320 # Original logger output is empty.
2321 self.assert_log_lines([])
2322
2323 #Same as test_config_7_ok but don't disable old loggers.
2324 def test_config_8_ok(self):
2325 with captured_stdout() as output:
2326 self.apply_config(self.config1)
2327 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002328 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002329 logger.info(self.next_message())
2330 logger.error(self.next_message())
2331 self.assert_log_lines([
2332 ('INFO', '1'),
2333 ('ERROR', '2'),
2334 ], stream=output)
2335 # Original logger output is empty.
2336 self.assert_log_lines([])
2337 with captured_stdout() as output:
2338 self.apply_config(self.config8)
2339 logger = logging.getLogger("compiler.parser")
2340 self.assertFalse(logger.disabled)
2341 # Both will output a message
2342 logger.info(self.next_message())
2343 logger.error(self.next_message())
2344 logger = logging.getLogger("compiler.lexer")
2345 # Both will output a message
2346 logger.info(self.next_message())
2347 logger.error(self.next_message())
2348 self.assert_log_lines([
2349 ('INFO', '3'),
2350 ('ERROR', '4'),
2351 ('INFO', '5'),
2352 ('ERROR', '6'),
2353 ], stream=output)
2354 # Original logger output is empty.
2355 self.assert_log_lines([])
2356
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002357 def test_config_8a_ok(self):
2358 with captured_stdout() as output:
2359 self.apply_config(self.config1a)
2360 logger = logging.getLogger("compiler.parser")
2361 # See issue #11424. compiler-hyphenated sorts
2362 # between compiler and compiler.xyz and this
2363 # was preventing compiler.xyz from being included
2364 # in the child loggers of compiler because of an
2365 # overzealous loop termination condition.
2366 hyphenated = logging.getLogger('compiler-hyphenated')
2367 # All will output a message
2368 logger.info(self.next_message())
2369 logger.error(self.next_message())
2370 hyphenated.critical(self.next_message())
2371 self.assert_log_lines([
2372 ('INFO', '1'),
2373 ('ERROR', '2'),
2374 ('CRITICAL', '3'),
2375 ], stream=output)
2376 # Original logger output is empty.
2377 self.assert_log_lines([])
2378 with captured_stdout() as output:
2379 self.apply_config(self.config8a)
2380 logger = logging.getLogger("compiler.parser")
2381 self.assertFalse(logger.disabled)
2382 # Both will output a message
2383 logger.info(self.next_message())
2384 logger.error(self.next_message())
2385 logger = logging.getLogger("compiler.lexer")
2386 # Both will output a message
2387 logger.info(self.next_message())
2388 logger.error(self.next_message())
2389 # Will not appear
2390 hyphenated.critical(self.next_message())
2391 self.assert_log_lines([
2392 ('INFO', '4'),
2393 ('ERROR', '5'),
2394 ('INFO', '6'),
2395 ('ERROR', '7'),
2396 ], stream=output)
2397 # Original logger output is empty.
2398 self.assert_log_lines([])
2399
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002400 def test_config_9_ok(self):
2401 with captured_stdout() as output:
2402 self.apply_config(self.config9)
2403 logger = logging.getLogger("compiler.parser")
2404 #Nothing will be output since both handler and logger are set to WARNING
2405 logger.info(self.next_message())
2406 self.assert_log_lines([], stream=output)
2407 self.apply_config(self.config9a)
2408 #Nothing will be output since both handler is still set to WARNING
2409 logger.info(self.next_message())
2410 self.assert_log_lines([], stream=output)
2411 self.apply_config(self.config9b)
2412 #Message should now be output
2413 logger.info(self.next_message())
2414 self.assert_log_lines([
2415 ('INFO', '3'),
2416 ], stream=output)
2417
2418 def test_config_10_ok(self):
2419 with captured_stdout() as output:
2420 self.apply_config(self.config10)
2421 logger = logging.getLogger("compiler.parser")
2422 logger.warning(self.next_message())
2423 logger = logging.getLogger('compiler')
2424 #Not output, because filtered
2425 logger.warning(self.next_message())
2426 logger = logging.getLogger('compiler.lexer')
2427 #Not output, because filtered
2428 logger.warning(self.next_message())
2429 logger = logging.getLogger("compiler.parser.codegen")
2430 #Output, as not filtered
2431 logger.error(self.next_message())
2432 self.assert_log_lines([
2433 ('WARNING', '1'),
2434 ('ERROR', '4'),
2435 ], stream=output)
2436
2437 def test_config11_ok(self):
2438 self.test_config1_ok(self.config11)
2439
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002440 def test_config12_failure(self):
2441 self.assertRaises(Exception, self.apply_config, self.config12)
2442
2443 def test_config13_failure(self):
2444 self.assertRaises(Exception, self.apply_config, self.config13)
2445
Victor Stinner45df8202010-04-28 22:31:17 +00002446 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002447 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002448 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002449 # Ask for a randomly assigned port (by using port 0)
2450 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002451 t.start()
2452 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002453 # Now get the port allocated
2454 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002455 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002456 try:
2457 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2458 sock.settimeout(2.0)
2459 sock.connect(('localhost', port))
2460
2461 slen = struct.pack('>L', len(text))
2462 s = slen + text
2463 sentsofar = 0
2464 left = len(s)
2465 while left > 0:
2466 sent = sock.send(s[sentsofar:])
2467 sentsofar += sent
2468 left -= sent
2469 sock.close()
2470 finally:
2471 t.ready.wait(2.0)
2472 logging.config.stopListening()
2473 t.join(2.0)
2474
2475 def test_listen_config_10_ok(self):
2476 with captured_stdout() as output:
2477 self.setup_via_listener(json.dumps(self.config10))
2478 logger = logging.getLogger("compiler.parser")
2479 logger.warning(self.next_message())
2480 logger = logging.getLogger('compiler')
2481 #Not output, because filtered
2482 logger.warning(self.next_message())
2483 logger = logging.getLogger('compiler.lexer')
2484 #Not output, because filtered
2485 logger.warning(self.next_message())
2486 logger = logging.getLogger("compiler.parser.codegen")
2487 #Output, as not filtered
2488 logger.error(self.next_message())
2489 self.assert_log_lines([
2490 ('WARNING', '1'),
2491 ('ERROR', '4'),
2492 ], stream=output)
2493
2494 def test_listen_config_1_ok(self):
2495 with captured_stdout() as output:
2496 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2497 logger = logging.getLogger("compiler.parser")
2498 # Both will output a message
2499 logger.info(self.next_message())
2500 logger.error(self.next_message())
2501 self.assert_log_lines([
2502 ('INFO', '1'),
2503 ('ERROR', '2'),
2504 ], stream=output)
2505 # Original logger output is empty.
2506 self.assert_log_lines([])
2507
Vinay Sajip373baef2011-04-26 20:05:24 +01002508 def test_baseconfig(self):
2509 d = {
2510 'atuple': (1, 2, 3),
2511 'alist': ['a', 'b', 'c'],
2512 'adict': {'d': 'e', 'f': 3 },
2513 'nest1': ('g', ('h', 'i'), 'j'),
2514 'nest2': ['k', ['l', 'm'], 'n'],
2515 'nest3': ['o', 'cfg://alist', 'p'],
2516 }
2517 bc = logging.config.BaseConfigurator(d)
2518 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2519 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2520 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2521 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2522 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2523 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2524 v = bc.convert('cfg://nest3')
2525 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2526 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2527 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2528 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002529
2530class ManagerTest(BaseTest):
2531 def test_manager_loggerclass(self):
2532 logged = []
2533
2534 class MyLogger(logging.Logger):
2535 def _log(self, level, msg, args, exc_info=None, extra=None):
2536 logged.append(msg)
2537
2538 man = logging.Manager(None)
2539 self.assertRaises(TypeError, man.setLoggerClass, int)
2540 man.setLoggerClass(MyLogger)
2541 logger = man.getLogger('test')
2542 logger.warning('should appear in logged')
2543 logging.warning('should not appear in logged')
2544
2545 self.assertEqual(logged, ['should appear in logged'])
2546
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002547 def test_set_log_record_factory(self):
2548 man = logging.Manager(None)
2549 expected = object()
2550 man.setLogRecordFactory(expected)
2551 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002552
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002553class ChildLoggerTest(BaseTest):
2554 def test_child_loggers(self):
2555 r = logging.getLogger()
2556 l1 = logging.getLogger('abc')
2557 l2 = logging.getLogger('def.ghi')
2558 c1 = r.getChild('xyz')
2559 c2 = r.getChild('uvw.xyz')
2560 self.assertTrue(c1 is logging.getLogger('xyz'))
2561 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2562 c1 = l1.getChild('def')
2563 c2 = c1.getChild('ghi')
2564 c3 = l1.getChild('def.ghi')
2565 self.assertTrue(c1 is logging.getLogger('abc.def'))
2566 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2567 self.assertTrue(c2 is c3)
2568
2569
Vinay Sajip6fac8172010-10-19 20:44:14 +00002570class DerivedLogRecord(logging.LogRecord):
2571 pass
2572
Vinay Sajip61561522010-12-03 11:50:38 +00002573class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002574
2575 def setUp(self):
2576 class CheckingFilter(logging.Filter):
2577 def __init__(self, cls):
2578 self.cls = cls
2579
2580 def filter(self, record):
2581 t = type(record)
2582 if t is not self.cls:
2583 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2584 self.cls)
2585 raise TypeError(msg)
2586 return True
2587
2588 BaseTest.setUp(self)
2589 self.filter = CheckingFilter(DerivedLogRecord)
2590 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002591 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002592
2593 def tearDown(self):
2594 self.root_logger.removeFilter(self.filter)
2595 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002596 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002597
2598 def test_logrecord_class(self):
2599 self.assertRaises(TypeError, self.root_logger.warning,
2600 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002601 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002602 self.root_logger.error(self.next_message())
2603 self.assert_log_lines([
2604 ('root', 'ERROR', '2'),
2605 ])
2606
2607
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002608class QueueHandlerTest(BaseTest):
2609 # Do not bother with a logger name group.
2610 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2611
2612 def setUp(self):
2613 BaseTest.setUp(self)
2614 self.queue = queue.Queue(-1)
2615 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2616 self.que_logger = logging.getLogger('que')
2617 self.que_logger.propagate = False
2618 self.que_logger.setLevel(logging.WARNING)
2619 self.que_logger.addHandler(self.que_hdlr)
2620
2621 def tearDown(self):
2622 self.que_hdlr.close()
2623 BaseTest.tearDown(self)
2624
2625 def test_queue_handler(self):
2626 self.que_logger.debug(self.next_message())
2627 self.assertRaises(queue.Empty, self.queue.get_nowait)
2628 self.que_logger.info(self.next_message())
2629 self.assertRaises(queue.Empty, self.queue.get_nowait)
2630 msg = self.next_message()
2631 self.que_logger.warning(msg)
2632 data = self.queue.get_nowait()
2633 self.assertTrue(isinstance(data, logging.LogRecord))
2634 self.assertEqual(data.name, self.que_logger.name)
2635 self.assertEqual((data.msg, data.args), (msg, None))
2636
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002637 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2638 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002639 def test_queue_listener(self):
2640 handler = TestHandler(Matcher())
2641 listener = logging.handlers.QueueListener(self.queue, handler)
2642 listener.start()
2643 try:
2644 self.que_logger.warning(self.next_message())
2645 self.que_logger.error(self.next_message())
2646 self.que_logger.critical(self.next_message())
2647 finally:
2648 listener.stop()
2649 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2650 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2651 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2652
Vinay Sajip37eb3382011-04-26 20:26:41 +01002653ZERO = datetime.timedelta(0)
2654
2655class UTC(datetime.tzinfo):
2656 def utcoffset(self, dt):
2657 return ZERO
2658
2659 dst = utcoffset
2660
2661 def tzname(self, dt):
2662 return 'UTC'
2663
2664utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002665
Vinay Sajipa39c5712010-10-25 13:57:39 +00002666class FormatterTest(unittest.TestCase):
2667 def setUp(self):
2668 self.common = {
2669 'name': 'formatter.test',
2670 'level': logging.DEBUG,
2671 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2672 'lineno': 42,
2673 'exc_info': None,
2674 'func': None,
2675 'msg': 'Message with %d %s',
2676 'args': (2, 'placeholders'),
2677 }
2678 self.variants = {
2679 }
2680
2681 def get_record(self, name=None):
2682 result = dict(self.common)
2683 if name is not None:
2684 result.update(self.variants[name])
2685 return logging.makeLogRecord(result)
2686
2687 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002688 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002689 r = self.get_record()
2690 f = logging.Formatter('${%(message)s}')
2691 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2692 f = logging.Formatter('%(random)s')
2693 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002694 self.assertFalse(f.usesTime())
2695 f = logging.Formatter('%(asctime)s')
2696 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002697 f = logging.Formatter('%(asctime)-15s')
2698 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002699 f = logging.Formatter('asctime')
2700 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002701
2702 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002703 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002704 r = self.get_record()
2705 f = logging.Formatter('$%{message}%$', style='{')
2706 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2707 f = logging.Formatter('{random}', style='{')
2708 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002709 self.assertFalse(f.usesTime())
2710 f = logging.Formatter('{asctime}', style='{')
2711 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002712 f = logging.Formatter('{asctime!s:15}', style='{')
2713 self.assertTrue(f.usesTime())
2714 f = logging.Formatter('{asctime:15}', style='{')
2715 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002716 f = logging.Formatter('asctime', style='{')
2717 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002718
2719 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002720 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002721 r = self.get_record()
2722 f = logging.Formatter('$message', style='$')
2723 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2724 f = logging.Formatter('$$%${message}%$$', style='$')
2725 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2726 f = logging.Formatter('${random}', style='$')
2727 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002728 self.assertFalse(f.usesTime())
2729 f = logging.Formatter('${asctime}', style='$')
2730 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002731 f = logging.Formatter('${asctime', style='$')
2732 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002733 f = logging.Formatter('$asctime', style='$')
2734 self.assertTrue(f.usesTime())
2735 f = logging.Formatter('asctime', style='$')
2736 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002737
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002738 def test_invalid_style(self):
2739 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2740
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002741 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002742 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002743 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002744 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002745 r.msecs = 123
2746 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002747 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002748 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2749 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002750 f.format(r)
2751 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2752
2753class TestBufferingFormatter(logging.BufferingFormatter):
2754 def formatHeader(self, records):
2755 return '[(%d)' % len(records)
2756
2757 def formatFooter(self, records):
2758 return '(%d)]' % len(records)
2759
2760class BufferingFormatterTest(unittest.TestCase):
2761 def setUp(self):
2762 self.records = [
2763 logging.makeLogRecord({'msg': 'one'}),
2764 logging.makeLogRecord({'msg': 'two'}),
2765 ]
2766
2767 def test_default(self):
2768 f = logging.BufferingFormatter()
2769 self.assertEqual('', f.format([]))
2770 self.assertEqual('onetwo', f.format(self.records))
2771
2772 def test_custom(self):
2773 f = TestBufferingFormatter()
2774 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2775 lf = logging.Formatter('<%(message)s>')
2776 f = TestBufferingFormatter(lf)
2777 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002778
2779class ExceptionTest(BaseTest):
2780 def test_formatting(self):
2781 r = self.root_logger
2782 h = RecordingHandler()
2783 r.addHandler(h)
2784 try:
2785 raise RuntimeError('deliberate mistake')
2786 except:
2787 logging.exception('failed', stack_info=True)
2788 r.removeHandler(h)
2789 h.close()
2790 r = h.records[0]
2791 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2792 'call last):\n'))
2793 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2794 'deliberate mistake'))
2795 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2796 'call last):\n'))
2797 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2798 'stack_info=True)'))
2799
2800
Vinay Sajip5a27d402010-12-10 11:42:57 +00002801class LastResortTest(BaseTest):
2802 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002803 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002804 root = self.root_logger
2805 root.removeHandler(self.root_hdlr)
2806 old_stderr = sys.stderr
2807 old_lastresort = logging.lastResort
2808 old_raise_exceptions = logging.raiseExceptions
2809 try:
2810 sys.stderr = sio = io.StringIO()
2811 root.warning('This is your final chance!')
2812 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2813 #No handlers and no last resort, so 'No handlers' message
2814 logging.lastResort = None
2815 sys.stderr = sio = io.StringIO()
2816 root.warning('This is your final chance!')
2817 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2818 # 'No handlers' message only printed once
2819 sys.stderr = sio = io.StringIO()
2820 root.warning('This is your final chance!')
2821 self.assertEqual(sio.getvalue(), '')
2822 root.manager.emittedNoHandlerWarning = False
2823 #If raiseExceptions is False, no message is printed
2824 logging.raiseExceptions = False
2825 sys.stderr = sio = io.StringIO()
2826 root.warning('This is your final chance!')
2827 self.assertEqual(sio.getvalue(), '')
2828 finally:
2829 sys.stderr = old_stderr
2830 root.addHandler(self.root_hdlr)
2831 logging.lastResort = old_lastresort
2832 logging.raiseExceptions = old_raise_exceptions
2833
2834
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002835class FakeHandler:
2836
2837 def __init__(self, identifier, called):
2838 for method in ('acquire', 'flush', 'close', 'release'):
2839 setattr(self, method, self.record_call(identifier, method, called))
2840
2841 def record_call(self, identifier, method_name, called):
2842 def inner():
2843 called.append('{} - {}'.format(identifier, method_name))
2844 return inner
2845
2846
2847class RecordingHandler(logging.NullHandler):
2848
2849 def __init__(self, *args, **kwargs):
2850 super(RecordingHandler, self).__init__(*args, **kwargs)
2851 self.records = []
2852
2853 def handle(self, record):
2854 """Keep track of all the emitted records."""
2855 self.records.append(record)
2856
2857
2858class ShutdownTest(BaseTest):
2859
Vinay Sajip5e66b162011-04-20 15:41:14 +01002860 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002861
2862 def setUp(self):
2863 super(ShutdownTest, self).setUp()
2864 self.called = []
2865
2866 raise_exceptions = logging.raiseExceptions
2867 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2868
2869 def raise_error(self, error):
2870 def inner():
2871 raise error()
2872 return inner
2873
2874 def test_no_failure(self):
2875 # create some fake handlers
2876 handler0 = FakeHandler(0, self.called)
2877 handler1 = FakeHandler(1, self.called)
2878 handler2 = FakeHandler(2, self.called)
2879
2880 # create live weakref to those handlers
2881 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2882
2883 logging.shutdown(handlerList=list(handlers))
2884
2885 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2886 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2887 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2888 self.assertEqual(expected, self.called)
2889
2890 def _test_with_failure_in_method(self, method, error):
2891 handler = FakeHandler(0, self.called)
2892 setattr(handler, method, self.raise_error(error))
2893 handlers = [logging.weakref.ref(handler)]
2894
2895 logging.shutdown(handlerList=list(handlers))
2896
2897 self.assertEqual('0 - release', self.called[-1])
2898
2899 def test_with_ioerror_in_acquire(self):
2900 self._test_with_failure_in_method('acquire', IOError)
2901
2902 def test_with_ioerror_in_flush(self):
2903 self._test_with_failure_in_method('flush', IOError)
2904
2905 def test_with_ioerror_in_close(self):
2906 self._test_with_failure_in_method('close', IOError)
2907
2908 def test_with_valueerror_in_acquire(self):
2909 self._test_with_failure_in_method('acquire', ValueError)
2910
2911 def test_with_valueerror_in_flush(self):
2912 self._test_with_failure_in_method('flush', ValueError)
2913
2914 def test_with_valueerror_in_close(self):
2915 self._test_with_failure_in_method('close', ValueError)
2916
2917 def test_with_other_error_in_acquire_without_raise(self):
2918 logging.raiseExceptions = False
2919 self._test_with_failure_in_method('acquire', IndexError)
2920
2921 def test_with_other_error_in_flush_without_raise(self):
2922 logging.raiseExceptions = False
2923 self._test_with_failure_in_method('flush', IndexError)
2924
2925 def test_with_other_error_in_close_without_raise(self):
2926 logging.raiseExceptions = False
2927 self._test_with_failure_in_method('close', IndexError)
2928
2929 def test_with_other_error_in_acquire_with_raise(self):
2930 logging.raiseExceptions = True
2931 self.assertRaises(IndexError, self._test_with_failure_in_method,
2932 'acquire', IndexError)
2933
2934 def test_with_other_error_in_flush_with_raise(self):
2935 logging.raiseExceptions = True
2936 self.assertRaises(IndexError, self._test_with_failure_in_method,
2937 'flush', IndexError)
2938
2939 def test_with_other_error_in_close_with_raise(self):
2940 logging.raiseExceptions = True
2941 self.assertRaises(IndexError, self._test_with_failure_in_method,
2942 'close', IndexError)
2943
2944
2945class ModuleLevelMiscTest(BaseTest):
2946
Vinay Sajip5e66b162011-04-20 15:41:14 +01002947 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002948
2949 def test_disable(self):
2950 old_disable = logging.root.manager.disable
2951 # confirm our assumptions are correct
2952 assert old_disable == 0
2953 self.addCleanup(lambda: logging.disable(old_disable))
2954
2955 logging.disable(83)
2956 self.assertEqual(logging.root.manager.disable, 83)
2957
2958 def _test_log(self, method, level=None):
2959 called = []
2960 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002961 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002962
2963 recording = RecordingHandler()
2964 logging.root.addHandler(recording)
2965
2966 log_method = getattr(logging, method)
2967 if level is not None:
2968 log_method(level, "test me: %r", recording)
2969 else:
2970 log_method("test me: %r", recording)
2971
2972 self.assertEqual(len(recording.records), 1)
2973 record = recording.records[0]
2974 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2975
2976 expected_level = level if level is not None else getattr(logging, method.upper())
2977 self.assertEqual(record.levelno, expected_level)
2978
2979 # basicConfig was not called!
2980 self.assertEqual(called, [])
2981
2982 def test_log(self):
2983 self._test_log('log', logging.ERROR)
2984
2985 def test_debug(self):
2986 self._test_log('debug')
2987
2988 def test_info(self):
2989 self._test_log('info')
2990
2991 def test_warning(self):
2992 self._test_log('warning')
2993
2994 def test_error(self):
2995 self._test_log('error')
2996
2997 def test_critical(self):
2998 self._test_log('critical')
2999
3000 def test_set_logger_class(self):
3001 self.assertRaises(TypeError, logging.setLoggerClass, object)
3002
3003 class MyLogger(logging.Logger):
3004 pass
3005
3006 logging.setLoggerClass(MyLogger)
3007 self.assertEqual(logging.getLoggerClass(), MyLogger)
3008
3009 logging.setLoggerClass(logging.Logger)
3010 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3011
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003012class LogRecordTest(BaseTest):
3013 def test_str_rep(self):
3014 r = logging.makeLogRecord({})
3015 s = str(r)
3016 self.assertTrue(s.startswith('<LogRecord: '))
3017 self.assertTrue(s.endswith('>'))
3018
3019 def test_dict_arg(self):
3020 h = RecordingHandler()
3021 r = logging.getLogger()
3022 r.addHandler(h)
3023 d = {'less' : 'more' }
3024 logging.warning('less is %(less)s', d)
3025 self.assertIs(h.records[0].args, d)
3026 self.assertEqual(h.records[0].message, 'less is more')
3027 r.removeHandler(h)
3028 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003029
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003030 def test_multiprocessing(self):
3031 r = logging.makeLogRecord({})
3032 self.assertEqual(r.processName, 'MainProcess')
3033 import multiprocessing as mp
3034 r = logging.makeLogRecord({})
3035 self.assertEqual(r.processName, mp.current_process().name)
3036
3037 def test_optional(self):
3038 r = logging.makeLogRecord({})
3039 NOT_NONE = self.assertIsNotNone
3040 NOT_NONE(r.thread)
3041 NOT_NONE(r.threadName)
3042 NOT_NONE(r.process)
3043 NOT_NONE(r.processName)
3044 log_threads = logging.logThreads
3045 log_processes = logging.logProcesses
3046 log_multiprocessing = logging.logMultiprocessing
3047 try:
3048 logging.logThreads = False
3049 logging.logProcesses = False
3050 logging.logMultiprocessing = False
3051 r = logging.makeLogRecord({})
3052 NONE = self.assertIsNone
3053 NONE(r.thread)
3054 NONE(r.threadName)
3055 NONE(r.process)
3056 NONE(r.processName)
3057 finally:
3058 logging.logThreads = log_threads
3059 logging.logProcesses = log_processes
3060 logging.logMultiprocessing = log_multiprocessing
3061
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003062class BasicConfigTest(unittest.TestCase):
3063
Vinay Sajip95bf5042011-04-20 11:50:56 +01003064 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003065
3066 def setUp(self):
3067 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003068 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003069 self.saved_handlers = logging._handlers.copy()
3070 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003071 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003072 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003073 logging.root.handlers = []
3074
3075 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003076 for h in logging.root.handlers[:]:
3077 logging.root.removeHandler(h)
3078 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003079 super(BasicConfigTest, self).tearDown()
3080
Vinay Sajip3def7e02011-04-20 10:58:06 +01003081 def cleanup(self):
3082 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003083 logging._handlers.clear()
3084 logging._handlers.update(self.saved_handlers)
3085 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003086 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003087
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003088 def test_no_kwargs(self):
3089 logging.basicConfig()
3090
3091 # handler defaults to a StreamHandler to sys.stderr
3092 self.assertEqual(len(logging.root.handlers), 1)
3093 handler = logging.root.handlers[0]
3094 self.assertIsInstance(handler, logging.StreamHandler)
3095 self.assertEqual(handler.stream, sys.stderr)
3096
3097 formatter = handler.formatter
3098 # format defaults to logging.BASIC_FORMAT
3099 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3100 # datefmt defaults to None
3101 self.assertIsNone(formatter.datefmt)
3102 # style defaults to %
3103 self.assertIsInstance(formatter._style, logging.PercentStyle)
3104
3105 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003106 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003107
3108 def test_filename(self):
3109 logging.basicConfig(filename='test.log')
3110
3111 self.assertEqual(len(logging.root.handlers), 1)
3112 handler = logging.root.handlers[0]
3113 self.assertIsInstance(handler, logging.FileHandler)
3114
3115 expected = logging.FileHandler('test.log', 'a')
3116 self.addCleanup(expected.close)
3117 self.assertEqual(handler.stream.mode, expected.stream.mode)
3118 self.assertEqual(handler.stream.name, expected.stream.name)
3119
3120 def test_filemode(self):
3121 logging.basicConfig(filename='test.log', filemode='wb')
3122
3123 handler = logging.root.handlers[0]
3124 expected = logging.FileHandler('test.log', 'wb')
3125 self.addCleanup(expected.close)
3126 self.assertEqual(handler.stream.mode, expected.stream.mode)
3127
3128 def test_stream(self):
3129 stream = io.StringIO()
3130 self.addCleanup(stream.close)
3131 logging.basicConfig(stream=stream)
3132
3133 self.assertEqual(len(logging.root.handlers), 1)
3134 handler = logging.root.handlers[0]
3135 self.assertIsInstance(handler, logging.StreamHandler)
3136 self.assertEqual(handler.stream, stream)
3137
3138 def test_format(self):
3139 logging.basicConfig(format='foo')
3140
3141 formatter = logging.root.handlers[0].formatter
3142 self.assertEqual(formatter._style._fmt, 'foo')
3143
3144 def test_datefmt(self):
3145 logging.basicConfig(datefmt='bar')
3146
3147 formatter = logging.root.handlers[0].formatter
3148 self.assertEqual(formatter.datefmt, 'bar')
3149
3150 def test_style(self):
3151 logging.basicConfig(style='$')
3152
3153 formatter = logging.root.handlers[0].formatter
3154 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3155
3156 def test_level(self):
3157 old_level = logging.root.level
3158 self.addCleanup(lambda: logging.root.setLevel(old_level))
3159
3160 logging.basicConfig(level=57)
3161 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003162 # Test that second call has no effect
3163 logging.basicConfig(level=58)
3164 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003165
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003166 def test_incompatible(self):
3167 assertRaises = self.assertRaises
3168 handlers = [logging.StreamHandler()]
3169 stream = sys.stderr
3170 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3171 stream=stream)
3172 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3173 handlers=handlers)
3174 assertRaises(ValueError, logging.basicConfig, stream=stream,
3175 handlers=handlers)
3176
3177 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003178 handlers = [
3179 logging.StreamHandler(),
3180 logging.StreamHandler(sys.stdout),
3181 logging.StreamHandler(),
3182 ]
3183 f = logging.Formatter()
3184 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003185 logging.basicConfig(handlers=handlers)
3186 self.assertIs(handlers[0], logging.root.handlers[0])
3187 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003188 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003189 self.assertIsNotNone(handlers[0].formatter)
3190 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003191 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003192 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3193
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003194 def _test_log(self, method, level=None):
3195 # logging.root has no handlers so basicConfig should be called
3196 called = []
3197
3198 old_basic_config = logging.basicConfig
3199 def my_basic_config(*a, **kw):
3200 old_basic_config()
3201 old_level = logging.root.level
3202 logging.root.setLevel(100) # avoid having messages in stderr
3203 self.addCleanup(lambda: logging.root.setLevel(old_level))
3204 called.append((a, kw))
3205
3206 patch(self, logging, 'basicConfig', my_basic_config)
3207
3208 log_method = getattr(logging, method)
3209 if level is not None:
3210 log_method(level, "test me")
3211 else:
3212 log_method("test me")
3213
3214 # basicConfig was called with no arguments
3215 self.assertEqual(called, [((), {})])
3216
3217 def test_log(self):
3218 self._test_log('log', logging.WARNING)
3219
3220 def test_debug(self):
3221 self._test_log('debug')
3222
3223 def test_info(self):
3224 self._test_log('info')
3225
3226 def test_warning(self):
3227 self._test_log('warning')
3228
3229 def test_error(self):
3230 self._test_log('error')
3231
3232 def test_critical(self):
3233 self._test_log('critical')
3234
3235
3236class LoggerAdapterTest(unittest.TestCase):
3237
3238 def setUp(self):
3239 super(LoggerAdapterTest, self).setUp()
3240 old_handler_list = logging._handlerList[:]
3241
3242 self.recording = RecordingHandler()
3243 self.logger = logging.root
3244 self.logger.addHandler(self.recording)
3245 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3246 self.addCleanup(self.recording.close)
3247
3248 def cleanup():
3249 logging._handlerList[:] = old_handler_list
3250
3251 self.addCleanup(cleanup)
3252 self.addCleanup(logging.shutdown)
3253 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3254
3255 def test_exception(self):
3256 msg = 'testing exception: %r'
3257 exc = None
3258 try:
3259 assert False
3260 except AssertionError as e:
3261 exc = e
3262 self.adapter.exception(msg, self.recording)
3263
3264 self.assertEqual(len(self.recording.records), 1)
3265 record = self.recording.records[0]
3266 self.assertEqual(record.levelno, logging.ERROR)
3267 self.assertEqual(record.msg, msg)
3268 self.assertEqual(record.args, (self.recording,))
3269 self.assertEqual(record.exc_info,
3270 (exc.__class__, exc, exc.__traceback__))
3271
3272 def test_critical(self):
3273 msg = 'critical test! %r'
3274 self.adapter.critical(msg, self.recording)
3275
3276 self.assertEqual(len(self.recording.records), 1)
3277 record = self.recording.records[0]
3278 self.assertEqual(record.levelno, logging.CRITICAL)
3279 self.assertEqual(record.msg, msg)
3280 self.assertEqual(record.args, (self.recording,))
3281
3282 def test_is_enabled_for(self):
3283 old_disable = self.adapter.logger.manager.disable
3284 self.adapter.logger.manager.disable = 33
3285 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3286 'disable', old_disable))
3287 self.assertFalse(self.adapter.isEnabledFor(32))
3288
3289 def test_has_handlers(self):
3290 self.assertTrue(self.adapter.hasHandlers())
3291
3292 for handler in self.logger.handlers:
3293 self.logger.removeHandler(handler)
3294 assert not self.logger.hasHandlers()
3295
3296 self.assertFalse(self.adapter.hasHandlers())
3297
3298
3299class LoggerTest(BaseTest):
3300
3301 def setUp(self):
3302 super(LoggerTest, self).setUp()
3303 self.recording = RecordingHandler()
3304 self.logger = logging.Logger(name='blah')
3305 self.logger.addHandler(self.recording)
3306 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3307 self.addCleanup(self.recording.close)
3308 self.addCleanup(logging.shutdown)
3309
3310 def test_set_invalid_level(self):
3311 self.assertRaises(TypeError, self.logger.setLevel, object())
3312
3313 def test_exception(self):
3314 msg = 'testing exception: %r'
3315 exc = None
3316 try:
3317 assert False
3318 except AssertionError as e:
3319 exc = e
3320 self.logger.exception(msg, self.recording)
3321
3322 self.assertEqual(len(self.recording.records), 1)
3323 record = self.recording.records[0]
3324 self.assertEqual(record.levelno, logging.ERROR)
3325 self.assertEqual(record.msg, msg)
3326 self.assertEqual(record.args, (self.recording,))
3327 self.assertEqual(record.exc_info,
3328 (exc.__class__, exc, exc.__traceback__))
3329
3330 def test_log_invalid_level_with_raise(self):
3331 old_raise = logging.raiseExceptions
3332 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3333
3334 logging.raiseExceptions = True
3335 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3336
3337 def test_log_invalid_level_no_raise(self):
3338 old_raise = logging.raiseExceptions
3339 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3340
3341 logging.raiseExceptions = False
3342 self.logger.log('10', 'test message') # no exception happens
3343
3344 def test_find_caller_with_stack_info(self):
3345 called = []
3346 patch(self, logging.traceback, 'print_stack',
3347 lambda f, file: called.append(file.getvalue()))
3348
3349 self.logger.findCaller(stack_info=True)
3350
3351 self.assertEqual(len(called), 1)
3352 self.assertEqual('Stack (most recent call last):\n', called[0])
3353
3354 def test_make_record_with_extra_overwrite(self):
3355 name = 'my record'
3356 level = 13
3357 fn = lno = msg = args = exc_info = func = sinfo = None
3358 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3359 exc_info, func, sinfo)
3360
3361 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3362 extra = {key: 'some value'}
3363 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3364 fn, lno, msg, args, exc_info,
3365 extra=extra, sinfo=sinfo)
3366
3367 def test_make_record_with_extra_no_overwrite(self):
3368 name = 'my record'
3369 level = 13
3370 fn = lno = msg = args = exc_info = func = sinfo = None
3371 extra = {'valid_key': 'some value'}
3372 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3373 exc_info, extra=extra, sinfo=sinfo)
3374 self.assertIn('valid_key', result.__dict__)
3375
3376 def test_has_handlers(self):
3377 self.assertTrue(self.logger.hasHandlers())
3378
3379 for handler in self.logger.handlers:
3380 self.logger.removeHandler(handler)
3381 assert not self.logger.hasHandlers()
3382
3383 self.assertFalse(self.logger.hasHandlers())
3384
3385 def test_has_handlers_no_propagate(self):
3386 child_logger = logging.getLogger('blah.child')
3387 child_logger.propagate = False
3388 assert child_logger.handlers == []
3389
3390 self.assertFalse(child_logger.hasHandlers())
3391
3392 def test_is_enabled_for(self):
3393 old_disable = self.logger.manager.disable
3394 self.logger.manager.disable = 23
3395 self.addCleanup(lambda: setattr(self.logger.manager,
3396 'disable', old_disable))
3397 self.assertFalse(self.logger.isEnabledFor(22))
3398
3399
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003400class BaseFileTest(BaseTest):
3401 "Base class for handler tests that write log files"
3402
3403 def setUp(self):
3404 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003405 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3406 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003407 self.rmfiles = []
3408
3409 def tearDown(self):
3410 for fn in self.rmfiles:
3411 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003412 if os.path.exists(self.fn):
3413 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003414 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003415
3416 def assertLogFile(self, filename):
3417 "Assert a log file is there and register it for deletion"
3418 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003419 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003420 self.rmfiles.append(filename)
3421
3422
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003423class FileHandlerTest(BaseFileTest):
3424 def test_delay(self):
3425 os.unlink(self.fn)
3426 fh = logging.FileHandler(self.fn, delay=True)
3427 self.assertIsNone(fh.stream)
3428 self.assertFalse(os.path.exists(self.fn))
3429 fh.handle(logging.makeLogRecord({}))
3430 self.assertIsNotNone(fh.stream)
3431 self.assertTrue(os.path.exists(self.fn))
3432 fh.close()
3433
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003434class RotatingFileHandlerTest(BaseFileTest):
3435 def next_rec(self):
3436 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3437 self.next_message(), None, None, None)
3438
3439 def test_should_not_rollover(self):
3440 # If maxbytes is zero rollover never occurs
3441 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3442 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003443 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003444
3445 def test_should_rollover(self):
3446 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3447 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003448 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003449
3450 def test_file_created(self):
3451 # checks that the file is created and assumes it was created
3452 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003453 rh = logging.handlers.RotatingFileHandler(self.fn)
3454 rh.emit(self.next_rec())
3455 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003456 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003457
3458 def test_rollover_filenames(self):
3459 rh = logging.handlers.RotatingFileHandler(
3460 self.fn, backupCount=2, maxBytes=1)
3461 rh.emit(self.next_rec())
3462 self.assertLogFile(self.fn)
3463 rh.emit(self.next_rec())
3464 self.assertLogFile(self.fn + ".1")
3465 rh.emit(self.next_rec())
3466 self.assertLogFile(self.fn + ".2")
3467 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003468 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003469
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003470class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003471 # other test methods added below
3472 def test_rollover(self):
3473 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S')
3474 r = logging.makeLogRecord({'msg': 'testing'})
3475 fh.emit(r)
3476 self.assertLogFile(self.fn)
3477 time.sleep(1.0)
3478 fh.emit(r)
3479 now = datetime.datetime.now()
3480 prevsec = now - datetime.timedelta(seconds=1)
3481 suffix = prevsec.strftime(".%Y-%m-%d_%H-%M-%S")
3482 self.assertLogFile(self.fn + suffix)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003483
3484def secs(**kw):
3485 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3486
3487for when, exp in (('S', 1),
3488 ('M', 60),
3489 ('H', 60 * 60),
3490 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003491 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003492 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003493 ('W0', secs(days=4, hours=24)),
3494 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003495 def test_compute_rollover(self, when=when, exp=exp):
3496 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003497 self.fn, when=when, interval=1, backupCount=0, utc=True)
3498 currentTime = 0.0
3499 actual = rh.computeRollover(currentTime)
3500 if exp != actual:
3501 # Failures occur on some systems for MIDNIGHT and W0.
3502 # Print detailed calculation for MIDNIGHT so we can try to see
3503 # what's going on
3504 import time
3505 if when == 'MIDNIGHT':
3506 try:
3507 if rh.utc:
3508 t = time.gmtime(currentTime)
3509 else:
3510 t = time.localtime(currentTime)
3511 currentHour = t[3]
3512 currentMinute = t[4]
3513 currentSecond = t[5]
3514 # r is the number of seconds left between now and midnight
3515 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3516 currentMinute) * 60 +
3517 currentSecond)
3518 result = currentTime + r
3519 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3520 print('currentHour: %s' % currentHour, file=sys.stderr)
3521 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3522 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3523 print('r: %s' % r, file=sys.stderr)
3524 print('result: %s' % result, file=sys.stderr)
3525 except Exception:
3526 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3527 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003528 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003529 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3530
Christian Heimes180510d2008-03-03 19:15:45 +00003531# Set the locale to the platform-dependent default. I have no idea
3532# why the test does this, but in any case we save the current locale
3533# first and restore it at the end.
3534@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003535def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003536 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003537 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003538 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3539 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3540 ManagerTest, FormatterTest, BufferingFormatterTest,
3541 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3542 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3543 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3544 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003545 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003546 SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003547 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003548 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003549
Christian Heimes180510d2008-03-03 19:15:45 +00003550if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003551 test_main()