blob: 2e6de4cd64abf543089e901c181b323162e1eec6 [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):
Vinay Sajip0372e102011-05-05 12:59:14 +01001467 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001468 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001469 if self.command == 'POST':
1470 try:
1471 rlen = int(request.headers['Content-Length'])
1472 self.post_data = request.rfile.read(rlen)
1473 except:
1474 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001475 request.send_response(200)
1476 self.handled.set()
1477
1478 def test_output(self):
1479 # The log message sent to the SysLogHandler is properly received.
1480 logger = logging.getLogger("http")
Vinay Sajip0372e102011-05-05 12:59:14 +01001481 for method in ('GET', 'POST'):
1482 self.h_hdlr.method = method
1483 msg = "sp\xe4m"
1484 logger.error(msg)
1485 self.handled.wait()
1486 self.assertEqual(self.log_data.path, '/frob')
1487 self.assertEqual(self.command, method)
1488 if method == 'GET':
1489 d = parse_qs(self.log_data.query)
1490 else:
1491 d = parse_qs(self.post_data.decode('utf-8'))
1492 self.assertEqual(d['name'], ['http'])
1493 self.assertEqual(d['funcName'], ['test_output'])
1494 self.assertEqual(d['msg'], [msg])
Vinay Sajip7367d082011-05-02 13:17:27 +01001495
Christian Heimes180510d2008-03-03 19:15:45 +00001496class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001497
Christian Heimes180510d2008-03-03 19:15:45 +00001498 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001499
Christian Heimes180510d2008-03-03 19:15:45 +00001500 def setUp(self):
1501 """Create a dict to remember potentially destroyed objects."""
1502 BaseTest.setUp(self)
1503 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001504
Christian Heimes180510d2008-03-03 19:15:45 +00001505 def _watch_for_survival(self, *args):
1506 """Watch the given objects for survival, by creating weakrefs to
1507 them."""
1508 for obj in args:
1509 key = id(obj), repr(obj)
1510 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001511
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001512 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001513 """Assert that all objects watched for survival have survived."""
1514 # Trigger cycle breaking.
1515 gc.collect()
1516 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001517 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001518 if ref() is None:
1519 dead.append(repr_)
1520 if dead:
1521 self.fail("%d objects should have survived "
1522 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001523
Christian Heimes180510d2008-03-03 19:15:45 +00001524 def test_persistent_loggers(self):
1525 # Logger objects are persistent and retain their configuration, even
1526 # if visible references are destroyed.
1527 self.root_logger.setLevel(logging.INFO)
1528 foo = logging.getLogger("foo")
1529 self._watch_for_survival(foo)
1530 foo.setLevel(logging.DEBUG)
1531 self.root_logger.debug(self.next_message())
1532 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001533 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001534 ('foo', 'DEBUG', '2'),
1535 ])
1536 del foo
1537 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001538 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001539 # foo has retained its settings.
1540 bar = logging.getLogger("foo")
1541 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001542 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001543 ('foo', 'DEBUG', '2'),
1544 ('foo', 'DEBUG', '3'),
1545 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001546
Benjamin Petersonf91df042009-02-13 02:50:59 +00001547
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001548class EncodingTest(BaseTest):
1549 def test_encoding_plain_file(self):
1550 # In Python 2.x, a plain file object is treated as having no encoding.
1551 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001552 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1553 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001554 # the non-ascii data we write to the log.
1555 data = "foo\x80"
1556 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001557 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001558 log.addHandler(handler)
1559 try:
1560 # write non-ascii data to the log.
1561 log.warning(data)
1562 finally:
1563 log.removeHandler(handler)
1564 handler.close()
1565 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001566 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001567 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001568 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001569 finally:
1570 f.close()
1571 finally:
1572 if os.path.isfile(fn):
1573 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001574
Benjamin Petersonf91df042009-02-13 02:50:59 +00001575 def test_encoding_cyrillic_unicode(self):
1576 log = logging.getLogger("test")
1577 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1578 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1579 #Ensure it's written in a Cyrillic encoding
1580 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001581 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001582 stream = io.BytesIO()
1583 writer = writer_class(stream, 'strict')
1584 handler = logging.StreamHandler(writer)
1585 log.addHandler(handler)
1586 try:
1587 log.warning(message)
1588 finally:
1589 log.removeHandler(handler)
1590 handler.close()
1591 # check we wrote exactly those bytes, ignoring trailing \n etc
1592 s = stream.getvalue()
1593 #Compare against what the data should be when encoded in CP-1251
1594 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1595
1596
Georg Brandlf9734072008-12-07 15:30:06 +00001597class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001598
Georg Brandlf9734072008-12-07 15:30:06 +00001599 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001600 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001601 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001602 self.addCleanup(lambda: logging.captureWarnings(False))
1603 warnings.filterwarnings("always", category=UserWarning)
1604 stream = io.StringIO()
1605 h = logging.StreamHandler(stream)
1606 logger = logging.getLogger("py.warnings")
1607 logger.addHandler(h)
1608 warnings.warn("I'm warning you...")
1609 logger.removeHandler(h)
1610 s = stream.getvalue()
1611 h.close()
1612 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001613
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001614 #See if an explicit file uses the original implementation
1615 a_file = io.StringIO()
1616 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1617 a_file, "Dummy line")
1618 s = a_file.getvalue()
1619 a_file.close()
1620 self.assertEqual(s,
1621 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1622
1623 def test_warnings_no_handlers(self):
1624 with warnings.catch_warnings():
1625 logging.captureWarnings(True)
1626 self.addCleanup(lambda: logging.captureWarnings(False))
1627
1628 # confirm our assumption: no loggers are set
1629 logger = logging.getLogger("py.warnings")
1630 assert logger.handlers == []
1631
1632 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1633 self.assertTrue(len(logger.handlers) == 1)
1634 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001635
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001636
1637def formatFunc(format, datefmt=None):
1638 return logging.Formatter(format, datefmt)
1639
1640def handlerFunc():
1641 return logging.StreamHandler()
1642
1643class CustomHandler(logging.StreamHandler):
1644 pass
1645
1646class ConfigDictTest(BaseTest):
1647
1648 """Reading logging config from a dictionary."""
1649
1650 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1651
1652 # config0 is a standard configuration.
1653 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001654 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001655 'formatters': {
1656 'form1' : {
1657 'format' : '%(levelname)s ++ %(message)s',
1658 },
1659 },
1660 'handlers' : {
1661 'hand1' : {
1662 'class' : 'logging.StreamHandler',
1663 'formatter' : 'form1',
1664 'level' : 'NOTSET',
1665 'stream' : 'ext://sys.stdout',
1666 },
1667 },
1668 'root' : {
1669 'level' : 'WARNING',
1670 'handlers' : ['hand1'],
1671 },
1672 }
1673
1674 # config1 adds a little to the standard configuration.
1675 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001676 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001677 'formatters': {
1678 'form1' : {
1679 'format' : '%(levelname)s ++ %(message)s',
1680 },
1681 },
1682 'handlers' : {
1683 'hand1' : {
1684 'class' : 'logging.StreamHandler',
1685 'formatter' : 'form1',
1686 'level' : 'NOTSET',
1687 'stream' : 'ext://sys.stdout',
1688 },
1689 },
1690 'loggers' : {
1691 'compiler.parser' : {
1692 'level' : 'DEBUG',
1693 'handlers' : ['hand1'],
1694 },
1695 },
1696 'root' : {
1697 'level' : 'WARNING',
1698 },
1699 }
1700
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001701 # config1a moves the handler to the root. Used with config8a
1702 config1a = {
1703 'version': 1,
1704 'formatters': {
1705 'form1' : {
1706 'format' : '%(levelname)s ++ %(message)s',
1707 },
1708 },
1709 'handlers' : {
1710 'hand1' : {
1711 'class' : 'logging.StreamHandler',
1712 'formatter' : 'form1',
1713 'level' : 'NOTSET',
1714 'stream' : 'ext://sys.stdout',
1715 },
1716 },
1717 'loggers' : {
1718 'compiler.parser' : {
1719 'level' : 'DEBUG',
1720 },
1721 },
1722 'root' : {
1723 'level' : 'WARNING',
1724 'handlers' : ['hand1'],
1725 },
1726 }
1727
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001728 # config2 has a subtle configuration error that should be reported
1729 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001730 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001731 'formatters': {
1732 'form1' : {
1733 'format' : '%(levelname)s ++ %(message)s',
1734 },
1735 },
1736 'handlers' : {
1737 'hand1' : {
1738 'class' : 'logging.StreamHandler',
1739 'formatter' : 'form1',
1740 'level' : 'NOTSET',
1741 'stream' : 'ext://sys.stdbout',
1742 },
1743 },
1744 'loggers' : {
1745 'compiler.parser' : {
1746 'level' : 'DEBUG',
1747 'handlers' : ['hand1'],
1748 },
1749 },
1750 'root' : {
1751 'level' : 'WARNING',
1752 },
1753 }
1754
1755 #As config1 but with a misspelt level on a handler
1756 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001757 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001758 'formatters': {
1759 'form1' : {
1760 'format' : '%(levelname)s ++ %(message)s',
1761 },
1762 },
1763 'handlers' : {
1764 'hand1' : {
1765 'class' : 'logging.StreamHandler',
1766 'formatter' : 'form1',
1767 'level' : 'NTOSET',
1768 'stream' : 'ext://sys.stdout',
1769 },
1770 },
1771 'loggers' : {
1772 'compiler.parser' : {
1773 'level' : 'DEBUG',
1774 'handlers' : ['hand1'],
1775 },
1776 },
1777 'root' : {
1778 'level' : 'WARNING',
1779 },
1780 }
1781
1782
1783 #As config1 but with a misspelt level on a logger
1784 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001785 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001786 'formatters': {
1787 'form1' : {
1788 'format' : '%(levelname)s ++ %(message)s',
1789 },
1790 },
1791 'handlers' : {
1792 'hand1' : {
1793 'class' : 'logging.StreamHandler',
1794 'formatter' : 'form1',
1795 'level' : 'NOTSET',
1796 'stream' : 'ext://sys.stdout',
1797 },
1798 },
1799 'loggers' : {
1800 'compiler.parser' : {
1801 'level' : 'DEBUG',
1802 'handlers' : ['hand1'],
1803 },
1804 },
1805 'root' : {
1806 'level' : 'WRANING',
1807 },
1808 }
1809
1810 # config3 has a less subtle configuration error
1811 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001812 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001813 'formatters': {
1814 'form1' : {
1815 'format' : '%(levelname)s ++ %(message)s',
1816 },
1817 },
1818 'handlers' : {
1819 'hand1' : {
1820 'class' : 'logging.StreamHandler',
1821 'formatter' : 'misspelled_name',
1822 'level' : 'NOTSET',
1823 'stream' : 'ext://sys.stdout',
1824 },
1825 },
1826 'loggers' : {
1827 'compiler.parser' : {
1828 'level' : 'DEBUG',
1829 'handlers' : ['hand1'],
1830 },
1831 },
1832 'root' : {
1833 'level' : 'WARNING',
1834 },
1835 }
1836
1837 # config4 specifies a custom formatter class to be loaded
1838 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001839 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001840 'formatters': {
1841 'form1' : {
1842 '()' : __name__ + '.ExceptionFormatter',
1843 'format' : '%(levelname)s:%(name)s:%(message)s',
1844 },
1845 },
1846 'handlers' : {
1847 'hand1' : {
1848 'class' : 'logging.StreamHandler',
1849 'formatter' : 'form1',
1850 'level' : 'NOTSET',
1851 'stream' : 'ext://sys.stdout',
1852 },
1853 },
1854 'root' : {
1855 'level' : 'NOTSET',
1856 'handlers' : ['hand1'],
1857 },
1858 }
1859
1860 # As config4 but using an actual callable rather than a string
1861 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001862 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001863 'formatters': {
1864 'form1' : {
1865 '()' : ExceptionFormatter,
1866 'format' : '%(levelname)s:%(name)s:%(message)s',
1867 },
1868 'form2' : {
1869 '()' : __name__ + '.formatFunc',
1870 'format' : '%(levelname)s:%(name)s:%(message)s',
1871 },
1872 'form3' : {
1873 '()' : formatFunc,
1874 'format' : '%(levelname)s:%(name)s:%(message)s',
1875 },
1876 },
1877 'handlers' : {
1878 'hand1' : {
1879 'class' : 'logging.StreamHandler',
1880 'formatter' : 'form1',
1881 'level' : 'NOTSET',
1882 'stream' : 'ext://sys.stdout',
1883 },
1884 'hand2' : {
1885 '()' : handlerFunc,
1886 },
1887 },
1888 'root' : {
1889 'level' : 'NOTSET',
1890 'handlers' : ['hand1'],
1891 },
1892 }
1893
1894 # config5 specifies a custom handler class to be loaded
1895 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001896 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001897 'formatters': {
1898 'form1' : {
1899 'format' : '%(levelname)s ++ %(message)s',
1900 },
1901 },
1902 'handlers' : {
1903 'hand1' : {
1904 'class' : __name__ + '.CustomHandler',
1905 'formatter' : 'form1',
1906 'level' : 'NOTSET',
1907 'stream' : 'ext://sys.stdout',
1908 },
1909 },
1910 'loggers' : {
1911 'compiler.parser' : {
1912 'level' : 'DEBUG',
1913 'handlers' : ['hand1'],
1914 },
1915 },
1916 'root' : {
1917 'level' : 'WARNING',
1918 },
1919 }
1920
1921 # config6 specifies a custom handler class to be loaded
1922 # but has bad arguments
1923 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001924 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001925 'formatters': {
1926 'form1' : {
1927 'format' : '%(levelname)s ++ %(message)s',
1928 },
1929 },
1930 'handlers' : {
1931 'hand1' : {
1932 'class' : __name__ + '.CustomHandler',
1933 'formatter' : 'form1',
1934 'level' : 'NOTSET',
1935 'stream' : 'ext://sys.stdout',
1936 '9' : 'invalid parameter name',
1937 },
1938 },
1939 'loggers' : {
1940 'compiler.parser' : {
1941 'level' : 'DEBUG',
1942 'handlers' : ['hand1'],
1943 },
1944 },
1945 'root' : {
1946 'level' : 'WARNING',
1947 },
1948 }
1949
1950 #config 7 does not define compiler.parser but defines compiler.lexer
1951 #so compiler.parser should be disabled after applying it
1952 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001953 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001954 'formatters': {
1955 'form1' : {
1956 'format' : '%(levelname)s ++ %(message)s',
1957 },
1958 },
1959 'handlers' : {
1960 'hand1' : {
1961 'class' : 'logging.StreamHandler',
1962 'formatter' : 'form1',
1963 'level' : 'NOTSET',
1964 'stream' : 'ext://sys.stdout',
1965 },
1966 },
1967 'loggers' : {
1968 'compiler.lexer' : {
1969 'level' : 'DEBUG',
1970 'handlers' : ['hand1'],
1971 },
1972 },
1973 'root' : {
1974 'level' : 'WARNING',
1975 },
1976 }
1977
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001978 # config8 defines both compiler and compiler.lexer
1979 # so compiler.parser should not be disabled (since
1980 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001981 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001982 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001983 'disable_existing_loggers' : False,
1984 'formatters': {
1985 'form1' : {
1986 'format' : '%(levelname)s ++ %(message)s',
1987 },
1988 },
1989 'handlers' : {
1990 'hand1' : {
1991 'class' : 'logging.StreamHandler',
1992 'formatter' : 'form1',
1993 'level' : 'NOTSET',
1994 'stream' : 'ext://sys.stdout',
1995 },
1996 },
1997 'loggers' : {
1998 'compiler' : {
1999 'level' : 'DEBUG',
2000 'handlers' : ['hand1'],
2001 },
2002 'compiler.lexer' : {
2003 },
2004 },
2005 'root' : {
2006 'level' : 'WARNING',
2007 },
2008 }
2009
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002010 # config8a disables existing loggers
2011 config8a = {
2012 'version': 1,
2013 'disable_existing_loggers' : True,
2014 'formatters': {
2015 'form1' : {
2016 'format' : '%(levelname)s ++ %(message)s',
2017 },
2018 },
2019 'handlers' : {
2020 'hand1' : {
2021 'class' : 'logging.StreamHandler',
2022 'formatter' : 'form1',
2023 'level' : 'NOTSET',
2024 'stream' : 'ext://sys.stdout',
2025 },
2026 },
2027 'loggers' : {
2028 'compiler' : {
2029 'level' : 'DEBUG',
2030 'handlers' : ['hand1'],
2031 },
2032 'compiler.lexer' : {
2033 },
2034 },
2035 'root' : {
2036 'level' : 'WARNING',
2037 },
2038 }
2039
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002040 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002041 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002042 'formatters': {
2043 'form1' : {
2044 'format' : '%(levelname)s ++ %(message)s',
2045 },
2046 },
2047 'handlers' : {
2048 'hand1' : {
2049 'class' : 'logging.StreamHandler',
2050 'formatter' : 'form1',
2051 'level' : 'WARNING',
2052 'stream' : 'ext://sys.stdout',
2053 },
2054 },
2055 'loggers' : {
2056 'compiler.parser' : {
2057 'level' : 'WARNING',
2058 'handlers' : ['hand1'],
2059 },
2060 },
2061 'root' : {
2062 'level' : 'NOTSET',
2063 },
2064 }
2065
2066 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002067 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002068 'incremental' : True,
2069 'handlers' : {
2070 'hand1' : {
2071 'level' : 'WARNING',
2072 },
2073 },
2074 'loggers' : {
2075 'compiler.parser' : {
2076 'level' : 'INFO',
2077 },
2078 },
2079 }
2080
2081 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002082 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002083 'incremental' : True,
2084 'handlers' : {
2085 'hand1' : {
2086 'level' : 'INFO',
2087 },
2088 },
2089 'loggers' : {
2090 'compiler.parser' : {
2091 'level' : 'INFO',
2092 },
2093 },
2094 }
2095
2096 #As config1 but with a filter added
2097 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002098 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002099 'formatters': {
2100 'form1' : {
2101 'format' : '%(levelname)s ++ %(message)s',
2102 },
2103 },
2104 'filters' : {
2105 'filt1' : {
2106 'name' : 'compiler.parser',
2107 },
2108 },
2109 'handlers' : {
2110 'hand1' : {
2111 'class' : 'logging.StreamHandler',
2112 'formatter' : 'form1',
2113 'level' : 'NOTSET',
2114 'stream' : 'ext://sys.stdout',
2115 'filters' : ['filt1'],
2116 },
2117 },
2118 'loggers' : {
2119 'compiler.parser' : {
2120 'level' : 'DEBUG',
2121 'filters' : ['filt1'],
2122 },
2123 },
2124 'root' : {
2125 'level' : 'WARNING',
2126 'handlers' : ['hand1'],
2127 },
2128 }
2129
2130 #As config1 but using cfg:// references
2131 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002132 'version': 1,
2133 'true_formatters': {
2134 'form1' : {
2135 'format' : '%(levelname)s ++ %(message)s',
2136 },
2137 },
2138 'handler_configs': {
2139 'hand1' : {
2140 'class' : 'logging.StreamHandler',
2141 'formatter' : 'form1',
2142 'level' : 'NOTSET',
2143 'stream' : 'ext://sys.stdout',
2144 },
2145 },
2146 'formatters' : 'cfg://true_formatters',
2147 'handlers' : {
2148 'hand1' : 'cfg://handler_configs[hand1]',
2149 },
2150 'loggers' : {
2151 'compiler.parser' : {
2152 'level' : 'DEBUG',
2153 'handlers' : ['hand1'],
2154 },
2155 },
2156 'root' : {
2157 'level' : 'WARNING',
2158 },
2159 }
2160
2161 #As config11 but missing the version key
2162 config12 = {
2163 'true_formatters': {
2164 'form1' : {
2165 'format' : '%(levelname)s ++ %(message)s',
2166 },
2167 },
2168 'handler_configs': {
2169 'hand1' : {
2170 'class' : 'logging.StreamHandler',
2171 'formatter' : 'form1',
2172 'level' : 'NOTSET',
2173 'stream' : 'ext://sys.stdout',
2174 },
2175 },
2176 'formatters' : 'cfg://true_formatters',
2177 'handlers' : {
2178 'hand1' : 'cfg://handler_configs[hand1]',
2179 },
2180 'loggers' : {
2181 'compiler.parser' : {
2182 'level' : 'DEBUG',
2183 'handlers' : ['hand1'],
2184 },
2185 },
2186 'root' : {
2187 'level' : 'WARNING',
2188 },
2189 }
2190
2191 #As config11 but using an unsupported version
2192 config13 = {
2193 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002194 'true_formatters': {
2195 'form1' : {
2196 'format' : '%(levelname)s ++ %(message)s',
2197 },
2198 },
2199 'handler_configs': {
2200 'hand1' : {
2201 'class' : 'logging.StreamHandler',
2202 'formatter' : 'form1',
2203 'level' : 'NOTSET',
2204 'stream' : 'ext://sys.stdout',
2205 },
2206 },
2207 'formatters' : 'cfg://true_formatters',
2208 'handlers' : {
2209 'hand1' : 'cfg://handler_configs[hand1]',
2210 },
2211 'loggers' : {
2212 'compiler.parser' : {
2213 'level' : 'DEBUG',
2214 'handlers' : ['hand1'],
2215 },
2216 },
2217 'root' : {
2218 'level' : 'WARNING',
2219 },
2220 }
2221
2222 def apply_config(self, conf):
2223 logging.config.dictConfig(conf)
2224
2225 def test_config0_ok(self):
2226 # A simple config which overrides the default settings.
2227 with captured_stdout() as output:
2228 self.apply_config(self.config0)
2229 logger = logging.getLogger()
2230 # Won't output anything
2231 logger.info(self.next_message())
2232 # Outputs a message
2233 logger.error(self.next_message())
2234 self.assert_log_lines([
2235 ('ERROR', '2'),
2236 ], stream=output)
2237 # Original logger output is empty.
2238 self.assert_log_lines([])
2239
2240 def test_config1_ok(self, config=config1):
2241 # A config defining a sub-parser as well.
2242 with captured_stdout() as output:
2243 self.apply_config(config)
2244 logger = logging.getLogger("compiler.parser")
2245 # Both will output a message
2246 logger.info(self.next_message())
2247 logger.error(self.next_message())
2248 self.assert_log_lines([
2249 ('INFO', '1'),
2250 ('ERROR', '2'),
2251 ], stream=output)
2252 # Original logger output is empty.
2253 self.assert_log_lines([])
2254
2255 def test_config2_failure(self):
2256 # A simple config which overrides the default settings.
2257 self.assertRaises(Exception, self.apply_config, self.config2)
2258
2259 def test_config2a_failure(self):
2260 # A simple config which overrides the default settings.
2261 self.assertRaises(Exception, self.apply_config, self.config2a)
2262
2263 def test_config2b_failure(self):
2264 # A simple config which overrides the default settings.
2265 self.assertRaises(Exception, self.apply_config, self.config2b)
2266
2267 def test_config3_failure(self):
2268 # A simple config which overrides the default settings.
2269 self.assertRaises(Exception, self.apply_config, self.config3)
2270
2271 def test_config4_ok(self):
2272 # A config specifying a custom formatter class.
2273 with captured_stdout() as output:
2274 self.apply_config(self.config4)
2275 #logger = logging.getLogger()
2276 try:
2277 raise RuntimeError()
2278 except RuntimeError:
2279 logging.exception("just testing")
2280 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002281 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002282 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2283 # Original logger output is empty
2284 self.assert_log_lines([])
2285
2286 def test_config4a_ok(self):
2287 # A config specifying a custom formatter class.
2288 with captured_stdout() as output:
2289 self.apply_config(self.config4a)
2290 #logger = logging.getLogger()
2291 try:
2292 raise RuntimeError()
2293 except RuntimeError:
2294 logging.exception("just testing")
2295 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002296 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002297 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2298 # Original logger output is empty
2299 self.assert_log_lines([])
2300
2301 def test_config5_ok(self):
2302 self.test_config1_ok(config=self.config5)
2303
2304 def test_config6_failure(self):
2305 self.assertRaises(Exception, self.apply_config, self.config6)
2306
2307 def test_config7_ok(self):
2308 with captured_stdout() as output:
2309 self.apply_config(self.config1)
2310 logger = logging.getLogger("compiler.parser")
2311 # Both will output a message
2312 logger.info(self.next_message())
2313 logger.error(self.next_message())
2314 self.assert_log_lines([
2315 ('INFO', '1'),
2316 ('ERROR', '2'),
2317 ], stream=output)
2318 # Original logger output is empty.
2319 self.assert_log_lines([])
2320 with captured_stdout() as output:
2321 self.apply_config(self.config7)
2322 logger = logging.getLogger("compiler.parser")
2323 self.assertTrue(logger.disabled)
2324 logger = logging.getLogger("compiler.lexer")
2325 # Both will output a message
2326 logger.info(self.next_message())
2327 logger.error(self.next_message())
2328 self.assert_log_lines([
2329 ('INFO', '3'),
2330 ('ERROR', '4'),
2331 ], stream=output)
2332 # Original logger output is empty.
2333 self.assert_log_lines([])
2334
2335 #Same as test_config_7_ok but don't disable old loggers.
2336 def test_config_8_ok(self):
2337 with captured_stdout() as output:
2338 self.apply_config(self.config1)
2339 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002340 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002341 logger.info(self.next_message())
2342 logger.error(self.next_message())
2343 self.assert_log_lines([
2344 ('INFO', '1'),
2345 ('ERROR', '2'),
2346 ], stream=output)
2347 # Original logger output is empty.
2348 self.assert_log_lines([])
2349 with captured_stdout() as output:
2350 self.apply_config(self.config8)
2351 logger = logging.getLogger("compiler.parser")
2352 self.assertFalse(logger.disabled)
2353 # Both will output a message
2354 logger.info(self.next_message())
2355 logger.error(self.next_message())
2356 logger = logging.getLogger("compiler.lexer")
2357 # Both will output a message
2358 logger.info(self.next_message())
2359 logger.error(self.next_message())
2360 self.assert_log_lines([
2361 ('INFO', '3'),
2362 ('ERROR', '4'),
2363 ('INFO', '5'),
2364 ('ERROR', '6'),
2365 ], stream=output)
2366 # Original logger output is empty.
2367 self.assert_log_lines([])
2368
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002369 def test_config_8a_ok(self):
2370 with captured_stdout() as output:
2371 self.apply_config(self.config1a)
2372 logger = logging.getLogger("compiler.parser")
2373 # See issue #11424. compiler-hyphenated sorts
2374 # between compiler and compiler.xyz and this
2375 # was preventing compiler.xyz from being included
2376 # in the child loggers of compiler because of an
2377 # overzealous loop termination condition.
2378 hyphenated = logging.getLogger('compiler-hyphenated')
2379 # All will output a message
2380 logger.info(self.next_message())
2381 logger.error(self.next_message())
2382 hyphenated.critical(self.next_message())
2383 self.assert_log_lines([
2384 ('INFO', '1'),
2385 ('ERROR', '2'),
2386 ('CRITICAL', '3'),
2387 ], stream=output)
2388 # Original logger output is empty.
2389 self.assert_log_lines([])
2390 with captured_stdout() as output:
2391 self.apply_config(self.config8a)
2392 logger = logging.getLogger("compiler.parser")
2393 self.assertFalse(logger.disabled)
2394 # Both will output a message
2395 logger.info(self.next_message())
2396 logger.error(self.next_message())
2397 logger = logging.getLogger("compiler.lexer")
2398 # Both will output a message
2399 logger.info(self.next_message())
2400 logger.error(self.next_message())
2401 # Will not appear
2402 hyphenated.critical(self.next_message())
2403 self.assert_log_lines([
2404 ('INFO', '4'),
2405 ('ERROR', '5'),
2406 ('INFO', '6'),
2407 ('ERROR', '7'),
2408 ], stream=output)
2409 # Original logger output is empty.
2410 self.assert_log_lines([])
2411
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002412 def test_config_9_ok(self):
2413 with captured_stdout() as output:
2414 self.apply_config(self.config9)
2415 logger = logging.getLogger("compiler.parser")
2416 #Nothing will be output since both handler and logger are set to WARNING
2417 logger.info(self.next_message())
2418 self.assert_log_lines([], stream=output)
2419 self.apply_config(self.config9a)
2420 #Nothing will be output since both handler is still set to WARNING
2421 logger.info(self.next_message())
2422 self.assert_log_lines([], stream=output)
2423 self.apply_config(self.config9b)
2424 #Message should now be output
2425 logger.info(self.next_message())
2426 self.assert_log_lines([
2427 ('INFO', '3'),
2428 ], stream=output)
2429
2430 def test_config_10_ok(self):
2431 with captured_stdout() as output:
2432 self.apply_config(self.config10)
2433 logger = logging.getLogger("compiler.parser")
2434 logger.warning(self.next_message())
2435 logger = logging.getLogger('compiler')
2436 #Not output, because filtered
2437 logger.warning(self.next_message())
2438 logger = logging.getLogger('compiler.lexer')
2439 #Not output, because filtered
2440 logger.warning(self.next_message())
2441 logger = logging.getLogger("compiler.parser.codegen")
2442 #Output, as not filtered
2443 logger.error(self.next_message())
2444 self.assert_log_lines([
2445 ('WARNING', '1'),
2446 ('ERROR', '4'),
2447 ], stream=output)
2448
2449 def test_config11_ok(self):
2450 self.test_config1_ok(self.config11)
2451
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002452 def test_config12_failure(self):
2453 self.assertRaises(Exception, self.apply_config, self.config12)
2454
2455 def test_config13_failure(self):
2456 self.assertRaises(Exception, self.apply_config, self.config13)
2457
Victor Stinner45df8202010-04-28 22:31:17 +00002458 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002459 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002460 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002461 # Ask for a randomly assigned port (by using port 0)
2462 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002463 t.start()
2464 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002465 # Now get the port allocated
2466 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002467 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002468 try:
2469 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2470 sock.settimeout(2.0)
2471 sock.connect(('localhost', port))
2472
2473 slen = struct.pack('>L', len(text))
2474 s = slen + text
2475 sentsofar = 0
2476 left = len(s)
2477 while left > 0:
2478 sent = sock.send(s[sentsofar:])
2479 sentsofar += sent
2480 left -= sent
2481 sock.close()
2482 finally:
2483 t.ready.wait(2.0)
2484 logging.config.stopListening()
2485 t.join(2.0)
2486
2487 def test_listen_config_10_ok(self):
2488 with captured_stdout() as output:
2489 self.setup_via_listener(json.dumps(self.config10))
2490 logger = logging.getLogger("compiler.parser")
2491 logger.warning(self.next_message())
2492 logger = logging.getLogger('compiler')
2493 #Not output, because filtered
2494 logger.warning(self.next_message())
2495 logger = logging.getLogger('compiler.lexer')
2496 #Not output, because filtered
2497 logger.warning(self.next_message())
2498 logger = logging.getLogger("compiler.parser.codegen")
2499 #Output, as not filtered
2500 logger.error(self.next_message())
2501 self.assert_log_lines([
2502 ('WARNING', '1'),
2503 ('ERROR', '4'),
2504 ], stream=output)
2505
2506 def test_listen_config_1_ok(self):
2507 with captured_stdout() as output:
2508 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2509 logger = logging.getLogger("compiler.parser")
2510 # Both will output a message
2511 logger.info(self.next_message())
2512 logger.error(self.next_message())
2513 self.assert_log_lines([
2514 ('INFO', '1'),
2515 ('ERROR', '2'),
2516 ], stream=output)
2517 # Original logger output is empty.
2518 self.assert_log_lines([])
2519
Vinay Sajip373baef2011-04-26 20:05:24 +01002520 def test_baseconfig(self):
2521 d = {
2522 'atuple': (1, 2, 3),
2523 'alist': ['a', 'b', 'c'],
2524 'adict': {'d': 'e', 'f': 3 },
2525 'nest1': ('g', ('h', 'i'), 'j'),
2526 'nest2': ['k', ['l', 'm'], 'n'],
2527 'nest3': ['o', 'cfg://alist', 'p'],
2528 }
2529 bc = logging.config.BaseConfigurator(d)
2530 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2531 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2532 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2533 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2534 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2535 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2536 v = bc.convert('cfg://nest3')
2537 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2538 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2539 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2540 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002541
2542class ManagerTest(BaseTest):
2543 def test_manager_loggerclass(self):
2544 logged = []
2545
2546 class MyLogger(logging.Logger):
2547 def _log(self, level, msg, args, exc_info=None, extra=None):
2548 logged.append(msg)
2549
2550 man = logging.Manager(None)
2551 self.assertRaises(TypeError, man.setLoggerClass, int)
2552 man.setLoggerClass(MyLogger)
2553 logger = man.getLogger('test')
2554 logger.warning('should appear in logged')
2555 logging.warning('should not appear in logged')
2556
2557 self.assertEqual(logged, ['should appear in logged'])
2558
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002559 def test_set_log_record_factory(self):
2560 man = logging.Manager(None)
2561 expected = object()
2562 man.setLogRecordFactory(expected)
2563 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002564
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002565class ChildLoggerTest(BaseTest):
2566 def test_child_loggers(self):
2567 r = logging.getLogger()
2568 l1 = logging.getLogger('abc')
2569 l2 = logging.getLogger('def.ghi')
2570 c1 = r.getChild('xyz')
2571 c2 = r.getChild('uvw.xyz')
2572 self.assertTrue(c1 is logging.getLogger('xyz'))
2573 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2574 c1 = l1.getChild('def')
2575 c2 = c1.getChild('ghi')
2576 c3 = l1.getChild('def.ghi')
2577 self.assertTrue(c1 is logging.getLogger('abc.def'))
2578 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2579 self.assertTrue(c2 is c3)
2580
2581
Vinay Sajip6fac8172010-10-19 20:44:14 +00002582class DerivedLogRecord(logging.LogRecord):
2583 pass
2584
Vinay Sajip61561522010-12-03 11:50:38 +00002585class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002586
2587 def setUp(self):
2588 class CheckingFilter(logging.Filter):
2589 def __init__(self, cls):
2590 self.cls = cls
2591
2592 def filter(self, record):
2593 t = type(record)
2594 if t is not self.cls:
2595 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2596 self.cls)
2597 raise TypeError(msg)
2598 return True
2599
2600 BaseTest.setUp(self)
2601 self.filter = CheckingFilter(DerivedLogRecord)
2602 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002603 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002604
2605 def tearDown(self):
2606 self.root_logger.removeFilter(self.filter)
2607 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002608 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002609
2610 def test_logrecord_class(self):
2611 self.assertRaises(TypeError, self.root_logger.warning,
2612 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002613 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002614 self.root_logger.error(self.next_message())
2615 self.assert_log_lines([
2616 ('root', 'ERROR', '2'),
2617 ])
2618
2619
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002620class QueueHandlerTest(BaseTest):
2621 # Do not bother with a logger name group.
2622 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2623
2624 def setUp(self):
2625 BaseTest.setUp(self)
2626 self.queue = queue.Queue(-1)
2627 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2628 self.que_logger = logging.getLogger('que')
2629 self.que_logger.propagate = False
2630 self.que_logger.setLevel(logging.WARNING)
2631 self.que_logger.addHandler(self.que_hdlr)
2632
2633 def tearDown(self):
2634 self.que_hdlr.close()
2635 BaseTest.tearDown(self)
2636
2637 def test_queue_handler(self):
2638 self.que_logger.debug(self.next_message())
2639 self.assertRaises(queue.Empty, self.queue.get_nowait)
2640 self.que_logger.info(self.next_message())
2641 self.assertRaises(queue.Empty, self.queue.get_nowait)
2642 msg = self.next_message()
2643 self.que_logger.warning(msg)
2644 data = self.queue.get_nowait()
2645 self.assertTrue(isinstance(data, logging.LogRecord))
2646 self.assertEqual(data.name, self.que_logger.name)
2647 self.assertEqual((data.msg, data.args), (msg, None))
2648
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002649 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2650 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002651 def test_queue_listener(self):
2652 handler = TestHandler(Matcher())
2653 listener = logging.handlers.QueueListener(self.queue, handler)
2654 listener.start()
2655 try:
2656 self.que_logger.warning(self.next_message())
2657 self.que_logger.error(self.next_message())
2658 self.que_logger.critical(self.next_message())
2659 finally:
2660 listener.stop()
2661 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2662 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2663 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2664
Vinay Sajip37eb3382011-04-26 20:26:41 +01002665ZERO = datetime.timedelta(0)
2666
2667class UTC(datetime.tzinfo):
2668 def utcoffset(self, dt):
2669 return ZERO
2670
2671 dst = utcoffset
2672
2673 def tzname(self, dt):
2674 return 'UTC'
2675
2676utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002677
Vinay Sajipa39c5712010-10-25 13:57:39 +00002678class FormatterTest(unittest.TestCase):
2679 def setUp(self):
2680 self.common = {
2681 'name': 'formatter.test',
2682 'level': logging.DEBUG,
2683 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2684 'lineno': 42,
2685 'exc_info': None,
2686 'func': None,
2687 'msg': 'Message with %d %s',
2688 'args': (2, 'placeholders'),
2689 }
2690 self.variants = {
2691 }
2692
2693 def get_record(self, name=None):
2694 result = dict(self.common)
2695 if name is not None:
2696 result.update(self.variants[name])
2697 return logging.makeLogRecord(result)
2698
2699 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002700 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002701 r = self.get_record()
2702 f = logging.Formatter('${%(message)s}')
2703 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2704 f = logging.Formatter('%(random)s')
2705 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002706 self.assertFalse(f.usesTime())
2707 f = logging.Formatter('%(asctime)s')
2708 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002709 f = logging.Formatter('%(asctime)-15s')
2710 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002711 f = logging.Formatter('asctime')
2712 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002713
2714 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002715 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002716 r = self.get_record()
2717 f = logging.Formatter('$%{message}%$', style='{')
2718 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2719 f = logging.Formatter('{random}', style='{')
2720 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002721 self.assertFalse(f.usesTime())
2722 f = logging.Formatter('{asctime}', style='{')
2723 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002724 f = logging.Formatter('{asctime!s:15}', style='{')
2725 self.assertTrue(f.usesTime())
2726 f = logging.Formatter('{asctime:15}', style='{')
2727 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002728 f = logging.Formatter('asctime', style='{')
2729 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002730
2731 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002732 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002733 r = self.get_record()
2734 f = logging.Formatter('$message', style='$')
2735 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2736 f = logging.Formatter('$$%${message}%$$', style='$')
2737 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2738 f = logging.Formatter('${random}', style='$')
2739 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002740 self.assertFalse(f.usesTime())
2741 f = logging.Formatter('${asctime}', style='$')
2742 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002743 f = logging.Formatter('${asctime', style='$')
2744 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002745 f = logging.Formatter('$asctime', style='$')
2746 self.assertTrue(f.usesTime())
2747 f = logging.Formatter('asctime', style='$')
2748 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002749
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002750 def test_invalid_style(self):
2751 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2752
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002753 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002754 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002755 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002756 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002757 r.msecs = 123
2758 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002759 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002760 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2761 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002762 f.format(r)
2763 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2764
2765class TestBufferingFormatter(logging.BufferingFormatter):
2766 def formatHeader(self, records):
2767 return '[(%d)' % len(records)
2768
2769 def formatFooter(self, records):
2770 return '(%d)]' % len(records)
2771
2772class BufferingFormatterTest(unittest.TestCase):
2773 def setUp(self):
2774 self.records = [
2775 logging.makeLogRecord({'msg': 'one'}),
2776 logging.makeLogRecord({'msg': 'two'}),
2777 ]
2778
2779 def test_default(self):
2780 f = logging.BufferingFormatter()
2781 self.assertEqual('', f.format([]))
2782 self.assertEqual('onetwo', f.format(self.records))
2783
2784 def test_custom(self):
2785 f = TestBufferingFormatter()
2786 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2787 lf = logging.Formatter('<%(message)s>')
2788 f = TestBufferingFormatter(lf)
2789 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002790
2791class ExceptionTest(BaseTest):
2792 def test_formatting(self):
2793 r = self.root_logger
2794 h = RecordingHandler()
2795 r.addHandler(h)
2796 try:
2797 raise RuntimeError('deliberate mistake')
2798 except:
2799 logging.exception('failed', stack_info=True)
2800 r.removeHandler(h)
2801 h.close()
2802 r = h.records[0]
2803 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2804 'call last):\n'))
2805 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2806 'deliberate mistake'))
2807 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2808 'call last):\n'))
2809 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2810 'stack_info=True)'))
2811
2812
Vinay Sajip5a27d402010-12-10 11:42:57 +00002813class LastResortTest(BaseTest):
2814 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002815 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002816 root = self.root_logger
2817 root.removeHandler(self.root_hdlr)
2818 old_stderr = sys.stderr
2819 old_lastresort = logging.lastResort
2820 old_raise_exceptions = logging.raiseExceptions
2821 try:
2822 sys.stderr = sio = io.StringIO()
2823 root.warning('This is your final chance!')
2824 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2825 #No handlers and no last resort, so 'No handlers' message
2826 logging.lastResort = None
2827 sys.stderr = sio = io.StringIO()
2828 root.warning('This is your final chance!')
2829 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2830 # 'No handlers' message only printed once
2831 sys.stderr = sio = io.StringIO()
2832 root.warning('This is your final chance!')
2833 self.assertEqual(sio.getvalue(), '')
2834 root.manager.emittedNoHandlerWarning = False
2835 #If raiseExceptions is False, no message is printed
2836 logging.raiseExceptions = False
2837 sys.stderr = sio = io.StringIO()
2838 root.warning('This is your final chance!')
2839 self.assertEqual(sio.getvalue(), '')
2840 finally:
2841 sys.stderr = old_stderr
2842 root.addHandler(self.root_hdlr)
2843 logging.lastResort = old_lastresort
2844 logging.raiseExceptions = old_raise_exceptions
2845
2846
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002847class FakeHandler:
2848
2849 def __init__(self, identifier, called):
2850 for method in ('acquire', 'flush', 'close', 'release'):
2851 setattr(self, method, self.record_call(identifier, method, called))
2852
2853 def record_call(self, identifier, method_name, called):
2854 def inner():
2855 called.append('{} - {}'.format(identifier, method_name))
2856 return inner
2857
2858
2859class RecordingHandler(logging.NullHandler):
2860
2861 def __init__(self, *args, **kwargs):
2862 super(RecordingHandler, self).__init__(*args, **kwargs)
2863 self.records = []
2864
2865 def handle(self, record):
2866 """Keep track of all the emitted records."""
2867 self.records.append(record)
2868
2869
2870class ShutdownTest(BaseTest):
2871
Vinay Sajip5e66b162011-04-20 15:41:14 +01002872 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002873
2874 def setUp(self):
2875 super(ShutdownTest, self).setUp()
2876 self.called = []
2877
2878 raise_exceptions = logging.raiseExceptions
2879 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2880
2881 def raise_error(self, error):
2882 def inner():
2883 raise error()
2884 return inner
2885
2886 def test_no_failure(self):
2887 # create some fake handlers
2888 handler0 = FakeHandler(0, self.called)
2889 handler1 = FakeHandler(1, self.called)
2890 handler2 = FakeHandler(2, self.called)
2891
2892 # create live weakref to those handlers
2893 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2894
2895 logging.shutdown(handlerList=list(handlers))
2896
2897 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2898 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2899 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2900 self.assertEqual(expected, self.called)
2901
2902 def _test_with_failure_in_method(self, method, error):
2903 handler = FakeHandler(0, self.called)
2904 setattr(handler, method, self.raise_error(error))
2905 handlers = [logging.weakref.ref(handler)]
2906
2907 logging.shutdown(handlerList=list(handlers))
2908
2909 self.assertEqual('0 - release', self.called[-1])
2910
2911 def test_with_ioerror_in_acquire(self):
2912 self._test_with_failure_in_method('acquire', IOError)
2913
2914 def test_with_ioerror_in_flush(self):
2915 self._test_with_failure_in_method('flush', IOError)
2916
2917 def test_with_ioerror_in_close(self):
2918 self._test_with_failure_in_method('close', IOError)
2919
2920 def test_with_valueerror_in_acquire(self):
2921 self._test_with_failure_in_method('acquire', ValueError)
2922
2923 def test_with_valueerror_in_flush(self):
2924 self._test_with_failure_in_method('flush', ValueError)
2925
2926 def test_with_valueerror_in_close(self):
2927 self._test_with_failure_in_method('close', ValueError)
2928
2929 def test_with_other_error_in_acquire_without_raise(self):
2930 logging.raiseExceptions = False
2931 self._test_with_failure_in_method('acquire', IndexError)
2932
2933 def test_with_other_error_in_flush_without_raise(self):
2934 logging.raiseExceptions = False
2935 self._test_with_failure_in_method('flush', IndexError)
2936
2937 def test_with_other_error_in_close_without_raise(self):
2938 logging.raiseExceptions = False
2939 self._test_with_failure_in_method('close', IndexError)
2940
2941 def test_with_other_error_in_acquire_with_raise(self):
2942 logging.raiseExceptions = True
2943 self.assertRaises(IndexError, self._test_with_failure_in_method,
2944 'acquire', IndexError)
2945
2946 def test_with_other_error_in_flush_with_raise(self):
2947 logging.raiseExceptions = True
2948 self.assertRaises(IndexError, self._test_with_failure_in_method,
2949 'flush', IndexError)
2950
2951 def test_with_other_error_in_close_with_raise(self):
2952 logging.raiseExceptions = True
2953 self.assertRaises(IndexError, self._test_with_failure_in_method,
2954 'close', IndexError)
2955
2956
2957class ModuleLevelMiscTest(BaseTest):
2958
Vinay Sajip5e66b162011-04-20 15:41:14 +01002959 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002960
2961 def test_disable(self):
2962 old_disable = logging.root.manager.disable
2963 # confirm our assumptions are correct
2964 assert old_disable == 0
2965 self.addCleanup(lambda: logging.disable(old_disable))
2966
2967 logging.disable(83)
2968 self.assertEqual(logging.root.manager.disable, 83)
2969
2970 def _test_log(self, method, level=None):
2971 called = []
2972 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002973 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002974
2975 recording = RecordingHandler()
2976 logging.root.addHandler(recording)
2977
2978 log_method = getattr(logging, method)
2979 if level is not None:
2980 log_method(level, "test me: %r", recording)
2981 else:
2982 log_method("test me: %r", recording)
2983
2984 self.assertEqual(len(recording.records), 1)
2985 record = recording.records[0]
2986 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2987
2988 expected_level = level if level is not None else getattr(logging, method.upper())
2989 self.assertEqual(record.levelno, expected_level)
2990
2991 # basicConfig was not called!
2992 self.assertEqual(called, [])
2993
2994 def test_log(self):
2995 self._test_log('log', logging.ERROR)
2996
2997 def test_debug(self):
2998 self._test_log('debug')
2999
3000 def test_info(self):
3001 self._test_log('info')
3002
3003 def test_warning(self):
3004 self._test_log('warning')
3005
3006 def test_error(self):
3007 self._test_log('error')
3008
3009 def test_critical(self):
3010 self._test_log('critical')
3011
3012 def test_set_logger_class(self):
3013 self.assertRaises(TypeError, logging.setLoggerClass, object)
3014
3015 class MyLogger(logging.Logger):
3016 pass
3017
3018 logging.setLoggerClass(MyLogger)
3019 self.assertEqual(logging.getLoggerClass(), MyLogger)
3020
3021 logging.setLoggerClass(logging.Logger)
3022 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3023
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003024class LogRecordTest(BaseTest):
3025 def test_str_rep(self):
3026 r = logging.makeLogRecord({})
3027 s = str(r)
3028 self.assertTrue(s.startswith('<LogRecord: '))
3029 self.assertTrue(s.endswith('>'))
3030
3031 def test_dict_arg(self):
3032 h = RecordingHandler()
3033 r = logging.getLogger()
3034 r.addHandler(h)
3035 d = {'less' : 'more' }
3036 logging.warning('less is %(less)s', d)
3037 self.assertIs(h.records[0].args, d)
3038 self.assertEqual(h.records[0].message, 'less is more')
3039 r.removeHandler(h)
3040 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003041
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003042 def test_multiprocessing(self):
3043 r = logging.makeLogRecord({})
3044 self.assertEqual(r.processName, 'MainProcess')
3045 import multiprocessing as mp
3046 r = logging.makeLogRecord({})
3047 self.assertEqual(r.processName, mp.current_process().name)
3048
3049 def test_optional(self):
3050 r = logging.makeLogRecord({})
3051 NOT_NONE = self.assertIsNotNone
3052 NOT_NONE(r.thread)
3053 NOT_NONE(r.threadName)
3054 NOT_NONE(r.process)
3055 NOT_NONE(r.processName)
3056 log_threads = logging.logThreads
3057 log_processes = logging.logProcesses
3058 log_multiprocessing = logging.logMultiprocessing
3059 try:
3060 logging.logThreads = False
3061 logging.logProcesses = False
3062 logging.logMultiprocessing = False
3063 r = logging.makeLogRecord({})
3064 NONE = self.assertIsNone
3065 NONE(r.thread)
3066 NONE(r.threadName)
3067 NONE(r.process)
3068 NONE(r.processName)
3069 finally:
3070 logging.logThreads = log_threads
3071 logging.logProcesses = log_processes
3072 logging.logMultiprocessing = log_multiprocessing
3073
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003074class BasicConfigTest(unittest.TestCase):
3075
Vinay Sajip95bf5042011-04-20 11:50:56 +01003076 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003077
3078 def setUp(self):
3079 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003080 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003081 self.saved_handlers = logging._handlers.copy()
3082 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003083 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003084 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003085 logging.root.handlers = []
3086
3087 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003088 for h in logging.root.handlers[:]:
3089 logging.root.removeHandler(h)
3090 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003091 super(BasicConfigTest, self).tearDown()
3092
Vinay Sajip3def7e02011-04-20 10:58:06 +01003093 def cleanup(self):
3094 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003095 logging._handlers.clear()
3096 logging._handlers.update(self.saved_handlers)
3097 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003098 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003099
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003100 def test_no_kwargs(self):
3101 logging.basicConfig()
3102
3103 # handler defaults to a StreamHandler to sys.stderr
3104 self.assertEqual(len(logging.root.handlers), 1)
3105 handler = logging.root.handlers[0]
3106 self.assertIsInstance(handler, logging.StreamHandler)
3107 self.assertEqual(handler.stream, sys.stderr)
3108
3109 formatter = handler.formatter
3110 # format defaults to logging.BASIC_FORMAT
3111 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3112 # datefmt defaults to None
3113 self.assertIsNone(formatter.datefmt)
3114 # style defaults to %
3115 self.assertIsInstance(formatter._style, logging.PercentStyle)
3116
3117 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003118 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003119
3120 def test_filename(self):
3121 logging.basicConfig(filename='test.log')
3122
3123 self.assertEqual(len(logging.root.handlers), 1)
3124 handler = logging.root.handlers[0]
3125 self.assertIsInstance(handler, logging.FileHandler)
3126
3127 expected = logging.FileHandler('test.log', 'a')
3128 self.addCleanup(expected.close)
3129 self.assertEqual(handler.stream.mode, expected.stream.mode)
3130 self.assertEqual(handler.stream.name, expected.stream.name)
3131
3132 def test_filemode(self):
3133 logging.basicConfig(filename='test.log', filemode='wb')
3134
3135 handler = logging.root.handlers[0]
3136 expected = logging.FileHandler('test.log', 'wb')
3137 self.addCleanup(expected.close)
3138 self.assertEqual(handler.stream.mode, expected.stream.mode)
3139
3140 def test_stream(self):
3141 stream = io.StringIO()
3142 self.addCleanup(stream.close)
3143 logging.basicConfig(stream=stream)
3144
3145 self.assertEqual(len(logging.root.handlers), 1)
3146 handler = logging.root.handlers[0]
3147 self.assertIsInstance(handler, logging.StreamHandler)
3148 self.assertEqual(handler.stream, stream)
3149
3150 def test_format(self):
3151 logging.basicConfig(format='foo')
3152
3153 formatter = logging.root.handlers[0].formatter
3154 self.assertEqual(formatter._style._fmt, 'foo')
3155
3156 def test_datefmt(self):
3157 logging.basicConfig(datefmt='bar')
3158
3159 formatter = logging.root.handlers[0].formatter
3160 self.assertEqual(formatter.datefmt, 'bar')
3161
3162 def test_style(self):
3163 logging.basicConfig(style='$')
3164
3165 formatter = logging.root.handlers[0].formatter
3166 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3167
3168 def test_level(self):
3169 old_level = logging.root.level
3170 self.addCleanup(lambda: logging.root.setLevel(old_level))
3171
3172 logging.basicConfig(level=57)
3173 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003174 # Test that second call has no effect
3175 logging.basicConfig(level=58)
3176 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003177
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003178 def test_incompatible(self):
3179 assertRaises = self.assertRaises
3180 handlers = [logging.StreamHandler()]
3181 stream = sys.stderr
3182 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3183 stream=stream)
3184 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3185 handlers=handlers)
3186 assertRaises(ValueError, logging.basicConfig, stream=stream,
3187 handlers=handlers)
3188
3189 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003190 handlers = [
3191 logging.StreamHandler(),
3192 logging.StreamHandler(sys.stdout),
3193 logging.StreamHandler(),
3194 ]
3195 f = logging.Formatter()
3196 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003197 logging.basicConfig(handlers=handlers)
3198 self.assertIs(handlers[0], logging.root.handlers[0])
3199 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003200 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003201 self.assertIsNotNone(handlers[0].formatter)
3202 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003203 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003204 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3205
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003206 def _test_log(self, method, level=None):
3207 # logging.root has no handlers so basicConfig should be called
3208 called = []
3209
3210 old_basic_config = logging.basicConfig
3211 def my_basic_config(*a, **kw):
3212 old_basic_config()
3213 old_level = logging.root.level
3214 logging.root.setLevel(100) # avoid having messages in stderr
3215 self.addCleanup(lambda: logging.root.setLevel(old_level))
3216 called.append((a, kw))
3217
3218 patch(self, logging, 'basicConfig', my_basic_config)
3219
3220 log_method = getattr(logging, method)
3221 if level is not None:
3222 log_method(level, "test me")
3223 else:
3224 log_method("test me")
3225
3226 # basicConfig was called with no arguments
3227 self.assertEqual(called, [((), {})])
3228
3229 def test_log(self):
3230 self._test_log('log', logging.WARNING)
3231
3232 def test_debug(self):
3233 self._test_log('debug')
3234
3235 def test_info(self):
3236 self._test_log('info')
3237
3238 def test_warning(self):
3239 self._test_log('warning')
3240
3241 def test_error(self):
3242 self._test_log('error')
3243
3244 def test_critical(self):
3245 self._test_log('critical')
3246
3247
3248class LoggerAdapterTest(unittest.TestCase):
3249
3250 def setUp(self):
3251 super(LoggerAdapterTest, self).setUp()
3252 old_handler_list = logging._handlerList[:]
3253
3254 self.recording = RecordingHandler()
3255 self.logger = logging.root
3256 self.logger.addHandler(self.recording)
3257 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3258 self.addCleanup(self.recording.close)
3259
3260 def cleanup():
3261 logging._handlerList[:] = old_handler_list
3262
3263 self.addCleanup(cleanup)
3264 self.addCleanup(logging.shutdown)
3265 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3266
3267 def test_exception(self):
3268 msg = 'testing exception: %r'
3269 exc = None
3270 try:
3271 assert False
3272 except AssertionError as e:
3273 exc = e
3274 self.adapter.exception(msg, self.recording)
3275
3276 self.assertEqual(len(self.recording.records), 1)
3277 record = self.recording.records[0]
3278 self.assertEqual(record.levelno, logging.ERROR)
3279 self.assertEqual(record.msg, msg)
3280 self.assertEqual(record.args, (self.recording,))
3281 self.assertEqual(record.exc_info,
3282 (exc.__class__, exc, exc.__traceback__))
3283
3284 def test_critical(self):
3285 msg = 'critical test! %r'
3286 self.adapter.critical(msg, self.recording)
3287
3288 self.assertEqual(len(self.recording.records), 1)
3289 record = self.recording.records[0]
3290 self.assertEqual(record.levelno, logging.CRITICAL)
3291 self.assertEqual(record.msg, msg)
3292 self.assertEqual(record.args, (self.recording,))
3293
3294 def test_is_enabled_for(self):
3295 old_disable = self.adapter.logger.manager.disable
3296 self.adapter.logger.manager.disable = 33
3297 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3298 'disable', old_disable))
3299 self.assertFalse(self.adapter.isEnabledFor(32))
3300
3301 def test_has_handlers(self):
3302 self.assertTrue(self.adapter.hasHandlers())
3303
3304 for handler in self.logger.handlers:
3305 self.logger.removeHandler(handler)
3306 assert not self.logger.hasHandlers()
3307
3308 self.assertFalse(self.adapter.hasHandlers())
3309
3310
3311class LoggerTest(BaseTest):
3312
3313 def setUp(self):
3314 super(LoggerTest, self).setUp()
3315 self.recording = RecordingHandler()
3316 self.logger = logging.Logger(name='blah')
3317 self.logger.addHandler(self.recording)
3318 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3319 self.addCleanup(self.recording.close)
3320 self.addCleanup(logging.shutdown)
3321
3322 def test_set_invalid_level(self):
3323 self.assertRaises(TypeError, self.logger.setLevel, object())
3324
3325 def test_exception(self):
3326 msg = 'testing exception: %r'
3327 exc = None
3328 try:
3329 assert False
3330 except AssertionError as e:
3331 exc = e
3332 self.logger.exception(msg, self.recording)
3333
3334 self.assertEqual(len(self.recording.records), 1)
3335 record = self.recording.records[0]
3336 self.assertEqual(record.levelno, logging.ERROR)
3337 self.assertEqual(record.msg, msg)
3338 self.assertEqual(record.args, (self.recording,))
3339 self.assertEqual(record.exc_info,
3340 (exc.__class__, exc, exc.__traceback__))
3341
3342 def test_log_invalid_level_with_raise(self):
3343 old_raise = logging.raiseExceptions
3344 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3345
3346 logging.raiseExceptions = True
3347 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3348
3349 def test_log_invalid_level_no_raise(self):
3350 old_raise = logging.raiseExceptions
3351 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3352
3353 logging.raiseExceptions = False
3354 self.logger.log('10', 'test message') # no exception happens
3355
3356 def test_find_caller_with_stack_info(self):
3357 called = []
3358 patch(self, logging.traceback, 'print_stack',
3359 lambda f, file: called.append(file.getvalue()))
3360
3361 self.logger.findCaller(stack_info=True)
3362
3363 self.assertEqual(len(called), 1)
3364 self.assertEqual('Stack (most recent call last):\n', called[0])
3365
3366 def test_make_record_with_extra_overwrite(self):
3367 name = 'my record'
3368 level = 13
3369 fn = lno = msg = args = exc_info = func = sinfo = None
3370 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3371 exc_info, func, sinfo)
3372
3373 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3374 extra = {key: 'some value'}
3375 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3376 fn, lno, msg, args, exc_info,
3377 extra=extra, sinfo=sinfo)
3378
3379 def test_make_record_with_extra_no_overwrite(self):
3380 name = 'my record'
3381 level = 13
3382 fn = lno = msg = args = exc_info = func = sinfo = None
3383 extra = {'valid_key': 'some value'}
3384 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3385 exc_info, extra=extra, sinfo=sinfo)
3386 self.assertIn('valid_key', result.__dict__)
3387
3388 def test_has_handlers(self):
3389 self.assertTrue(self.logger.hasHandlers())
3390
3391 for handler in self.logger.handlers:
3392 self.logger.removeHandler(handler)
3393 assert not self.logger.hasHandlers()
3394
3395 self.assertFalse(self.logger.hasHandlers())
3396
3397 def test_has_handlers_no_propagate(self):
3398 child_logger = logging.getLogger('blah.child')
3399 child_logger.propagate = False
3400 assert child_logger.handlers == []
3401
3402 self.assertFalse(child_logger.hasHandlers())
3403
3404 def test_is_enabled_for(self):
3405 old_disable = self.logger.manager.disable
3406 self.logger.manager.disable = 23
3407 self.addCleanup(lambda: setattr(self.logger.manager,
3408 'disable', old_disable))
3409 self.assertFalse(self.logger.isEnabledFor(22))
3410
3411
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003412class BaseFileTest(BaseTest):
3413 "Base class for handler tests that write log files"
3414
3415 def setUp(self):
3416 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003417 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3418 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003419 self.rmfiles = []
3420
3421 def tearDown(self):
3422 for fn in self.rmfiles:
3423 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003424 if os.path.exists(self.fn):
3425 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003426 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003427
3428 def assertLogFile(self, filename):
3429 "Assert a log file is there and register it for deletion"
3430 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003431 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003432 self.rmfiles.append(filename)
3433
3434
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003435class FileHandlerTest(BaseFileTest):
3436 def test_delay(self):
3437 os.unlink(self.fn)
3438 fh = logging.FileHandler(self.fn, delay=True)
3439 self.assertIsNone(fh.stream)
3440 self.assertFalse(os.path.exists(self.fn))
3441 fh.handle(logging.makeLogRecord({}))
3442 self.assertIsNotNone(fh.stream)
3443 self.assertTrue(os.path.exists(self.fn))
3444 fh.close()
3445
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003446class RotatingFileHandlerTest(BaseFileTest):
3447 def next_rec(self):
3448 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3449 self.next_message(), None, None, None)
3450
3451 def test_should_not_rollover(self):
3452 # If maxbytes is zero rollover never occurs
3453 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3454 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003455 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003456
3457 def test_should_rollover(self):
3458 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3459 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003460 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003461
3462 def test_file_created(self):
3463 # checks that the file is created and assumes it was created
3464 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003465 rh = logging.handlers.RotatingFileHandler(self.fn)
3466 rh.emit(self.next_rec())
3467 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003468 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003469
3470 def test_rollover_filenames(self):
3471 rh = logging.handlers.RotatingFileHandler(
3472 self.fn, backupCount=2, maxBytes=1)
3473 rh.emit(self.next_rec())
3474 self.assertLogFile(self.fn)
3475 rh.emit(self.next_rec())
3476 self.assertLogFile(self.fn + ".1")
3477 rh.emit(self.next_rec())
3478 self.assertLogFile(self.fn + ".2")
3479 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003480 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003481
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003482class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003483 # other test methods added below
3484 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003485 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3486 backupCount=1)
Vinay Sajip7367d082011-05-02 13:17:27 +01003487 r = logging.makeLogRecord({'msg': 'testing'})
3488 fh.emit(r)
3489 self.assertLogFile(self.fn)
3490 time.sleep(1.0)
3491 fh.emit(r)
3492 now = datetime.datetime.now()
3493 prevsec = now - datetime.timedelta(seconds=1)
3494 suffix = prevsec.strftime(".%Y-%m-%d_%H-%M-%S")
3495 self.assertLogFile(self.fn + suffix)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003496
Vinay Sajip0372e102011-05-05 12:59:14 +01003497 def test_invalid(self):
3498 assertRaises = self.assertRaises
3499 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
3500 self.fn, 'X')
3501 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
3502 self.fn, 'W')
3503 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
3504 self.fn, 'W7')
3505
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003506def secs(**kw):
3507 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3508
3509for when, exp in (('S', 1),
3510 ('M', 60),
3511 ('H', 60 * 60),
3512 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003513 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003514 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003515 ('W0', secs(days=4, hours=24)),
3516 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003517 def test_compute_rollover(self, when=when, exp=exp):
3518 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003519 self.fn, when=when, interval=1, backupCount=0, utc=True)
3520 currentTime = 0.0
3521 actual = rh.computeRollover(currentTime)
3522 if exp != actual:
3523 # Failures occur on some systems for MIDNIGHT and W0.
3524 # Print detailed calculation for MIDNIGHT so we can try to see
3525 # what's going on
3526 import time
3527 if when == 'MIDNIGHT':
3528 try:
3529 if rh.utc:
3530 t = time.gmtime(currentTime)
3531 else:
3532 t = time.localtime(currentTime)
3533 currentHour = t[3]
3534 currentMinute = t[4]
3535 currentSecond = t[5]
3536 # r is the number of seconds left between now and midnight
3537 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3538 currentMinute) * 60 +
3539 currentSecond)
3540 result = currentTime + r
3541 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3542 print('currentHour: %s' % currentHour, file=sys.stderr)
3543 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3544 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3545 print('r: %s' % r, file=sys.stderr)
3546 print('result: %s' % result, file=sys.stderr)
3547 except Exception:
3548 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3549 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003550 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003551 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3552
Christian Heimes180510d2008-03-03 19:15:45 +00003553# Set the locale to the platform-dependent default. I have no idea
3554# why the test does this, but in any case we save the current locale
3555# first and restore it at the end.
3556@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003557def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003558 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003559 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003560 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3561 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3562 ManagerTest, FormatterTest, BufferingFormatterTest,
3563 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3564 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3565 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3566 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003567 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003568 SysLogHandlerTest, HTTPHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003569 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003570 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003571
Christian Heimes180510d2008-03-03 19:15:45 +00003572if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003573 test_main()