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