blob: c68de1045f1745c30a4279dba0709c0113572f9a [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
Vinay Sajip60ccd822011-05-09 17:32:09 +010061try:
62 import win32evtlog
63except ImportError:
64 win32evtlog = None
65try:
66 import win32evtlogutil
67except ImportError:
68 win32evtlogutil = None
69 win32evtlog = None
Christian Heimes18c66892008-02-17 13:31:39 +000070
71
Christian Heimes180510d2008-03-03 19:15:45 +000072class BaseTest(unittest.TestCase):
73
74 """Base class for logging tests."""
75
76 log_format = "%(name)s -> %(levelname)s: %(message)s"
77 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
78 message_num = 0
79
80 def setUp(self):
81 """Setup the default logging stream to an internal StringIO instance,
82 so that we can examine log output as we want."""
83 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000084 logging._acquireLock()
85 try:
Christian Heimes180510d2008-03-03 19:15:45 +000086 self.saved_handlers = logging._handlers.copy()
87 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000088 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000089 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000090 self.logger_states = logger_states = {}
91 for name in saved_loggers:
92 logger_states[name] = getattr(saved_loggers[name],
93 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000094 finally:
95 logging._releaseLock()
96
Benjamin Peterson22005fc2010-04-11 16:25:06 +000097 # Set two unused loggers: one non-ASCII and one Unicode.
98 # This is to test correct operation when sorting existing
99 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +0000100 self.logger1 = logging.getLogger("\xab\xd7\xbb")
101 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +0000102
Christian Heimes180510d2008-03-03 19:15:45 +0000103 self.root_logger = logging.getLogger("")
104 self.original_logging_level = self.root_logger.getEffectiveLevel()
105
106 self.stream = io.StringIO()
107 self.root_logger.setLevel(logging.DEBUG)
108 self.root_hdlr = logging.StreamHandler(self.stream)
109 self.root_formatter = logging.Formatter(self.log_format)
110 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +0000111 if self.logger1.hasHandlers():
112 hlist = self.logger1.handlers + self.root_logger.handlers
113 raise AssertionError('Unexpected handlers: %s' % hlist)
114 if self.logger2.hasHandlers():
115 hlist = self.logger2.handlers + self.root_logger.handlers
116 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000117 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000118 self.assertTrue(self.logger1.hasHandlers())
119 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000120
121 def tearDown(self):
122 """Remove our logging stream, and restore the original logging
123 level."""
124 self.stream.close()
125 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000126 while self.root_logger.handlers:
127 h = self.root_logger.handlers[0]
128 self.root_logger.removeHandler(h)
129 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000130 self.root_logger.setLevel(self.original_logging_level)
131 logging._acquireLock()
132 try:
133 logging._levelNames.clear()
134 logging._levelNames.update(self.saved_level_names)
135 logging._handlers.clear()
136 logging._handlers.update(self.saved_handlers)
137 logging._handlerList[:] = self.saved_handler_list
138 loggerDict = logging.getLogger().manager.loggerDict
139 loggerDict.clear()
140 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000141 logger_states = self.logger_states
142 for name in self.logger_states:
143 if logger_states[name] is not None:
144 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000145 finally:
146 logging._releaseLock()
147
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000148 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000149 """Match the collected log lines against the regular expression
150 self.expected_log_pat, and compare the extracted group values to
151 the expected_values list of tuples."""
152 stream = stream or self.stream
153 pat = re.compile(self.expected_log_pat)
154 try:
155 stream.reset()
156 actual_lines = stream.readlines()
157 except AttributeError:
158 # StringIO.StringIO lacks a reset() method.
159 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000160 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000161 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000162 for actual, expected in zip(actual_lines, expected_values):
163 match = pat.search(actual)
164 if not match:
165 self.fail("Log line does not match expected pattern:\n" +
166 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000167 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000168 s = stream.read()
169 if s:
170 self.fail("Remaining output at end of log stream:\n" + s)
171
172 def next_message(self):
173 """Generate a message consisting solely of an auto-incrementing
174 integer."""
175 self.message_num += 1
176 return "%d" % self.message_num
177
178
179class BuiltinLevelsTest(BaseTest):
180 """Test builtin levels and their inheritance."""
181
182 def test_flat(self):
183 #Logging levels in a flat logger namespace.
184 m = self.next_message
185
186 ERR = logging.getLogger("ERR")
187 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000188 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000189 INF.setLevel(logging.INFO)
190 DEB = logging.getLogger("DEB")
191 DEB.setLevel(logging.DEBUG)
192
193 # These should log.
194 ERR.log(logging.CRITICAL, m())
195 ERR.error(m())
196
197 INF.log(logging.CRITICAL, m())
198 INF.error(m())
199 INF.warn(m())
200 INF.info(m())
201
202 DEB.log(logging.CRITICAL, m())
203 DEB.error(m())
204 DEB.warn (m())
205 DEB.info (m())
206 DEB.debug(m())
207
208 # These should not log.
209 ERR.warn(m())
210 ERR.info(m())
211 ERR.debug(m())
212
213 INF.debug(m())
214
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000215 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000216 ('ERR', 'CRITICAL', '1'),
217 ('ERR', 'ERROR', '2'),
218 ('INF', 'CRITICAL', '3'),
219 ('INF', 'ERROR', '4'),
220 ('INF', 'WARNING', '5'),
221 ('INF', 'INFO', '6'),
222 ('DEB', 'CRITICAL', '7'),
223 ('DEB', 'ERROR', '8'),
224 ('DEB', 'WARNING', '9'),
225 ('DEB', 'INFO', '10'),
226 ('DEB', 'DEBUG', '11'),
227 ])
228
229 def test_nested_explicit(self):
230 # Logging levels in a nested namespace, all explicitly set.
231 m = self.next_message
232
233 INF = logging.getLogger("INF")
234 INF.setLevel(logging.INFO)
235 INF_ERR = logging.getLogger("INF.ERR")
236 INF_ERR.setLevel(logging.ERROR)
237
238 # These should log.
239 INF_ERR.log(logging.CRITICAL, m())
240 INF_ERR.error(m())
241
242 # These should not log.
243 INF_ERR.warn(m())
244 INF_ERR.info(m())
245 INF_ERR.debug(m())
246
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000247 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000248 ('INF.ERR', 'CRITICAL', '1'),
249 ('INF.ERR', 'ERROR', '2'),
250 ])
251
252 def test_nested_inherited(self):
253 #Logging levels in a nested namespace, inherited from parent loggers.
254 m = self.next_message
255
256 INF = logging.getLogger("INF")
257 INF.setLevel(logging.INFO)
258 INF_ERR = logging.getLogger("INF.ERR")
259 INF_ERR.setLevel(logging.ERROR)
260 INF_UNDEF = logging.getLogger("INF.UNDEF")
261 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
262 UNDEF = logging.getLogger("UNDEF")
263
264 # These should log.
265 INF_UNDEF.log(logging.CRITICAL, m())
266 INF_UNDEF.error(m())
267 INF_UNDEF.warn(m())
268 INF_UNDEF.info(m())
269 INF_ERR_UNDEF.log(logging.CRITICAL, m())
270 INF_ERR_UNDEF.error(m())
271
272 # These should not log.
273 INF_UNDEF.debug(m())
274 INF_ERR_UNDEF.warn(m())
275 INF_ERR_UNDEF.info(m())
276 INF_ERR_UNDEF.debug(m())
277
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000278 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000279 ('INF.UNDEF', 'CRITICAL', '1'),
280 ('INF.UNDEF', 'ERROR', '2'),
281 ('INF.UNDEF', 'WARNING', '3'),
282 ('INF.UNDEF', 'INFO', '4'),
283 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
284 ('INF.ERR.UNDEF', 'ERROR', '6'),
285 ])
286
287 def test_nested_with_virtual_parent(self):
288 # Logging levels when some parent does not exist yet.
289 m = self.next_message
290
291 INF = logging.getLogger("INF")
292 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
293 CHILD = logging.getLogger("INF.BADPARENT")
294 INF.setLevel(logging.INFO)
295
296 # These should log.
297 GRANDCHILD.log(logging.FATAL, m())
298 GRANDCHILD.info(m())
299 CHILD.log(logging.FATAL, m())
300 CHILD.info(m())
301
302 # These should not log.
303 GRANDCHILD.debug(m())
304 CHILD.debug(m())
305
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000306 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000307 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
308 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
309 ('INF.BADPARENT', 'CRITICAL', '3'),
310 ('INF.BADPARENT', 'INFO', '4'),
311 ])
312
313
314class BasicFilterTest(BaseTest):
315
316 """Test the bundled Filter class."""
317
318 def test_filter(self):
319 # Only messages satisfying the specified criteria pass through the
320 # filter.
321 filter_ = logging.Filter("spam.eggs")
322 handler = self.root_logger.handlers[0]
323 try:
324 handler.addFilter(filter_)
325 spam = logging.getLogger("spam")
326 spam_eggs = logging.getLogger("spam.eggs")
327 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
328 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
329
330 spam.info(self.next_message())
331 spam_eggs.info(self.next_message()) # Good.
332 spam_eggs_fish.info(self.next_message()) # Good.
333 spam_bakedbeans.info(self.next_message())
334
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000335 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000336 ('spam.eggs', 'INFO', '2'),
337 ('spam.eggs.fish', 'INFO', '3'),
338 ])
339 finally:
340 handler.removeFilter(filter_)
341
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000342 def test_callable_filter(self):
343 # Only messages satisfying the specified criteria pass through the
344 # filter.
345
346 def filterfunc(record):
347 parts = record.name.split('.')
348 prefix = '.'.join(parts[:2])
349 return prefix == 'spam.eggs'
350
351 handler = self.root_logger.handlers[0]
352 try:
353 handler.addFilter(filterfunc)
354 spam = logging.getLogger("spam")
355 spam_eggs = logging.getLogger("spam.eggs")
356 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
357 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
358
359 spam.info(self.next_message())
360 spam_eggs.info(self.next_message()) # Good.
361 spam_eggs_fish.info(self.next_message()) # Good.
362 spam_bakedbeans.info(self.next_message())
363
364 self.assert_log_lines([
365 ('spam.eggs', 'INFO', '2'),
366 ('spam.eggs.fish', 'INFO', '3'),
367 ])
368 finally:
369 handler.removeFilter(filterfunc)
370
Vinay Sajip985ef872011-04-26 19:34:04 +0100371 def test_empty_filter(self):
372 f = logging.Filter()
373 r = logging.makeLogRecord({'name': 'spam.eggs'})
374 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000375
376#
377# First, we define our levels. There can be as many as you want - the only
378# limitations are that they should be integers, the lowest should be > 0 and
379# larger values mean less information being logged. If you need specific
380# level values which do not fit into these limitations, you can use a
381# mapping dictionary to convert between your application levels and the
382# logging system.
383#
384SILENT = 120
385TACITURN = 119
386TERSE = 118
387EFFUSIVE = 117
388SOCIABLE = 116
389VERBOSE = 115
390TALKATIVE = 114
391GARRULOUS = 113
392CHATTERBOX = 112
393BORING = 111
394
395LEVEL_RANGE = range(BORING, SILENT + 1)
396
397#
398# Next, we define names for our levels. You don't need to do this - in which
399# case the system will use "Level n" to denote the text for the level.
400#
401my_logging_levels = {
402 SILENT : 'Silent',
403 TACITURN : 'Taciturn',
404 TERSE : 'Terse',
405 EFFUSIVE : 'Effusive',
406 SOCIABLE : 'Sociable',
407 VERBOSE : 'Verbose',
408 TALKATIVE : 'Talkative',
409 GARRULOUS : 'Garrulous',
410 CHATTERBOX : 'Chatterbox',
411 BORING : 'Boring',
412}
413
414class GarrulousFilter(logging.Filter):
415
416 """A filter which blocks garrulous messages."""
417
418 def filter(self, record):
419 return record.levelno != GARRULOUS
420
421class VerySpecificFilter(logging.Filter):
422
423 """A filter which blocks sociable and taciturn messages."""
424
425 def filter(self, record):
426 return record.levelno not in [SOCIABLE, TACITURN]
427
428
429class CustomLevelsAndFiltersTest(BaseTest):
430
431 """Test various filtering possibilities with custom logging levels."""
432
433 # Skip the logger name group.
434 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
435
436 def setUp(self):
437 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000438 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000439 logging.addLevelName(k, v)
440
441 def log_at_all_levels(self, logger):
442 for lvl in LEVEL_RANGE:
443 logger.log(lvl, self.next_message())
444
445 def test_logger_filter(self):
446 # Filter at logger level.
447 self.root_logger.setLevel(VERBOSE)
448 # Levels >= 'Verbose' are good.
449 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000450 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000451 ('Verbose', '5'),
452 ('Sociable', '6'),
453 ('Effusive', '7'),
454 ('Terse', '8'),
455 ('Taciturn', '9'),
456 ('Silent', '10'),
457 ])
458
459 def test_handler_filter(self):
460 # Filter at handler level.
461 self.root_logger.handlers[0].setLevel(SOCIABLE)
462 try:
463 # Levels >= 'Sociable' are good.
464 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000465 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000466 ('Sociable', '6'),
467 ('Effusive', '7'),
468 ('Terse', '8'),
469 ('Taciturn', '9'),
470 ('Silent', '10'),
471 ])
472 finally:
473 self.root_logger.handlers[0].setLevel(logging.NOTSET)
474
475 def test_specific_filters(self):
476 # Set a specific filter object on the handler, and then add another
477 # filter object on the logger itself.
478 handler = self.root_logger.handlers[0]
479 specific_filter = None
480 garr = GarrulousFilter()
481 handler.addFilter(garr)
482 try:
483 self.log_at_all_levels(self.root_logger)
484 first_lines = [
485 # Notice how 'Garrulous' is missing
486 ('Boring', '1'),
487 ('Chatterbox', '2'),
488 ('Talkative', '4'),
489 ('Verbose', '5'),
490 ('Sociable', '6'),
491 ('Effusive', '7'),
492 ('Terse', '8'),
493 ('Taciturn', '9'),
494 ('Silent', '10'),
495 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000496 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000497
498 specific_filter = VerySpecificFilter()
499 self.root_logger.addFilter(specific_filter)
500 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000501 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000502 # Not only 'Garrulous' is still missing, but also 'Sociable'
503 # and 'Taciturn'
504 ('Boring', '11'),
505 ('Chatterbox', '12'),
506 ('Talkative', '14'),
507 ('Verbose', '15'),
508 ('Effusive', '17'),
509 ('Terse', '18'),
510 ('Silent', '20'),
511 ])
512 finally:
513 if specific_filter:
514 self.root_logger.removeFilter(specific_filter)
515 handler.removeFilter(garr)
516
517
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100518class HandlerTest(BaseTest):
519 def test_name(self):
520 h = logging.Handler()
521 h.name = 'generic'
522 self.assertEqual(h.name, 'generic')
523 h.name = 'anothergeneric'
524 self.assertEqual(h.name, 'anothergeneric')
525 self.assertRaises(NotImplementedError, h.emit, None)
526
Vinay Sajip5a35b062011-04-27 11:31:14 +0100527 def test_builtin_handlers(self):
528 # We can't actually *use* too many handlers in the tests,
529 # but we can try instantiating them with various options
530 if sys.platform in ('linux2', 'darwin'):
531 for existing in (True, False):
532 fd, fn = tempfile.mkstemp()
533 os.close(fd)
534 if not existing:
535 os.unlink(fn)
536 h = logging.handlers.WatchedFileHandler(fn, delay=True)
537 if existing:
Vinay Sajip7fe1d512011-04-28 12:04:58 +0100538 dev, ino = h.dev, h.ino
539 self.assertNotEqual(dev, -1)
540 self.assertNotEqual(ino, -1)
541 r = logging.makeLogRecord({'msg': 'Test'})
542 h.handle(r)
543 # Now remove the file.
544 os.unlink(fn)
545 self.assertFalse(os.path.exists(fn))
546 # The next call should recreate the file.
547 h.handle(r)
548 self.assertTrue(os.path.exists(fn))
Vinay Sajip5a35b062011-04-27 11:31:14 +0100549 else:
550 self.assertEqual(h.dev, -1)
551 self.assertEqual(h.ino, -1)
552 h.close()
553 if existing:
554 os.unlink(fn)
555 if sys.platform == 'darwin':
Vinay Sajip6bba65c2011-04-27 14:31:55 +0100556 sockname = '/var/run/syslog'
Vinay Sajip5a35b062011-04-27 11:31:14 +0100557 else:
558 sockname = '/dev/log'
Vinay Sajipdbeb2742011-04-27 14:18:06 +0100559 try:
560 h = logging.handlers.SysLogHandler(sockname)
561 self.assertEqual(h.facility, h.LOG_USER)
562 self.assertTrue(h.unixsocket)
563 h.close()
564 except socket.error: # syslogd might not be available
565 pass
Vinay Sajip5a35b062011-04-27 11:31:14 +0100566 for method in ('GET', 'POST', 'PUT'):
567 if method == 'PUT':
568 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
569 'localhost', '/log', method)
570 else:
571 h = logging.handlers.HTTPHandler('localhost', '/log', method)
572 h.close()
573 h = logging.handlers.BufferingHandler(0)
574 r = logging.makeLogRecord({})
575 self.assertTrue(h.shouldFlush(r))
576 h.close()
577 h = logging.handlers.BufferingHandler(1)
578 self.assertFalse(h.shouldFlush(r))
579 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100580
581class BadStream(object):
582 def write(self, data):
583 raise RuntimeError('deliberate mistake')
584
585class TestStreamHandler(logging.StreamHandler):
586 def handleError(self, record):
587 self.error_record = record
588
589class StreamHandlerTest(BaseTest):
590 def test_error_handling(self):
591 h = TestStreamHandler(BadStream())
592 r = logging.makeLogRecord({})
593 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100594 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100595 try:
596 h.handle(r)
597 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100598 h = logging.StreamHandler(BadStream())
599 sys.stderr = sio = io.StringIO()
600 h.handle(r)
601 self.assertTrue('\nRuntimeError: '
602 'deliberate mistake\n' in sio.getvalue())
603 logging.raiseExceptions = False
604 sys.stderr = sio = io.StringIO()
605 h.handle(r)
606 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100607 finally:
608 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100609 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100610
Vinay Sajip7367d082011-05-02 13:17:27 +0100611# -- The following section could be moved into a server_helper.py module
612# -- if it proves to be of wider utility than just test_logging
Vinay Sajipa463d252011-04-30 21:52:48 +0100613
614class TestSMTPChannel(smtpd.SMTPChannel):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100615 """
616 This derived class has had to be created because smtpd does not
617 support use of custom channel maps, although they are allowed by
618 asyncore's design. Issue #11959 has been raised to address this,
619 and if resolved satisfactorily, some of this code can be removed.
620 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100621 def __init__(self, server, conn, addr, sockmap):
622 asynchat.async_chat.__init__(self, conn, sockmap)
623 self.smtp_server = server
624 self.conn = conn
625 self.addr = addr
626 self.received_lines = []
627 self.smtp_state = self.COMMAND
628 self.seen_greeting = ''
629 self.mailfrom = None
630 self.rcpttos = []
631 self.received_data = ''
632 self.fqdn = socket.getfqdn()
633 self.num_bytes = 0
634 try:
635 self.peer = conn.getpeername()
636 except socket.error as err:
637 # a race condition may occur if the other end is closing
638 # before we can get the peername
639 self.close()
640 if err.args[0] != errno.ENOTCONN:
641 raise
642 return
643 self.push('220 %s %s' % (self.fqdn, smtpd.__version__))
644 self.set_terminator(b'\r\n')
645
646
647class TestSMTPServer(smtpd.SMTPServer):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100648 """
649 This class implements a test SMTP server.
650
651 :param addr: A (host, port) tuple which the server listens on.
652 You can specify a port value of zero: the server's
653 *port* attribute will hold the actual port number
654 used, which can be used in client connections.
655 :param handler: A callable which will be called to process
656 incoming messages. The handler will be passed
657 the client address tuple, who the message is from,
658 a list of recipients and the message data.
659 :param poll_interval: The interval, in seconds, used in the underlying
660 :func:`select` or :func:`poll` call by
661 :func:`asyncore.loop`.
662 :param sockmap: A dictionary which will be used to hold
663 :class:`asyncore.dispatcher` instances used by
664 :func:`asyncore.loop`. This avoids changing the
665 :mod:`asyncore` module's global state.
666 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100667 channel_class = TestSMTPChannel
668
669 def __init__(self, addr, handler, poll_interval, sockmap):
670 self._localaddr = addr
671 self._remoteaddr = None
672 self.sockmap = sockmap
673 asyncore.dispatcher.__init__(self, map=sockmap)
674 try:
675 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
676 sock.setblocking(0)
677 self.set_socket(sock, map=sockmap)
678 # try to re-use a server port if possible
679 self.set_reuse_addr()
680 self.bind(addr)
Vinay Sajip7367d082011-05-02 13:17:27 +0100681 self.port = sock.getsockname()[1]
Vinay Sajipa463d252011-04-30 21:52:48 +0100682 self.listen(5)
683 except:
684 self.close()
685 raise
686 self._handler = handler
687 self._thread = None
688 self.poll_interval = poll_interval
689
690 def handle_accepted(self, conn, addr):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100691 """
692 Redefined only because the base class does not pass in a
693 map, forcing use of a global in :mod:`asyncore`.
694 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100695 channel = self.channel_class(self, conn, addr, self.sockmap)
696
697 def process_message(self, peer, mailfrom, rcpttos, data):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100698 """
699 Delegates to the handler passed in to the server's constructor.
700
701 Typically, this will be a test case method.
702 :param peer: The client (host, port) tuple.
703 :param mailfrom: The address of the sender.
704 :param rcpttos: The addresses of the recipients.
705 :param data: The message.
706 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100707 self._handler(peer, mailfrom, rcpttos, data)
708
709 def start(self):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100710 """
711 Start the server running on a separate daemon thread.
712 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100713 self._thread = t = threading.Thread(target=self.serve_forever,
714 args=(self.poll_interval,))
715 t.setDaemon(True)
716 t.start()
717
718 def serve_forever(self, poll_interval):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100719 """
720 Run the :mod:`asyncore` loop until normal termination
721 conditions arise.
722 :param poll_interval: The interval, in seconds, used in the underlying
723 :func:`select` or :func:`poll` call by
724 :func:`asyncore.loop`.
725 """
Vinay Sajipe73afad2011-05-10 07:48:28 +0100726 try:
727 asyncore.loop(poll_interval, map=self.sockmap)
728 except select.error:
729 # On FreeBSD 8, closing the server repeatably
730 # raises this error. We swallow it if the
731 # server has been closed.
732 if self.connected or self.accepting:
733 raise
Vinay Sajipa463d252011-04-30 21:52:48 +0100734
Vinay Sajip7367d082011-05-02 13:17:27 +0100735 def stop(self, timeout=None):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100736 """
737 Stop the thread by closing the server instance.
738 Wait for the server thread to terminate.
739
740 :param timeout: How long to wait for the server thread
741 to terminate.
742 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100743 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100744 self._thread.join(timeout)
Vinay Sajipa463d252011-04-30 21:52:48 +0100745 self._thread = None
746
Vinay Sajip7367d082011-05-02 13:17:27 +0100747class ControlMixin(object):
748 """
749 This mixin is used to start a server on a separate thread, and
750 shut it down programmatically. Request handling is simplified - instead
751 of needing to derive a suitable RequestHandler subclass, you just
752 provide a callable which will be passed each received request to be
753 processed.
754
755 :param handler: A handler callable which will be called with a
756 single parameter - the request - in order to
757 process the request. This handler is called on the
758 server thread, effectively meaning that requests are
759 processed serially. While not quite Web scale ;-),
760 this should be fine for testing applications.
761 :param poll_interval: The polling interval in seconds.
762 """
763 def __init__(self, handler, poll_interval):
764 self._thread = None
765 self.poll_interval = poll_interval
766 self._handler = handler
767 self.ready = threading.Event()
768
769 def start(self):
770 """
771 Create a daemon thread to run the server, and start it.
772 """
773 self._thread = t = threading.Thread(target=self.serve_forever,
774 args=(self.poll_interval,))
775 t.setDaemon(True)
776 t.start()
777
778 def serve_forever(self, poll_interval):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100779 """
780 Run the server. Set the ready flag before entering the
781 service loop.
782 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100783 self.ready.set()
784 super(ControlMixin, self).serve_forever(poll_interval)
785
786 def stop(self, timeout=None):
787 """
788 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100789
790 :param timeout: How long to wait for the server thread
791 to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100792 """
793 self.shutdown()
794 if self._thread is not None:
795 self._thread.join(timeout)
796 self._thread = None
797 self.server_close()
798 self.ready.clear()
799
800class TestHTTPServer(ControlMixin, HTTPServer):
801 """
802 An HTTP server which is controllable using :class:`ControlMixin`.
803
804 :param addr: A tuple with the IP address and port to listen on.
805 :param handler: A handler callable which will be called with a
806 single parameter - the request - in order to
807 process the request.
808 :param poll_interval: The polling interval in seconds.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100809 :param log: Pass ``True`` to enable log messages.
Vinay Sajip7367d082011-05-02 13:17:27 +0100810 """
811 def __init__(self, addr, handler, poll_interval=0.5, log=False):
812 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
813 def __getattr__(self, name, default=None):
814 if name.startswith('do_'):
815 return self.process_request
816 raise AttributeError(name)
817
818 def process_request(self):
819 self.server._handler(self)
820
821 def log_message(self, format, *args):
822 if log:
823 super(DelegatingHTTPRequestHandler,
824 self).log_message(format, *args)
825 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
826 ControlMixin.__init__(self, handler, poll_interval)
827
828class TestTCPServer(ControlMixin, ThreadingTCPServer):
829 """
830 A TCP server which is controllable using :class:`ControlMixin`.
831
832 :param addr: A tuple with the IP address and port to listen on.
833 :param handler: A handler callable which will be called with a single
834 parameter - the request - in order to process the request.
835 :param poll_interval: The polling interval in seconds.
836 :bind_and_activate: If True (the default), binds the server and starts it
837 listening. If False, you need to call
838 :meth:`server_bind` and :meth:`server_activate` at
839 some later time before calling :meth:`start`, so that
840 the server will set up the socket and listen on it.
841 """
842
843 allow_reuse_address = True
844
845 def __init__(self, addr, handler, poll_interval=0.5,
846 bind_and_activate=True):
847 class DelegatingTCPRequestHandler(StreamRequestHandler):
848
849 def handle(self):
850 self.server._handler(self)
851 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
852 bind_and_activate)
853 ControlMixin.__init__(self, handler, poll_interval)
854
855 def server_bind(self):
856 super(TestTCPServer, self).server_bind()
857 self.port = self.socket.getsockname()[1]
858
859class TestUDPServer(ControlMixin, ThreadingUDPServer):
860 """
861 A UDP server which is controllable using :class:`ControlMixin`.
862
863 :param addr: A tuple with the IP address and port to listen on.
864 :param handler: A handler callable which will be called with a
865 single parameter - the request - in order to
866 process the request.
867 :param poll_interval: The polling interval for shutdown requests,
868 in seconds.
869 :bind_and_activate: If True (the default), binds the server and
870 starts it listening. If False, you need to
871 call :meth:`server_bind` and
872 :meth:`server_activate` at some later time
873 before calling :meth:`start`, so that the server will
874 set up the socket and listen on it.
875 """
876 def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True):
877 class DelegatingUDPRequestHandler(DatagramRequestHandler):
878
879 def handle(self):
880 self.server._handler(self)
881 ThreadingUDPServer.__init__(self, addr, DelegatingUDPRequestHandler,
882 bind_and_activate)
883 ControlMixin.__init__(self, handler, poll_interval)
884
885 def server_bind(self):
886 super(TestUDPServer, self).server_bind()
887 self.port = self.socket.getsockname()[1]
888
889
890# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100891
892class SMTPHandlerTest(BaseTest):
893 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100894 sockmap = {}
895 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
896 sockmap)
897 server.start()
898 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100899 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
900 self.assertEqual(h.toaddrs, ['you'])
901 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100902 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100903 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100904 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100905 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100906 server.stop()
907 self.assertEqual(len(self.messages), 1)
908 peer, mailfrom, rcpttos, data = self.messages[0]
909 self.assertEqual(mailfrom, 'me')
910 self.assertEqual(rcpttos, ['you'])
911 self.assertTrue('\nSubject: Log\n' in data)
912 self.assertTrue(data.endswith('\n\nHello'))
913 h.close()
914
915 def process_message(self, *args):
916 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100917 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100918
Christian Heimes180510d2008-03-03 19:15:45 +0000919class MemoryHandlerTest(BaseTest):
920
921 """Tests for the MemoryHandler."""
922
923 # Do not bother with a logger name group.
924 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
925
926 def setUp(self):
927 BaseTest.setUp(self)
928 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
929 self.root_hdlr)
930 self.mem_logger = logging.getLogger('mem')
931 self.mem_logger.propagate = 0
932 self.mem_logger.addHandler(self.mem_hdlr)
933
934 def tearDown(self):
935 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000936 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000937
938 def test_flush(self):
939 # The memory handler flushes to its target handler based on specific
940 # criteria (message count and message level).
941 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000942 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000943 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000944 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000945 # This will flush because the level is >= logging.WARNING
946 self.mem_logger.warn(self.next_message())
947 lines = [
948 ('DEBUG', '1'),
949 ('INFO', '2'),
950 ('WARNING', '3'),
951 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000952 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000953 for n in (4, 14):
954 for i in range(9):
955 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000956 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000957 # This will flush because it's the 10th message since the last
958 # flush.
959 self.mem_logger.debug(self.next_message())
960 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000961 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000962
963 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000964 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000965
966
967class ExceptionFormatter(logging.Formatter):
968 """A special exception formatter."""
969 def formatException(self, ei):
970 return "Got a [%s]" % ei[0].__name__
971
972
973class ConfigFileTest(BaseTest):
974
975 """Reading logging config from a .ini-style config file."""
976
977 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
978
979 # config0 is a standard configuration.
980 config0 = """
981 [loggers]
982 keys=root
983
984 [handlers]
985 keys=hand1
986
987 [formatters]
988 keys=form1
989
990 [logger_root]
991 level=WARNING
992 handlers=hand1
993
994 [handler_hand1]
995 class=StreamHandler
996 level=NOTSET
997 formatter=form1
998 args=(sys.stdout,)
999
1000 [formatter_form1]
1001 format=%(levelname)s ++ %(message)s
1002 datefmt=
1003 """
1004
1005 # config1 adds a little to the standard configuration.
1006 config1 = """
1007 [loggers]
1008 keys=root,parser
1009
1010 [handlers]
1011 keys=hand1
1012
1013 [formatters]
1014 keys=form1
1015
1016 [logger_root]
1017 level=WARNING
1018 handlers=
1019
1020 [logger_parser]
1021 level=DEBUG
1022 handlers=hand1
1023 propagate=1
1024 qualname=compiler.parser
1025
1026 [handler_hand1]
1027 class=StreamHandler
1028 level=NOTSET
1029 formatter=form1
1030 args=(sys.stdout,)
1031
1032 [formatter_form1]
1033 format=%(levelname)s ++ %(message)s
1034 datefmt=
1035 """
1036
Vinay Sajip3f84b072011-03-07 17:49:33 +00001037 # config1a moves the handler to the root.
1038 config1a = """
1039 [loggers]
1040 keys=root,parser
1041
1042 [handlers]
1043 keys=hand1
1044
1045 [formatters]
1046 keys=form1
1047
1048 [logger_root]
1049 level=WARNING
1050 handlers=hand1
1051
1052 [logger_parser]
1053 level=DEBUG
1054 handlers=
1055 propagate=1
1056 qualname=compiler.parser
1057
1058 [handler_hand1]
1059 class=StreamHandler
1060 level=NOTSET
1061 formatter=form1
1062 args=(sys.stdout,)
1063
1064 [formatter_form1]
1065 format=%(levelname)s ++ %(message)s
1066 datefmt=
1067 """
1068
Christian Heimes180510d2008-03-03 19:15:45 +00001069 # config2 has a subtle configuration error that should be reported
1070 config2 = config1.replace("sys.stdout", "sys.stbout")
1071
1072 # config3 has a less subtle configuration error
1073 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1074
1075 # config4 specifies a custom formatter class to be loaded
1076 config4 = """
1077 [loggers]
1078 keys=root
1079
1080 [handlers]
1081 keys=hand1
1082
1083 [formatters]
1084 keys=form1
1085
1086 [logger_root]
1087 level=NOTSET
1088 handlers=hand1
1089
1090 [handler_hand1]
1091 class=StreamHandler
1092 level=NOTSET
1093 formatter=form1
1094 args=(sys.stdout,)
1095
1096 [formatter_form1]
1097 class=""" + __name__ + """.ExceptionFormatter
1098 format=%(levelname)s:%(name)s:%(message)s
1099 datefmt=
1100 """
1101
Georg Brandl3dbca812008-07-23 16:10:53 +00001102 # config5 specifies a custom handler class to be loaded
1103 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1104
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001105 # config6 uses ', ' delimiters in the handlers and formatters sections
1106 config6 = """
1107 [loggers]
1108 keys=root,parser
1109
1110 [handlers]
1111 keys=hand1, hand2
1112
1113 [formatters]
1114 keys=form1, form2
1115
1116 [logger_root]
1117 level=WARNING
1118 handlers=
1119
1120 [logger_parser]
1121 level=DEBUG
1122 handlers=hand1
1123 propagate=1
1124 qualname=compiler.parser
1125
1126 [handler_hand1]
1127 class=StreamHandler
1128 level=NOTSET
1129 formatter=form1
1130 args=(sys.stdout,)
1131
1132 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001133 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001134 level=NOTSET
1135 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001136 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001137
1138 [formatter_form1]
1139 format=%(levelname)s ++ %(message)s
1140 datefmt=
1141
1142 [formatter_form2]
1143 format=%(message)s
1144 datefmt=
1145 """
1146
Vinay Sajip3f84b072011-03-07 17:49:33 +00001147 # config7 adds a compiler logger.
1148 config7 = """
1149 [loggers]
1150 keys=root,parser,compiler
1151
1152 [handlers]
1153 keys=hand1
1154
1155 [formatters]
1156 keys=form1
1157
1158 [logger_root]
1159 level=WARNING
1160 handlers=hand1
1161
1162 [logger_compiler]
1163 level=DEBUG
1164 handlers=
1165 propagate=1
1166 qualname=compiler
1167
1168 [logger_parser]
1169 level=DEBUG
1170 handlers=
1171 propagate=1
1172 qualname=compiler.parser
1173
1174 [handler_hand1]
1175 class=StreamHandler
1176 level=NOTSET
1177 formatter=form1
1178 args=(sys.stdout,)
1179
1180 [formatter_form1]
1181 format=%(levelname)s ++ %(message)s
1182 datefmt=
1183 """
1184
Christian Heimes180510d2008-03-03 19:15:45 +00001185 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001186 file = io.StringIO(textwrap.dedent(conf))
1187 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001188
1189 def test_config0_ok(self):
1190 # A simple config file which overrides the default settings.
1191 with captured_stdout() as output:
1192 self.apply_config(self.config0)
1193 logger = logging.getLogger()
1194 # Won't output anything
1195 logger.info(self.next_message())
1196 # Outputs a message
1197 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001198 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001199 ('ERROR', '2'),
1200 ], stream=output)
1201 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001202 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001203
Georg Brandl3dbca812008-07-23 16:10:53 +00001204 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001205 # A config file defining a sub-parser as well.
1206 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001207 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001208 logger = logging.getLogger("compiler.parser")
1209 # Both will output a message
1210 logger.info(self.next_message())
1211 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001212 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001213 ('INFO', '1'),
1214 ('ERROR', '2'),
1215 ], stream=output)
1216 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001217 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001218
1219 def test_config2_failure(self):
1220 # A simple config file which overrides the default settings.
1221 self.assertRaises(Exception, self.apply_config, self.config2)
1222
1223 def test_config3_failure(self):
1224 # A simple config file which overrides the default settings.
1225 self.assertRaises(Exception, self.apply_config, self.config3)
1226
1227 def test_config4_ok(self):
1228 # A config file specifying a custom formatter class.
1229 with captured_stdout() as output:
1230 self.apply_config(self.config4)
1231 logger = logging.getLogger()
1232 try:
1233 raise RuntimeError()
1234 except RuntimeError:
1235 logging.exception("just testing")
1236 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001237 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001238 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1239 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001240 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001241
Georg Brandl3dbca812008-07-23 16:10:53 +00001242 def test_config5_ok(self):
1243 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001244
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001245 def test_config6_ok(self):
1246 self.test_config1_ok(config=self.config6)
1247
Vinay Sajip3f84b072011-03-07 17:49:33 +00001248 def test_config7_ok(self):
1249 with captured_stdout() as output:
1250 self.apply_config(self.config1a)
1251 logger = logging.getLogger("compiler.parser")
1252 # See issue #11424. compiler-hyphenated sorts
1253 # between compiler and compiler.xyz and this
1254 # was preventing compiler.xyz from being included
1255 # in the child loggers of compiler because of an
1256 # overzealous loop termination condition.
1257 hyphenated = logging.getLogger('compiler-hyphenated')
1258 # All will output a message
1259 logger.info(self.next_message())
1260 logger.error(self.next_message())
1261 hyphenated.critical(self.next_message())
1262 self.assert_log_lines([
1263 ('INFO', '1'),
1264 ('ERROR', '2'),
1265 ('CRITICAL', '3'),
1266 ], stream=output)
1267 # Original logger output is empty.
1268 self.assert_log_lines([])
1269 with captured_stdout() as output:
1270 self.apply_config(self.config7)
1271 logger = logging.getLogger("compiler.parser")
1272 self.assertFalse(logger.disabled)
1273 # Both will output a message
1274 logger.info(self.next_message())
1275 logger.error(self.next_message())
1276 logger = logging.getLogger("compiler.lexer")
1277 # Both will output a message
1278 logger.info(self.next_message())
1279 logger.error(self.next_message())
1280 # Will not appear
1281 hyphenated.critical(self.next_message())
1282 self.assert_log_lines([
1283 ('INFO', '4'),
1284 ('ERROR', '5'),
1285 ('INFO', '6'),
1286 ('ERROR', '7'),
1287 ], stream=output)
1288 # Original logger output is empty.
1289 self.assert_log_lines([])
1290
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001291
Victor Stinner45df8202010-04-28 22:31:17 +00001292@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001293class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001294
Christian Heimes180510d2008-03-03 19:15:45 +00001295 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001296
Christian Heimes180510d2008-03-03 19:15:45 +00001297 def setUp(self):
1298 """Set up a TCP server to receive log messages, and a SocketHandler
1299 pointing to that server's address and port."""
1300 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001301 addr = ('localhost', 0)
1302 self.server = server = TestTCPServer(addr, self.handle_socket,
1303 0.01)
1304 server.start()
1305 server.ready.wait()
1306 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1307 server.port)
1308 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001309 self.root_logger.removeHandler(self.root_logger.handlers[0])
1310 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001311 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001312
Christian Heimes180510d2008-03-03 19:15:45 +00001313 def tearDown(self):
1314 """Shutdown the TCP server."""
1315 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001316 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001317 self.root_logger.removeHandler(self.sock_hdlr)
1318 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001319 finally:
1320 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001321
Vinay Sajip7367d082011-05-02 13:17:27 +01001322 def handle_socket(self, request):
1323 conn = request.connection
1324 while True:
1325 chunk = conn.recv(4)
1326 if len(chunk) < 4:
1327 break
1328 slen = struct.unpack(">L", chunk)[0]
1329 chunk = conn.recv(slen)
1330 while len(chunk) < slen:
1331 chunk = chunk + conn.recv(slen - len(chunk))
1332 obj = pickle.loads(chunk)
1333 record = logging.makeLogRecord(obj)
1334 self.log_output += record.msg + '\n'
1335 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001336
Christian Heimes180510d2008-03-03 19:15:45 +00001337 def test_output(self):
1338 # The log message sent to the SocketHandler is properly received.
1339 logger = logging.getLogger("tcp")
1340 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001341 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001342 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001343 self.handled.acquire()
1344 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001345
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001346 def test_noserver(self):
1347 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001348 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001349 #The logging call should try to connect, which should fail
1350 try:
1351 raise RuntimeError('Deliberate mistake')
1352 except RuntimeError:
1353 self.root_logger.exception('Never sent')
1354 self.root_logger.error('Never sent, either')
1355 now = time.time()
1356 self.assertTrue(self.sock_hdlr.retryTime > now)
1357 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1358 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001359
Vinay Sajip7367d082011-05-02 13:17:27 +01001360
1361@unittest.skipUnless(threading, 'Threading required for this test.')
1362class DatagramHandlerTest(BaseTest):
1363
1364 """Test for DatagramHandler."""
1365
1366 def setUp(self):
1367 """Set up a UDP server to receive log messages, and a DatagramHandler
1368 pointing to that server's address and port."""
1369 BaseTest.setUp(self)
1370 addr = ('localhost', 0)
1371 self.server = server = TestUDPServer(addr, self.handle_datagram,
1372 0.01)
1373 server.start()
1374 server.ready.wait()
1375 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1376 server.port)
1377 self.log_output = ''
1378 self.root_logger.removeHandler(self.root_logger.handlers[0])
1379 self.root_logger.addHandler(self.sock_hdlr)
1380 self.handled = threading.Event()
1381
1382 def tearDown(self):
1383 """Shutdown the UDP server."""
1384 try:
1385 self.server.stop(2.0)
1386 self.root_logger.removeHandler(self.sock_hdlr)
1387 self.sock_hdlr.close()
1388 finally:
1389 BaseTest.tearDown(self)
1390
1391 def handle_datagram(self, request):
1392 slen = struct.pack('>L', 0) # length of prefix
1393 packet = request.packet[len(slen):]
1394 obj = pickle.loads(packet)
1395 record = logging.makeLogRecord(obj)
1396 self.log_output += record.msg + '\n'
1397 self.handled.set()
1398
1399 def test_output(self):
1400 # The log message sent to the DatagramHandler is properly received.
1401 logger = logging.getLogger("udp")
1402 logger.error("spam")
1403 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001404 self.handled.clear()
1405 logger.error("eggs")
1406 self.handled.wait()
1407 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001408
1409
1410@unittest.skipUnless(threading, 'Threading required for this test.')
1411class SysLogHandlerTest(BaseTest):
1412
1413 """Test for SysLogHandler using UDP."""
1414
1415 def setUp(self):
1416 """Set up a UDP server to receive log messages, and a SysLogHandler
1417 pointing to that server's address and port."""
1418 BaseTest.setUp(self)
1419 addr = ('localhost', 0)
1420 self.server = server = TestUDPServer(addr, self.handle_datagram,
1421 0.01)
1422 server.start()
1423 server.ready.wait()
1424 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1425 server.port))
1426 self.log_output = ''
1427 self.root_logger.removeHandler(self.root_logger.handlers[0])
1428 self.root_logger.addHandler(self.sl_hdlr)
1429 self.handled = threading.Event()
1430
1431 def tearDown(self):
1432 """Shutdown the UDP server."""
1433 try:
1434 self.server.stop(2.0)
1435 self.root_logger.removeHandler(self.sl_hdlr)
1436 self.sl_hdlr.close()
1437 finally:
1438 BaseTest.tearDown(self)
1439
1440 def handle_datagram(self, request):
1441 self.log_output = request.packet
1442 self.handled.set()
1443
1444 def test_output(self):
1445 # The log message sent to the SysLogHandler is properly received.
1446 logger = logging.getLogger("slh")
1447 logger.error("sp\xe4m")
1448 self.handled.wait()
1449 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1450
1451
1452@unittest.skipUnless(threading, 'Threading required for this test.')
1453class HTTPHandlerTest(BaseTest):
1454
1455 """Test for HTTPHandler."""
1456
1457 def setUp(self):
1458 """Set up an HTTP server to receive log messages, and a HTTPHandler
1459 pointing to that server's address and port."""
1460 BaseTest.setUp(self)
1461 addr = ('localhost', 0)
1462 self.server = server = TestHTTPServer(addr, self.handle_request,
1463 0.01)
1464 server.start()
1465 server.ready.wait()
1466 host = 'localhost:%d' % server.server_port
1467 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob')
1468 self.log_data = None
1469 self.root_logger.removeHandler(self.root_logger.handlers[0])
1470 self.root_logger.addHandler(self.h_hdlr)
1471 self.handled = threading.Event()
1472
1473 def tearDown(self):
1474 """Shutdown the UDP server."""
1475 try:
1476 self.server.stop(2.0)
1477 self.root_logger.removeHandler(self.h_hdlr)
1478 self.h_hdlr.close()
1479 finally:
1480 BaseTest.tearDown(self)
1481
1482 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001483 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001484 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001485 if self.command == 'POST':
1486 try:
1487 rlen = int(request.headers['Content-Length'])
1488 self.post_data = request.rfile.read(rlen)
1489 except:
1490 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001491 request.send_response(200)
Vinay Sajip463ac512011-05-10 09:10:08 +01001492 request.end_headers()
Vinay Sajip7367d082011-05-02 13:17:27 +01001493 self.handled.set()
1494
1495 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001496 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001497 logger = logging.getLogger("http")
Vinay Sajip0372e102011-05-05 12:59:14 +01001498 for method in ('GET', 'POST'):
1499 self.h_hdlr.method = method
1500 msg = "sp\xe4m"
1501 logger.error(msg)
1502 self.handled.wait()
1503 self.assertEqual(self.log_data.path, '/frob')
1504 self.assertEqual(self.command, method)
1505 if method == 'GET':
1506 d = parse_qs(self.log_data.query)
1507 else:
1508 d = parse_qs(self.post_data.decode('utf-8'))
1509 self.assertEqual(d['name'], ['http'])
1510 self.assertEqual(d['funcName'], ['test_output'])
1511 self.assertEqual(d['msg'], [msg])
Vinay Sajip7367d082011-05-02 13:17:27 +01001512
Christian Heimes180510d2008-03-03 19:15:45 +00001513class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001514
Christian Heimes180510d2008-03-03 19:15:45 +00001515 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001516
Christian Heimes180510d2008-03-03 19:15:45 +00001517 def setUp(self):
1518 """Create a dict to remember potentially destroyed objects."""
1519 BaseTest.setUp(self)
1520 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001521
Christian Heimes180510d2008-03-03 19:15:45 +00001522 def _watch_for_survival(self, *args):
1523 """Watch the given objects for survival, by creating weakrefs to
1524 them."""
1525 for obj in args:
1526 key = id(obj), repr(obj)
1527 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001528
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001529 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001530 """Assert that all objects watched for survival have survived."""
1531 # Trigger cycle breaking.
1532 gc.collect()
1533 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001534 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001535 if ref() is None:
1536 dead.append(repr_)
1537 if dead:
1538 self.fail("%d objects should have survived "
1539 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001540
Christian Heimes180510d2008-03-03 19:15:45 +00001541 def test_persistent_loggers(self):
1542 # Logger objects are persistent and retain their configuration, even
1543 # if visible references are destroyed.
1544 self.root_logger.setLevel(logging.INFO)
1545 foo = logging.getLogger("foo")
1546 self._watch_for_survival(foo)
1547 foo.setLevel(logging.DEBUG)
1548 self.root_logger.debug(self.next_message())
1549 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001550 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001551 ('foo', 'DEBUG', '2'),
1552 ])
1553 del foo
1554 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001555 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001556 # foo has retained its settings.
1557 bar = logging.getLogger("foo")
1558 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001559 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001560 ('foo', 'DEBUG', '2'),
1561 ('foo', 'DEBUG', '3'),
1562 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001563
Benjamin Petersonf91df042009-02-13 02:50:59 +00001564
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001565class EncodingTest(BaseTest):
1566 def test_encoding_plain_file(self):
1567 # In Python 2.x, a plain file object is treated as having no encoding.
1568 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001569 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1570 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001571 # the non-ascii data we write to the log.
1572 data = "foo\x80"
1573 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001574 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001575 log.addHandler(handler)
1576 try:
1577 # write non-ascii data to the log.
1578 log.warning(data)
1579 finally:
1580 log.removeHandler(handler)
1581 handler.close()
1582 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001583 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001584 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001585 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001586 finally:
1587 f.close()
1588 finally:
1589 if os.path.isfile(fn):
1590 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001591
Benjamin Petersonf91df042009-02-13 02:50:59 +00001592 def test_encoding_cyrillic_unicode(self):
1593 log = logging.getLogger("test")
1594 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1595 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1596 #Ensure it's written in a Cyrillic encoding
1597 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001598 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001599 stream = io.BytesIO()
1600 writer = writer_class(stream, 'strict')
1601 handler = logging.StreamHandler(writer)
1602 log.addHandler(handler)
1603 try:
1604 log.warning(message)
1605 finally:
1606 log.removeHandler(handler)
1607 handler.close()
1608 # check we wrote exactly those bytes, ignoring trailing \n etc
1609 s = stream.getvalue()
1610 #Compare against what the data should be when encoded in CP-1251
1611 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1612
1613
Georg Brandlf9734072008-12-07 15:30:06 +00001614class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001615
Georg Brandlf9734072008-12-07 15:30:06 +00001616 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001617 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001618 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001619 self.addCleanup(lambda: logging.captureWarnings(False))
1620 warnings.filterwarnings("always", category=UserWarning)
1621 stream = io.StringIO()
1622 h = logging.StreamHandler(stream)
1623 logger = logging.getLogger("py.warnings")
1624 logger.addHandler(h)
1625 warnings.warn("I'm warning you...")
1626 logger.removeHandler(h)
1627 s = stream.getvalue()
1628 h.close()
1629 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001630
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001631 #See if an explicit file uses the original implementation
1632 a_file = io.StringIO()
1633 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1634 a_file, "Dummy line")
1635 s = a_file.getvalue()
1636 a_file.close()
1637 self.assertEqual(s,
1638 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1639
1640 def test_warnings_no_handlers(self):
1641 with warnings.catch_warnings():
1642 logging.captureWarnings(True)
1643 self.addCleanup(lambda: logging.captureWarnings(False))
1644
1645 # confirm our assumption: no loggers are set
1646 logger = logging.getLogger("py.warnings")
1647 assert logger.handlers == []
1648
1649 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1650 self.assertTrue(len(logger.handlers) == 1)
1651 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001652
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001653
1654def formatFunc(format, datefmt=None):
1655 return logging.Formatter(format, datefmt)
1656
1657def handlerFunc():
1658 return logging.StreamHandler()
1659
1660class CustomHandler(logging.StreamHandler):
1661 pass
1662
1663class ConfigDictTest(BaseTest):
1664
1665 """Reading logging config from a dictionary."""
1666
1667 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1668
1669 # config0 is a standard configuration.
1670 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001671 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001672 'formatters': {
1673 'form1' : {
1674 'format' : '%(levelname)s ++ %(message)s',
1675 },
1676 },
1677 'handlers' : {
1678 'hand1' : {
1679 'class' : 'logging.StreamHandler',
1680 'formatter' : 'form1',
1681 'level' : 'NOTSET',
1682 'stream' : 'ext://sys.stdout',
1683 },
1684 },
1685 'root' : {
1686 'level' : 'WARNING',
1687 'handlers' : ['hand1'],
1688 },
1689 }
1690
1691 # config1 adds a little to the standard configuration.
1692 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001693 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001694 'formatters': {
1695 'form1' : {
1696 'format' : '%(levelname)s ++ %(message)s',
1697 },
1698 },
1699 'handlers' : {
1700 'hand1' : {
1701 'class' : 'logging.StreamHandler',
1702 'formatter' : 'form1',
1703 'level' : 'NOTSET',
1704 'stream' : 'ext://sys.stdout',
1705 },
1706 },
1707 'loggers' : {
1708 'compiler.parser' : {
1709 'level' : 'DEBUG',
1710 'handlers' : ['hand1'],
1711 },
1712 },
1713 'root' : {
1714 'level' : 'WARNING',
1715 },
1716 }
1717
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001718 # config1a moves the handler to the root. Used with config8a
1719 config1a = {
1720 'version': 1,
1721 'formatters': {
1722 'form1' : {
1723 'format' : '%(levelname)s ++ %(message)s',
1724 },
1725 },
1726 'handlers' : {
1727 'hand1' : {
1728 'class' : 'logging.StreamHandler',
1729 'formatter' : 'form1',
1730 'level' : 'NOTSET',
1731 'stream' : 'ext://sys.stdout',
1732 },
1733 },
1734 'loggers' : {
1735 'compiler.parser' : {
1736 'level' : 'DEBUG',
1737 },
1738 },
1739 'root' : {
1740 'level' : 'WARNING',
1741 'handlers' : ['hand1'],
1742 },
1743 }
1744
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001745 # config2 has a subtle configuration error that should be reported
1746 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001747 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001748 'formatters': {
1749 'form1' : {
1750 'format' : '%(levelname)s ++ %(message)s',
1751 },
1752 },
1753 'handlers' : {
1754 'hand1' : {
1755 'class' : 'logging.StreamHandler',
1756 'formatter' : 'form1',
1757 'level' : 'NOTSET',
1758 'stream' : 'ext://sys.stdbout',
1759 },
1760 },
1761 'loggers' : {
1762 'compiler.parser' : {
1763 'level' : 'DEBUG',
1764 'handlers' : ['hand1'],
1765 },
1766 },
1767 'root' : {
1768 'level' : 'WARNING',
1769 },
1770 }
1771
1772 #As config1 but with a misspelt level on a handler
1773 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001774 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001775 'formatters': {
1776 'form1' : {
1777 'format' : '%(levelname)s ++ %(message)s',
1778 },
1779 },
1780 'handlers' : {
1781 'hand1' : {
1782 'class' : 'logging.StreamHandler',
1783 'formatter' : 'form1',
1784 'level' : 'NTOSET',
1785 'stream' : 'ext://sys.stdout',
1786 },
1787 },
1788 'loggers' : {
1789 'compiler.parser' : {
1790 'level' : 'DEBUG',
1791 'handlers' : ['hand1'],
1792 },
1793 },
1794 'root' : {
1795 'level' : 'WARNING',
1796 },
1797 }
1798
1799
1800 #As config1 but with a misspelt level on a logger
1801 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001802 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001803 'formatters': {
1804 'form1' : {
1805 'format' : '%(levelname)s ++ %(message)s',
1806 },
1807 },
1808 'handlers' : {
1809 'hand1' : {
1810 'class' : 'logging.StreamHandler',
1811 'formatter' : 'form1',
1812 'level' : 'NOTSET',
1813 'stream' : 'ext://sys.stdout',
1814 },
1815 },
1816 'loggers' : {
1817 'compiler.parser' : {
1818 'level' : 'DEBUG',
1819 'handlers' : ['hand1'],
1820 },
1821 },
1822 'root' : {
1823 'level' : 'WRANING',
1824 },
1825 }
1826
1827 # config3 has a less subtle configuration error
1828 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001829 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001830 'formatters': {
1831 'form1' : {
1832 'format' : '%(levelname)s ++ %(message)s',
1833 },
1834 },
1835 'handlers' : {
1836 'hand1' : {
1837 'class' : 'logging.StreamHandler',
1838 'formatter' : 'misspelled_name',
1839 'level' : 'NOTSET',
1840 'stream' : 'ext://sys.stdout',
1841 },
1842 },
1843 'loggers' : {
1844 'compiler.parser' : {
1845 'level' : 'DEBUG',
1846 'handlers' : ['hand1'],
1847 },
1848 },
1849 'root' : {
1850 'level' : 'WARNING',
1851 },
1852 }
1853
1854 # config4 specifies a custom formatter class to be loaded
1855 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001856 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001857 'formatters': {
1858 'form1' : {
1859 '()' : __name__ + '.ExceptionFormatter',
1860 'format' : '%(levelname)s:%(name)s:%(message)s',
1861 },
1862 },
1863 'handlers' : {
1864 'hand1' : {
1865 'class' : 'logging.StreamHandler',
1866 'formatter' : 'form1',
1867 'level' : 'NOTSET',
1868 'stream' : 'ext://sys.stdout',
1869 },
1870 },
1871 'root' : {
1872 'level' : 'NOTSET',
1873 'handlers' : ['hand1'],
1874 },
1875 }
1876
1877 # As config4 but using an actual callable rather than a string
1878 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001879 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001880 'formatters': {
1881 'form1' : {
1882 '()' : ExceptionFormatter,
1883 'format' : '%(levelname)s:%(name)s:%(message)s',
1884 },
1885 'form2' : {
1886 '()' : __name__ + '.formatFunc',
1887 'format' : '%(levelname)s:%(name)s:%(message)s',
1888 },
1889 'form3' : {
1890 '()' : formatFunc,
1891 'format' : '%(levelname)s:%(name)s:%(message)s',
1892 },
1893 },
1894 'handlers' : {
1895 'hand1' : {
1896 'class' : 'logging.StreamHandler',
1897 'formatter' : 'form1',
1898 'level' : 'NOTSET',
1899 'stream' : 'ext://sys.stdout',
1900 },
1901 'hand2' : {
1902 '()' : handlerFunc,
1903 },
1904 },
1905 'root' : {
1906 'level' : 'NOTSET',
1907 'handlers' : ['hand1'],
1908 },
1909 }
1910
1911 # config5 specifies a custom handler class to be loaded
1912 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001913 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001914 'formatters': {
1915 'form1' : {
1916 'format' : '%(levelname)s ++ %(message)s',
1917 },
1918 },
1919 'handlers' : {
1920 'hand1' : {
1921 'class' : __name__ + '.CustomHandler',
1922 'formatter' : 'form1',
1923 'level' : 'NOTSET',
1924 'stream' : 'ext://sys.stdout',
1925 },
1926 },
1927 'loggers' : {
1928 'compiler.parser' : {
1929 'level' : 'DEBUG',
1930 'handlers' : ['hand1'],
1931 },
1932 },
1933 'root' : {
1934 'level' : 'WARNING',
1935 },
1936 }
1937
1938 # config6 specifies a custom handler class to be loaded
1939 # but has bad arguments
1940 config6 = {
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' : __name__ + '.CustomHandler',
1950 'formatter' : 'form1',
1951 'level' : 'NOTSET',
1952 'stream' : 'ext://sys.stdout',
1953 '9' : 'invalid parameter name',
1954 },
1955 },
1956 'loggers' : {
1957 'compiler.parser' : {
1958 'level' : 'DEBUG',
1959 'handlers' : ['hand1'],
1960 },
1961 },
1962 'root' : {
1963 'level' : 'WARNING',
1964 },
1965 }
1966
1967 #config 7 does not define compiler.parser but defines compiler.lexer
1968 #so compiler.parser should be disabled after applying it
1969 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001970 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001971 'formatters': {
1972 'form1' : {
1973 'format' : '%(levelname)s ++ %(message)s',
1974 },
1975 },
1976 'handlers' : {
1977 'hand1' : {
1978 'class' : 'logging.StreamHandler',
1979 'formatter' : 'form1',
1980 'level' : 'NOTSET',
1981 'stream' : 'ext://sys.stdout',
1982 },
1983 },
1984 'loggers' : {
1985 'compiler.lexer' : {
1986 'level' : 'DEBUG',
1987 'handlers' : ['hand1'],
1988 },
1989 },
1990 'root' : {
1991 'level' : 'WARNING',
1992 },
1993 }
1994
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001995 # config8 defines both compiler and compiler.lexer
1996 # so compiler.parser should not be disabled (since
1997 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001998 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001999 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002000 'disable_existing_loggers' : False,
2001 'formatters': {
2002 'form1' : {
2003 'format' : '%(levelname)s ++ %(message)s',
2004 },
2005 },
2006 'handlers' : {
2007 'hand1' : {
2008 'class' : 'logging.StreamHandler',
2009 'formatter' : 'form1',
2010 'level' : 'NOTSET',
2011 'stream' : 'ext://sys.stdout',
2012 },
2013 },
2014 'loggers' : {
2015 'compiler' : {
2016 'level' : 'DEBUG',
2017 'handlers' : ['hand1'],
2018 },
2019 'compiler.lexer' : {
2020 },
2021 },
2022 'root' : {
2023 'level' : 'WARNING',
2024 },
2025 }
2026
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002027 # config8a disables existing loggers
2028 config8a = {
2029 'version': 1,
2030 'disable_existing_loggers' : True,
2031 'formatters': {
2032 'form1' : {
2033 'format' : '%(levelname)s ++ %(message)s',
2034 },
2035 },
2036 'handlers' : {
2037 'hand1' : {
2038 'class' : 'logging.StreamHandler',
2039 'formatter' : 'form1',
2040 'level' : 'NOTSET',
2041 'stream' : 'ext://sys.stdout',
2042 },
2043 },
2044 'loggers' : {
2045 'compiler' : {
2046 'level' : 'DEBUG',
2047 'handlers' : ['hand1'],
2048 },
2049 'compiler.lexer' : {
2050 },
2051 },
2052 'root' : {
2053 'level' : 'WARNING',
2054 },
2055 }
2056
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002057 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002058 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002059 'formatters': {
2060 'form1' : {
2061 'format' : '%(levelname)s ++ %(message)s',
2062 },
2063 },
2064 'handlers' : {
2065 'hand1' : {
2066 'class' : 'logging.StreamHandler',
2067 'formatter' : 'form1',
2068 'level' : 'WARNING',
2069 'stream' : 'ext://sys.stdout',
2070 },
2071 },
2072 'loggers' : {
2073 'compiler.parser' : {
2074 'level' : 'WARNING',
2075 'handlers' : ['hand1'],
2076 },
2077 },
2078 'root' : {
2079 'level' : 'NOTSET',
2080 },
2081 }
2082
2083 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002084 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002085 'incremental' : True,
2086 'handlers' : {
2087 'hand1' : {
2088 'level' : 'WARNING',
2089 },
2090 },
2091 'loggers' : {
2092 'compiler.parser' : {
2093 'level' : 'INFO',
2094 },
2095 },
2096 }
2097
2098 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002099 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002100 'incremental' : True,
2101 'handlers' : {
2102 'hand1' : {
2103 'level' : 'INFO',
2104 },
2105 },
2106 'loggers' : {
2107 'compiler.parser' : {
2108 'level' : 'INFO',
2109 },
2110 },
2111 }
2112
2113 #As config1 but with a filter added
2114 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002115 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002116 'formatters': {
2117 'form1' : {
2118 'format' : '%(levelname)s ++ %(message)s',
2119 },
2120 },
2121 'filters' : {
2122 'filt1' : {
2123 'name' : 'compiler.parser',
2124 },
2125 },
2126 'handlers' : {
2127 'hand1' : {
2128 'class' : 'logging.StreamHandler',
2129 'formatter' : 'form1',
2130 'level' : 'NOTSET',
2131 'stream' : 'ext://sys.stdout',
2132 'filters' : ['filt1'],
2133 },
2134 },
2135 'loggers' : {
2136 'compiler.parser' : {
2137 'level' : 'DEBUG',
2138 'filters' : ['filt1'],
2139 },
2140 },
2141 'root' : {
2142 'level' : 'WARNING',
2143 'handlers' : ['hand1'],
2144 },
2145 }
2146
2147 #As config1 but using cfg:// references
2148 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002149 'version': 1,
2150 'true_formatters': {
2151 'form1' : {
2152 'format' : '%(levelname)s ++ %(message)s',
2153 },
2154 },
2155 'handler_configs': {
2156 'hand1' : {
2157 'class' : 'logging.StreamHandler',
2158 'formatter' : 'form1',
2159 'level' : 'NOTSET',
2160 'stream' : 'ext://sys.stdout',
2161 },
2162 },
2163 'formatters' : 'cfg://true_formatters',
2164 'handlers' : {
2165 'hand1' : 'cfg://handler_configs[hand1]',
2166 },
2167 'loggers' : {
2168 'compiler.parser' : {
2169 'level' : 'DEBUG',
2170 'handlers' : ['hand1'],
2171 },
2172 },
2173 'root' : {
2174 'level' : 'WARNING',
2175 },
2176 }
2177
2178 #As config11 but missing the version key
2179 config12 = {
2180 'true_formatters': {
2181 'form1' : {
2182 'format' : '%(levelname)s ++ %(message)s',
2183 },
2184 },
2185 'handler_configs': {
2186 'hand1' : {
2187 'class' : 'logging.StreamHandler',
2188 'formatter' : 'form1',
2189 'level' : 'NOTSET',
2190 'stream' : 'ext://sys.stdout',
2191 },
2192 },
2193 'formatters' : 'cfg://true_formatters',
2194 'handlers' : {
2195 'hand1' : 'cfg://handler_configs[hand1]',
2196 },
2197 'loggers' : {
2198 'compiler.parser' : {
2199 'level' : 'DEBUG',
2200 'handlers' : ['hand1'],
2201 },
2202 },
2203 'root' : {
2204 'level' : 'WARNING',
2205 },
2206 }
2207
2208 #As config11 but using an unsupported version
2209 config13 = {
2210 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002211 'true_formatters': {
2212 'form1' : {
2213 'format' : '%(levelname)s ++ %(message)s',
2214 },
2215 },
2216 'handler_configs': {
2217 'hand1' : {
2218 'class' : 'logging.StreamHandler',
2219 'formatter' : 'form1',
2220 'level' : 'NOTSET',
2221 'stream' : 'ext://sys.stdout',
2222 },
2223 },
2224 'formatters' : 'cfg://true_formatters',
2225 'handlers' : {
2226 'hand1' : 'cfg://handler_configs[hand1]',
2227 },
2228 'loggers' : {
2229 'compiler.parser' : {
2230 'level' : 'DEBUG',
2231 'handlers' : ['hand1'],
2232 },
2233 },
2234 'root' : {
2235 'level' : 'WARNING',
2236 },
2237 }
2238
2239 def apply_config(self, conf):
2240 logging.config.dictConfig(conf)
2241
2242 def test_config0_ok(self):
2243 # A simple config which overrides the default settings.
2244 with captured_stdout() as output:
2245 self.apply_config(self.config0)
2246 logger = logging.getLogger()
2247 # Won't output anything
2248 logger.info(self.next_message())
2249 # Outputs a message
2250 logger.error(self.next_message())
2251 self.assert_log_lines([
2252 ('ERROR', '2'),
2253 ], stream=output)
2254 # Original logger output is empty.
2255 self.assert_log_lines([])
2256
2257 def test_config1_ok(self, config=config1):
2258 # A config defining a sub-parser as well.
2259 with captured_stdout() as output:
2260 self.apply_config(config)
2261 logger = logging.getLogger("compiler.parser")
2262 # Both will output a message
2263 logger.info(self.next_message())
2264 logger.error(self.next_message())
2265 self.assert_log_lines([
2266 ('INFO', '1'),
2267 ('ERROR', '2'),
2268 ], stream=output)
2269 # Original logger output is empty.
2270 self.assert_log_lines([])
2271
2272 def test_config2_failure(self):
2273 # A simple config which overrides the default settings.
2274 self.assertRaises(Exception, self.apply_config, self.config2)
2275
2276 def test_config2a_failure(self):
2277 # A simple config which overrides the default settings.
2278 self.assertRaises(Exception, self.apply_config, self.config2a)
2279
2280 def test_config2b_failure(self):
2281 # A simple config which overrides the default settings.
2282 self.assertRaises(Exception, self.apply_config, self.config2b)
2283
2284 def test_config3_failure(self):
2285 # A simple config which overrides the default settings.
2286 self.assertRaises(Exception, self.apply_config, self.config3)
2287
2288 def test_config4_ok(self):
2289 # A config specifying a custom formatter class.
2290 with captured_stdout() as output:
2291 self.apply_config(self.config4)
2292 #logger = logging.getLogger()
2293 try:
2294 raise RuntimeError()
2295 except RuntimeError:
2296 logging.exception("just testing")
2297 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002298 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002299 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2300 # Original logger output is empty
2301 self.assert_log_lines([])
2302
2303 def test_config4a_ok(self):
2304 # A config specifying a custom formatter class.
2305 with captured_stdout() as output:
2306 self.apply_config(self.config4a)
2307 #logger = logging.getLogger()
2308 try:
2309 raise RuntimeError()
2310 except RuntimeError:
2311 logging.exception("just testing")
2312 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002313 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002314 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2315 # Original logger output is empty
2316 self.assert_log_lines([])
2317
2318 def test_config5_ok(self):
2319 self.test_config1_ok(config=self.config5)
2320
2321 def test_config6_failure(self):
2322 self.assertRaises(Exception, self.apply_config, self.config6)
2323
2324 def test_config7_ok(self):
2325 with captured_stdout() as output:
2326 self.apply_config(self.config1)
2327 logger = logging.getLogger("compiler.parser")
2328 # Both will output a message
2329 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.config7)
2339 logger = logging.getLogger("compiler.parser")
2340 self.assertTrue(logger.disabled)
2341 logger = logging.getLogger("compiler.lexer")
2342 # Both will output a message
2343 logger.info(self.next_message())
2344 logger.error(self.next_message())
2345 self.assert_log_lines([
2346 ('INFO', '3'),
2347 ('ERROR', '4'),
2348 ], stream=output)
2349 # Original logger output is empty.
2350 self.assert_log_lines([])
2351
2352 #Same as test_config_7_ok but don't disable old loggers.
2353 def test_config_8_ok(self):
2354 with captured_stdout() as output:
2355 self.apply_config(self.config1)
2356 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002357 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002358 logger.info(self.next_message())
2359 logger.error(self.next_message())
2360 self.assert_log_lines([
2361 ('INFO', '1'),
2362 ('ERROR', '2'),
2363 ], stream=output)
2364 # Original logger output is empty.
2365 self.assert_log_lines([])
2366 with captured_stdout() as output:
2367 self.apply_config(self.config8)
2368 logger = logging.getLogger("compiler.parser")
2369 self.assertFalse(logger.disabled)
2370 # Both will output a message
2371 logger.info(self.next_message())
2372 logger.error(self.next_message())
2373 logger = logging.getLogger("compiler.lexer")
2374 # Both will output a message
2375 logger.info(self.next_message())
2376 logger.error(self.next_message())
2377 self.assert_log_lines([
2378 ('INFO', '3'),
2379 ('ERROR', '4'),
2380 ('INFO', '5'),
2381 ('ERROR', '6'),
2382 ], stream=output)
2383 # Original logger output is empty.
2384 self.assert_log_lines([])
2385
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002386 def test_config_8a_ok(self):
2387 with captured_stdout() as output:
2388 self.apply_config(self.config1a)
2389 logger = logging.getLogger("compiler.parser")
2390 # See issue #11424. compiler-hyphenated sorts
2391 # between compiler and compiler.xyz and this
2392 # was preventing compiler.xyz from being included
2393 # in the child loggers of compiler because of an
2394 # overzealous loop termination condition.
2395 hyphenated = logging.getLogger('compiler-hyphenated')
2396 # All will output a message
2397 logger.info(self.next_message())
2398 logger.error(self.next_message())
2399 hyphenated.critical(self.next_message())
2400 self.assert_log_lines([
2401 ('INFO', '1'),
2402 ('ERROR', '2'),
2403 ('CRITICAL', '3'),
2404 ], stream=output)
2405 # Original logger output is empty.
2406 self.assert_log_lines([])
2407 with captured_stdout() as output:
2408 self.apply_config(self.config8a)
2409 logger = logging.getLogger("compiler.parser")
2410 self.assertFalse(logger.disabled)
2411 # Both will output a message
2412 logger.info(self.next_message())
2413 logger.error(self.next_message())
2414 logger = logging.getLogger("compiler.lexer")
2415 # Both will output a message
2416 logger.info(self.next_message())
2417 logger.error(self.next_message())
2418 # Will not appear
2419 hyphenated.critical(self.next_message())
2420 self.assert_log_lines([
2421 ('INFO', '4'),
2422 ('ERROR', '5'),
2423 ('INFO', '6'),
2424 ('ERROR', '7'),
2425 ], stream=output)
2426 # Original logger output is empty.
2427 self.assert_log_lines([])
2428
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002429 def test_config_9_ok(self):
2430 with captured_stdout() as output:
2431 self.apply_config(self.config9)
2432 logger = logging.getLogger("compiler.parser")
2433 #Nothing will be output since both handler and logger are set to WARNING
2434 logger.info(self.next_message())
2435 self.assert_log_lines([], stream=output)
2436 self.apply_config(self.config9a)
2437 #Nothing will be output since both handler is still set to WARNING
2438 logger.info(self.next_message())
2439 self.assert_log_lines([], stream=output)
2440 self.apply_config(self.config9b)
2441 #Message should now be output
2442 logger.info(self.next_message())
2443 self.assert_log_lines([
2444 ('INFO', '3'),
2445 ], stream=output)
2446
2447 def test_config_10_ok(self):
2448 with captured_stdout() as output:
2449 self.apply_config(self.config10)
2450 logger = logging.getLogger("compiler.parser")
2451 logger.warning(self.next_message())
2452 logger = logging.getLogger('compiler')
2453 #Not output, because filtered
2454 logger.warning(self.next_message())
2455 logger = logging.getLogger('compiler.lexer')
2456 #Not output, because filtered
2457 logger.warning(self.next_message())
2458 logger = logging.getLogger("compiler.parser.codegen")
2459 #Output, as not filtered
2460 logger.error(self.next_message())
2461 self.assert_log_lines([
2462 ('WARNING', '1'),
2463 ('ERROR', '4'),
2464 ], stream=output)
2465
2466 def test_config11_ok(self):
2467 self.test_config1_ok(self.config11)
2468
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002469 def test_config12_failure(self):
2470 self.assertRaises(Exception, self.apply_config, self.config12)
2471
2472 def test_config13_failure(self):
2473 self.assertRaises(Exception, self.apply_config, self.config13)
2474
Victor Stinner45df8202010-04-28 22:31:17 +00002475 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002476 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002477 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002478 # Ask for a randomly assigned port (by using port 0)
2479 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002480 t.start()
2481 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002482 # Now get the port allocated
2483 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002484 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002485 try:
2486 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2487 sock.settimeout(2.0)
2488 sock.connect(('localhost', port))
2489
2490 slen = struct.pack('>L', len(text))
2491 s = slen + text
2492 sentsofar = 0
2493 left = len(s)
2494 while left > 0:
2495 sent = sock.send(s[sentsofar:])
2496 sentsofar += sent
2497 left -= sent
2498 sock.close()
2499 finally:
2500 t.ready.wait(2.0)
2501 logging.config.stopListening()
2502 t.join(2.0)
2503
2504 def test_listen_config_10_ok(self):
2505 with captured_stdout() as output:
2506 self.setup_via_listener(json.dumps(self.config10))
2507 logger = logging.getLogger("compiler.parser")
2508 logger.warning(self.next_message())
2509 logger = logging.getLogger('compiler')
2510 #Not output, because filtered
2511 logger.warning(self.next_message())
2512 logger = logging.getLogger('compiler.lexer')
2513 #Not output, because filtered
2514 logger.warning(self.next_message())
2515 logger = logging.getLogger("compiler.parser.codegen")
2516 #Output, as not filtered
2517 logger.error(self.next_message())
2518 self.assert_log_lines([
2519 ('WARNING', '1'),
2520 ('ERROR', '4'),
2521 ], stream=output)
2522
2523 def test_listen_config_1_ok(self):
2524 with captured_stdout() as output:
2525 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2526 logger = logging.getLogger("compiler.parser")
2527 # Both will output a message
2528 logger.info(self.next_message())
2529 logger.error(self.next_message())
2530 self.assert_log_lines([
2531 ('INFO', '1'),
2532 ('ERROR', '2'),
2533 ], stream=output)
2534 # Original logger output is empty.
2535 self.assert_log_lines([])
2536
Vinay Sajip373baef2011-04-26 20:05:24 +01002537 def test_baseconfig(self):
2538 d = {
2539 'atuple': (1, 2, 3),
2540 'alist': ['a', 'b', 'c'],
2541 'adict': {'d': 'e', 'f': 3 },
2542 'nest1': ('g', ('h', 'i'), 'j'),
2543 'nest2': ['k', ['l', 'm'], 'n'],
2544 'nest3': ['o', 'cfg://alist', 'p'],
2545 }
2546 bc = logging.config.BaseConfigurator(d)
2547 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2548 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2549 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2550 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2551 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2552 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2553 v = bc.convert('cfg://nest3')
2554 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2555 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2556 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2557 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002558
2559class ManagerTest(BaseTest):
2560 def test_manager_loggerclass(self):
2561 logged = []
2562
2563 class MyLogger(logging.Logger):
2564 def _log(self, level, msg, args, exc_info=None, extra=None):
2565 logged.append(msg)
2566
2567 man = logging.Manager(None)
2568 self.assertRaises(TypeError, man.setLoggerClass, int)
2569 man.setLoggerClass(MyLogger)
2570 logger = man.getLogger('test')
2571 logger.warning('should appear in logged')
2572 logging.warning('should not appear in logged')
2573
2574 self.assertEqual(logged, ['should appear in logged'])
2575
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002576 def test_set_log_record_factory(self):
2577 man = logging.Manager(None)
2578 expected = object()
2579 man.setLogRecordFactory(expected)
2580 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002581
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002582class ChildLoggerTest(BaseTest):
2583 def test_child_loggers(self):
2584 r = logging.getLogger()
2585 l1 = logging.getLogger('abc')
2586 l2 = logging.getLogger('def.ghi')
2587 c1 = r.getChild('xyz')
2588 c2 = r.getChild('uvw.xyz')
2589 self.assertTrue(c1 is logging.getLogger('xyz'))
2590 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2591 c1 = l1.getChild('def')
2592 c2 = c1.getChild('ghi')
2593 c3 = l1.getChild('def.ghi')
2594 self.assertTrue(c1 is logging.getLogger('abc.def'))
2595 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2596 self.assertTrue(c2 is c3)
2597
2598
Vinay Sajip6fac8172010-10-19 20:44:14 +00002599class DerivedLogRecord(logging.LogRecord):
2600 pass
2601
Vinay Sajip61561522010-12-03 11:50:38 +00002602class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002603
2604 def setUp(self):
2605 class CheckingFilter(logging.Filter):
2606 def __init__(self, cls):
2607 self.cls = cls
2608
2609 def filter(self, record):
2610 t = type(record)
2611 if t is not self.cls:
2612 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2613 self.cls)
2614 raise TypeError(msg)
2615 return True
2616
2617 BaseTest.setUp(self)
2618 self.filter = CheckingFilter(DerivedLogRecord)
2619 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002620 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002621
2622 def tearDown(self):
2623 self.root_logger.removeFilter(self.filter)
2624 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002625 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002626
2627 def test_logrecord_class(self):
2628 self.assertRaises(TypeError, self.root_logger.warning,
2629 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002630 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002631 self.root_logger.error(self.next_message())
2632 self.assert_log_lines([
2633 ('root', 'ERROR', '2'),
2634 ])
2635
2636
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002637class QueueHandlerTest(BaseTest):
2638 # Do not bother with a logger name group.
2639 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2640
2641 def setUp(self):
2642 BaseTest.setUp(self)
2643 self.queue = queue.Queue(-1)
2644 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2645 self.que_logger = logging.getLogger('que')
2646 self.que_logger.propagate = False
2647 self.que_logger.setLevel(logging.WARNING)
2648 self.que_logger.addHandler(self.que_hdlr)
2649
2650 def tearDown(self):
2651 self.que_hdlr.close()
2652 BaseTest.tearDown(self)
2653
2654 def test_queue_handler(self):
2655 self.que_logger.debug(self.next_message())
2656 self.assertRaises(queue.Empty, self.queue.get_nowait)
2657 self.que_logger.info(self.next_message())
2658 self.assertRaises(queue.Empty, self.queue.get_nowait)
2659 msg = self.next_message()
2660 self.que_logger.warning(msg)
2661 data = self.queue.get_nowait()
2662 self.assertTrue(isinstance(data, logging.LogRecord))
2663 self.assertEqual(data.name, self.que_logger.name)
2664 self.assertEqual((data.msg, data.args), (msg, None))
2665
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002666 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2667 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002668 def test_queue_listener(self):
2669 handler = TestHandler(Matcher())
2670 listener = logging.handlers.QueueListener(self.queue, handler)
2671 listener.start()
2672 try:
2673 self.que_logger.warning(self.next_message())
2674 self.que_logger.error(self.next_message())
2675 self.que_logger.critical(self.next_message())
2676 finally:
2677 listener.stop()
2678 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2679 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2680 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2681
Vinay Sajip37eb3382011-04-26 20:26:41 +01002682ZERO = datetime.timedelta(0)
2683
2684class UTC(datetime.tzinfo):
2685 def utcoffset(self, dt):
2686 return ZERO
2687
2688 dst = utcoffset
2689
2690 def tzname(self, dt):
2691 return 'UTC'
2692
2693utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002694
Vinay Sajipa39c5712010-10-25 13:57:39 +00002695class FormatterTest(unittest.TestCase):
2696 def setUp(self):
2697 self.common = {
2698 'name': 'formatter.test',
2699 'level': logging.DEBUG,
2700 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2701 'lineno': 42,
2702 'exc_info': None,
2703 'func': None,
2704 'msg': 'Message with %d %s',
2705 'args': (2, 'placeholders'),
2706 }
2707 self.variants = {
2708 }
2709
2710 def get_record(self, name=None):
2711 result = dict(self.common)
2712 if name is not None:
2713 result.update(self.variants[name])
2714 return logging.makeLogRecord(result)
2715
2716 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002717 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002718 r = self.get_record()
2719 f = logging.Formatter('${%(message)s}')
2720 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2721 f = logging.Formatter('%(random)s')
2722 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002723 self.assertFalse(f.usesTime())
2724 f = logging.Formatter('%(asctime)s')
2725 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002726 f = logging.Formatter('%(asctime)-15s')
2727 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002728 f = logging.Formatter('asctime')
2729 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002730
2731 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002732 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002733 r = self.get_record()
2734 f = logging.Formatter('$%{message}%$', style='{')
2735 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2736 f = logging.Formatter('{random}', style='{')
2737 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002738 self.assertFalse(f.usesTime())
2739 f = logging.Formatter('{asctime}', style='{')
2740 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002741 f = logging.Formatter('{asctime!s:15}', style='{')
2742 self.assertTrue(f.usesTime())
2743 f = logging.Formatter('{asctime:15}', style='{')
2744 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002745 f = logging.Formatter('asctime', style='{')
2746 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002747
2748 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002749 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002750 r = self.get_record()
2751 f = logging.Formatter('$message', style='$')
2752 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2753 f = logging.Formatter('$$%${message}%$$', style='$')
2754 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2755 f = logging.Formatter('${random}', style='$')
2756 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002757 self.assertFalse(f.usesTime())
2758 f = logging.Formatter('${asctime}', style='$')
2759 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002760 f = logging.Formatter('${asctime', style='$')
2761 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002762 f = logging.Formatter('$asctime', style='$')
2763 self.assertTrue(f.usesTime())
2764 f = logging.Formatter('asctime', style='$')
2765 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002766
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002767 def test_invalid_style(self):
2768 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2769
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002770 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002771 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002772 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002773 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002774 r.msecs = 123
2775 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002776 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002777 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2778 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002779 f.format(r)
2780 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2781
2782class TestBufferingFormatter(logging.BufferingFormatter):
2783 def formatHeader(self, records):
2784 return '[(%d)' % len(records)
2785
2786 def formatFooter(self, records):
2787 return '(%d)]' % len(records)
2788
2789class BufferingFormatterTest(unittest.TestCase):
2790 def setUp(self):
2791 self.records = [
2792 logging.makeLogRecord({'msg': 'one'}),
2793 logging.makeLogRecord({'msg': 'two'}),
2794 ]
2795
2796 def test_default(self):
2797 f = logging.BufferingFormatter()
2798 self.assertEqual('', f.format([]))
2799 self.assertEqual('onetwo', f.format(self.records))
2800
2801 def test_custom(self):
2802 f = TestBufferingFormatter()
2803 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2804 lf = logging.Formatter('<%(message)s>')
2805 f = TestBufferingFormatter(lf)
2806 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002807
2808class ExceptionTest(BaseTest):
2809 def test_formatting(self):
2810 r = self.root_logger
2811 h = RecordingHandler()
2812 r.addHandler(h)
2813 try:
2814 raise RuntimeError('deliberate mistake')
2815 except:
2816 logging.exception('failed', stack_info=True)
2817 r.removeHandler(h)
2818 h.close()
2819 r = h.records[0]
2820 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2821 'call last):\n'))
2822 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2823 'deliberate mistake'))
2824 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2825 'call last):\n'))
2826 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2827 'stack_info=True)'))
2828
2829
Vinay Sajip5a27d402010-12-10 11:42:57 +00002830class LastResortTest(BaseTest):
2831 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002832 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002833 root = self.root_logger
2834 root.removeHandler(self.root_hdlr)
2835 old_stderr = sys.stderr
2836 old_lastresort = logging.lastResort
2837 old_raise_exceptions = logging.raiseExceptions
2838 try:
2839 sys.stderr = sio = io.StringIO()
2840 root.warning('This is your final chance!')
2841 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2842 #No handlers and no last resort, so 'No handlers' message
2843 logging.lastResort = None
2844 sys.stderr = sio = io.StringIO()
2845 root.warning('This is your final chance!')
2846 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2847 # 'No handlers' message only printed once
2848 sys.stderr = sio = io.StringIO()
2849 root.warning('This is your final chance!')
2850 self.assertEqual(sio.getvalue(), '')
2851 root.manager.emittedNoHandlerWarning = False
2852 #If raiseExceptions is False, no message is printed
2853 logging.raiseExceptions = False
2854 sys.stderr = sio = io.StringIO()
2855 root.warning('This is your final chance!')
2856 self.assertEqual(sio.getvalue(), '')
2857 finally:
2858 sys.stderr = old_stderr
2859 root.addHandler(self.root_hdlr)
2860 logging.lastResort = old_lastresort
2861 logging.raiseExceptions = old_raise_exceptions
2862
2863
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002864class FakeHandler:
2865
2866 def __init__(self, identifier, called):
2867 for method in ('acquire', 'flush', 'close', 'release'):
2868 setattr(self, method, self.record_call(identifier, method, called))
2869
2870 def record_call(self, identifier, method_name, called):
2871 def inner():
2872 called.append('{} - {}'.format(identifier, method_name))
2873 return inner
2874
2875
2876class RecordingHandler(logging.NullHandler):
2877
2878 def __init__(self, *args, **kwargs):
2879 super(RecordingHandler, self).__init__(*args, **kwargs)
2880 self.records = []
2881
2882 def handle(self, record):
2883 """Keep track of all the emitted records."""
2884 self.records.append(record)
2885
2886
2887class ShutdownTest(BaseTest):
2888
Vinay Sajip5e66b162011-04-20 15:41:14 +01002889 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002890
2891 def setUp(self):
2892 super(ShutdownTest, self).setUp()
2893 self.called = []
2894
2895 raise_exceptions = logging.raiseExceptions
2896 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2897
2898 def raise_error(self, error):
2899 def inner():
2900 raise error()
2901 return inner
2902
2903 def test_no_failure(self):
2904 # create some fake handlers
2905 handler0 = FakeHandler(0, self.called)
2906 handler1 = FakeHandler(1, self.called)
2907 handler2 = FakeHandler(2, self.called)
2908
2909 # create live weakref to those handlers
2910 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2911
2912 logging.shutdown(handlerList=list(handlers))
2913
2914 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2915 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2916 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2917 self.assertEqual(expected, self.called)
2918
2919 def _test_with_failure_in_method(self, method, error):
2920 handler = FakeHandler(0, self.called)
2921 setattr(handler, method, self.raise_error(error))
2922 handlers = [logging.weakref.ref(handler)]
2923
2924 logging.shutdown(handlerList=list(handlers))
2925
2926 self.assertEqual('0 - release', self.called[-1])
2927
2928 def test_with_ioerror_in_acquire(self):
2929 self._test_with_failure_in_method('acquire', IOError)
2930
2931 def test_with_ioerror_in_flush(self):
2932 self._test_with_failure_in_method('flush', IOError)
2933
2934 def test_with_ioerror_in_close(self):
2935 self._test_with_failure_in_method('close', IOError)
2936
2937 def test_with_valueerror_in_acquire(self):
2938 self._test_with_failure_in_method('acquire', ValueError)
2939
2940 def test_with_valueerror_in_flush(self):
2941 self._test_with_failure_in_method('flush', ValueError)
2942
2943 def test_with_valueerror_in_close(self):
2944 self._test_with_failure_in_method('close', ValueError)
2945
2946 def test_with_other_error_in_acquire_without_raise(self):
2947 logging.raiseExceptions = False
2948 self._test_with_failure_in_method('acquire', IndexError)
2949
2950 def test_with_other_error_in_flush_without_raise(self):
2951 logging.raiseExceptions = False
2952 self._test_with_failure_in_method('flush', IndexError)
2953
2954 def test_with_other_error_in_close_without_raise(self):
2955 logging.raiseExceptions = False
2956 self._test_with_failure_in_method('close', IndexError)
2957
2958 def test_with_other_error_in_acquire_with_raise(self):
2959 logging.raiseExceptions = True
2960 self.assertRaises(IndexError, self._test_with_failure_in_method,
2961 'acquire', IndexError)
2962
2963 def test_with_other_error_in_flush_with_raise(self):
2964 logging.raiseExceptions = True
2965 self.assertRaises(IndexError, self._test_with_failure_in_method,
2966 'flush', IndexError)
2967
2968 def test_with_other_error_in_close_with_raise(self):
2969 logging.raiseExceptions = True
2970 self.assertRaises(IndexError, self._test_with_failure_in_method,
2971 'close', IndexError)
2972
2973
2974class ModuleLevelMiscTest(BaseTest):
2975
Vinay Sajip5e66b162011-04-20 15:41:14 +01002976 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002977
2978 def test_disable(self):
2979 old_disable = logging.root.manager.disable
2980 # confirm our assumptions are correct
2981 assert old_disable == 0
2982 self.addCleanup(lambda: logging.disable(old_disable))
2983
2984 logging.disable(83)
2985 self.assertEqual(logging.root.manager.disable, 83)
2986
2987 def _test_log(self, method, level=None):
2988 called = []
2989 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002990 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002991
2992 recording = RecordingHandler()
2993 logging.root.addHandler(recording)
2994
2995 log_method = getattr(logging, method)
2996 if level is not None:
2997 log_method(level, "test me: %r", recording)
2998 else:
2999 log_method("test me: %r", recording)
3000
3001 self.assertEqual(len(recording.records), 1)
3002 record = recording.records[0]
3003 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3004
3005 expected_level = level if level is not None else getattr(logging, method.upper())
3006 self.assertEqual(record.levelno, expected_level)
3007
3008 # basicConfig was not called!
3009 self.assertEqual(called, [])
3010
3011 def test_log(self):
3012 self._test_log('log', logging.ERROR)
3013
3014 def test_debug(self):
3015 self._test_log('debug')
3016
3017 def test_info(self):
3018 self._test_log('info')
3019
3020 def test_warning(self):
3021 self._test_log('warning')
3022
3023 def test_error(self):
3024 self._test_log('error')
3025
3026 def test_critical(self):
3027 self._test_log('critical')
3028
3029 def test_set_logger_class(self):
3030 self.assertRaises(TypeError, logging.setLoggerClass, object)
3031
3032 class MyLogger(logging.Logger):
3033 pass
3034
3035 logging.setLoggerClass(MyLogger)
3036 self.assertEqual(logging.getLoggerClass(), MyLogger)
3037
3038 logging.setLoggerClass(logging.Logger)
3039 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3040
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003041class LogRecordTest(BaseTest):
3042 def test_str_rep(self):
3043 r = logging.makeLogRecord({})
3044 s = str(r)
3045 self.assertTrue(s.startswith('<LogRecord: '))
3046 self.assertTrue(s.endswith('>'))
3047
3048 def test_dict_arg(self):
3049 h = RecordingHandler()
3050 r = logging.getLogger()
3051 r.addHandler(h)
3052 d = {'less' : 'more' }
3053 logging.warning('less is %(less)s', d)
3054 self.assertIs(h.records[0].args, d)
3055 self.assertEqual(h.records[0].message, 'less is more')
3056 r.removeHandler(h)
3057 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003058
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003059 def test_multiprocessing(self):
3060 r = logging.makeLogRecord({})
3061 self.assertEqual(r.processName, 'MainProcess')
3062 import multiprocessing as mp
3063 r = logging.makeLogRecord({})
3064 self.assertEqual(r.processName, mp.current_process().name)
3065
3066 def test_optional(self):
3067 r = logging.makeLogRecord({})
3068 NOT_NONE = self.assertIsNotNone
3069 NOT_NONE(r.thread)
3070 NOT_NONE(r.threadName)
3071 NOT_NONE(r.process)
3072 NOT_NONE(r.processName)
3073 log_threads = logging.logThreads
3074 log_processes = logging.logProcesses
3075 log_multiprocessing = logging.logMultiprocessing
3076 try:
3077 logging.logThreads = False
3078 logging.logProcesses = False
3079 logging.logMultiprocessing = False
3080 r = logging.makeLogRecord({})
3081 NONE = self.assertIsNone
3082 NONE(r.thread)
3083 NONE(r.threadName)
3084 NONE(r.process)
3085 NONE(r.processName)
3086 finally:
3087 logging.logThreads = log_threads
3088 logging.logProcesses = log_processes
3089 logging.logMultiprocessing = log_multiprocessing
3090
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003091class BasicConfigTest(unittest.TestCase):
3092
Vinay Sajip95bf5042011-04-20 11:50:56 +01003093 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003094
3095 def setUp(self):
3096 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003097 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003098 self.saved_handlers = logging._handlers.copy()
3099 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003100 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003101 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003102 logging.root.handlers = []
3103
3104 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003105 for h in logging.root.handlers[:]:
3106 logging.root.removeHandler(h)
3107 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003108 super(BasicConfigTest, self).tearDown()
3109
Vinay Sajip3def7e02011-04-20 10:58:06 +01003110 def cleanup(self):
3111 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003112 logging._handlers.clear()
3113 logging._handlers.update(self.saved_handlers)
3114 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003115 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003116
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003117 def test_no_kwargs(self):
3118 logging.basicConfig()
3119
3120 # handler defaults to a StreamHandler to sys.stderr
3121 self.assertEqual(len(logging.root.handlers), 1)
3122 handler = logging.root.handlers[0]
3123 self.assertIsInstance(handler, logging.StreamHandler)
3124 self.assertEqual(handler.stream, sys.stderr)
3125
3126 formatter = handler.formatter
3127 # format defaults to logging.BASIC_FORMAT
3128 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3129 # datefmt defaults to None
3130 self.assertIsNone(formatter.datefmt)
3131 # style defaults to %
3132 self.assertIsInstance(formatter._style, logging.PercentStyle)
3133
3134 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003135 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003136
3137 def test_filename(self):
3138 logging.basicConfig(filename='test.log')
3139
3140 self.assertEqual(len(logging.root.handlers), 1)
3141 handler = logging.root.handlers[0]
3142 self.assertIsInstance(handler, logging.FileHandler)
3143
3144 expected = logging.FileHandler('test.log', 'a')
3145 self.addCleanup(expected.close)
3146 self.assertEqual(handler.stream.mode, expected.stream.mode)
3147 self.assertEqual(handler.stream.name, expected.stream.name)
3148
3149 def test_filemode(self):
3150 logging.basicConfig(filename='test.log', filemode='wb')
3151
3152 handler = logging.root.handlers[0]
3153 expected = logging.FileHandler('test.log', 'wb')
3154 self.addCleanup(expected.close)
3155 self.assertEqual(handler.stream.mode, expected.stream.mode)
3156
3157 def test_stream(self):
3158 stream = io.StringIO()
3159 self.addCleanup(stream.close)
3160 logging.basicConfig(stream=stream)
3161
3162 self.assertEqual(len(logging.root.handlers), 1)
3163 handler = logging.root.handlers[0]
3164 self.assertIsInstance(handler, logging.StreamHandler)
3165 self.assertEqual(handler.stream, stream)
3166
3167 def test_format(self):
3168 logging.basicConfig(format='foo')
3169
3170 formatter = logging.root.handlers[0].formatter
3171 self.assertEqual(formatter._style._fmt, 'foo')
3172
3173 def test_datefmt(self):
3174 logging.basicConfig(datefmt='bar')
3175
3176 formatter = logging.root.handlers[0].formatter
3177 self.assertEqual(formatter.datefmt, 'bar')
3178
3179 def test_style(self):
3180 logging.basicConfig(style='$')
3181
3182 formatter = logging.root.handlers[0].formatter
3183 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3184
3185 def test_level(self):
3186 old_level = logging.root.level
3187 self.addCleanup(lambda: logging.root.setLevel(old_level))
3188
3189 logging.basicConfig(level=57)
3190 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003191 # Test that second call has no effect
3192 logging.basicConfig(level=58)
3193 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003194
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003195 def test_incompatible(self):
3196 assertRaises = self.assertRaises
3197 handlers = [logging.StreamHandler()]
3198 stream = sys.stderr
3199 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3200 stream=stream)
3201 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3202 handlers=handlers)
3203 assertRaises(ValueError, logging.basicConfig, stream=stream,
3204 handlers=handlers)
3205
3206 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003207 handlers = [
3208 logging.StreamHandler(),
3209 logging.StreamHandler(sys.stdout),
3210 logging.StreamHandler(),
3211 ]
3212 f = logging.Formatter()
3213 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003214 logging.basicConfig(handlers=handlers)
3215 self.assertIs(handlers[0], logging.root.handlers[0])
3216 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003217 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003218 self.assertIsNotNone(handlers[0].formatter)
3219 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003220 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003221 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3222
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003223 def _test_log(self, method, level=None):
3224 # logging.root has no handlers so basicConfig should be called
3225 called = []
3226
3227 old_basic_config = logging.basicConfig
3228 def my_basic_config(*a, **kw):
3229 old_basic_config()
3230 old_level = logging.root.level
3231 logging.root.setLevel(100) # avoid having messages in stderr
3232 self.addCleanup(lambda: logging.root.setLevel(old_level))
3233 called.append((a, kw))
3234
3235 patch(self, logging, 'basicConfig', my_basic_config)
3236
3237 log_method = getattr(logging, method)
3238 if level is not None:
3239 log_method(level, "test me")
3240 else:
3241 log_method("test me")
3242
3243 # basicConfig was called with no arguments
3244 self.assertEqual(called, [((), {})])
3245
3246 def test_log(self):
3247 self._test_log('log', logging.WARNING)
3248
3249 def test_debug(self):
3250 self._test_log('debug')
3251
3252 def test_info(self):
3253 self._test_log('info')
3254
3255 def test_warning(self):
3256 self._test_log('warning')
3257
3258 def test_error(self):
3259 self._test_log('error')
3260
3261 def test_critical(self):
3262 self._test_log('critical')
3263
3264
3265class LoggerAdapterTest(unittest.TestCase):
3266
3267 def setUp(self):
3268 super(LoggerAdapterTest, self).setUp()
3269 old_handler_list = logging._handlerList[:]
3270
3271 self.recording = RecordingHandler()
3272 self.logger = logging.root
3273 self.logger.addHandler(self.recording)
3274 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3275 self.addCleanup(self.recording.close)
3276
3277 def cleanup():
3278 logging._handlerList[:] = old_handler_list
3279
3280 self.addCleanup(cleanup)
3281 self.addCleanup(logging.shutdown)
3282 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3283
3284 def test_exception(self):
3285 msg = 'testing exception: %r'
3286 exc = None
3287 try:
3288 assert False
3289 except AssertionError as e:
3290 exc = e
3291 self.adapter.exception(msg, self.recording)
3292
3293 self.assertEqual(len(self.recording.records), 1)
3294 record = self.recording.records[0]
3295 self.assertEqual(record.levelno, logging.ERROR)
3296 self.assertEqual(record.msg, msg)
3297 self.assertEqual(record.args, (self.recording,))
3298 self.assertEqual(record.exc_info,
3299 (exc.__class__, exc, exc.__traceback__))
3300
3301 def test_critical(self):
3302 msg = 'critical test! %r'
3303 self.adapter.critical(msg, self.recording)
3304
3305 self.assertEqual(len(self.recording.records), 1)
3306 record = self.recording.records[0]
3307 self.assertEqual(record.levelno, logging.CRITICAL)
3308 self.assertEqual(record.msg, msg)
3309 self.assertEqual(record.args, (self.recording,))
3310
3311 def test_is_enabled_for(self):
3312 old_disable = self.adapter.logger.manager.disable
3313 self.adapter.logger.manager.disable = 33
3314 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3315 'disable', old_disable))
3316 self.assertFalse(self.adapter.isEnabledFor(32))
3317
3318 def test_has_handlers(self):
3319 self.assertTrue(self.adapter.hasHandlers())
3320
3321 for handler in self.logger.handlers:
3322 self.logger.removeHandler(handler)
3323 assert not self.logger.hasHandlers()
3324
3325 self.assertFalse(self.adapter.hasHandlers())
3326
3327
3328class LoggerTest(BaseTest):
3329
3330 def setUp(self):
3331 super(LoggerTest, self).setUp()
3332 self.recording = RecordingHandler()
3333 self.logger = logging.Logger(name='blah')
3334 self.logger.addHandler(self.recording)
3335 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3336 self.addCleanup(self.recording.close)
3337 self.addCleanup(logging.shutdown)
3338
3339 def test_set_invalid_level(self):
3340 self.assertRaises(TypeError, self.logger.setLevel, object())
3341
3342 def test_exception(self):
3343 msg = 'testing exception: %r'
3344 exc = None
3345 try:
3346 assert False
3347 except AssertionError as e:
3348 exc = e
3349 self.logger.exception(msg, self.recording)
3350
3351 self.assertEqual(len(self.recording.records), 1)
3352 record = self.recording.records[0]
3353 self.assertEqual(record.levelno, logging.ERROR)
3354 self.assertEqual(record.msg, msg)
3355 self.assertEqual(record.args, (self.recording,))
3356 self.assertEqual(record.exc_info,
3357 (exc.__class__, exc, exc.__traceback__))
3358
3359 def test_log_invalid_level_with_raise(self):
3360 old_raise = logging.raiseExceptions
3361 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3362
3363 logging.raiseExceptions = True
3364 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3365
3366 def test_log_invalid_level_no_raise(self):
3367 old_raise = logging.raiseExceptions
3368 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3369
3370 logging.raiseExceptions = False
3371 self.logger.log('10', 'test message') # no exception happens
3372
3373 def test_find_caller_with_stack_info(self):
3374 called = []
3375 patch(self, logging.traceback, 'print_stack',
3376 lambda f, file: called.append(file.getvalue()))
3377
3378 self.logger.findCaller(stack_info=True)
3379
3380 self.assertEqual(len(called), 1)
3381 self.assertEqual('Stack (most recent call last):\n', called[0])
3382
3383 def test_make_record_with_extra_overwrite(self):
3384 name = 'my record'
3385 level = 13
3386 fn = lno = msg = args = exc_info = func = sinfo = None
3387 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3388 exc_info, func, sinfo)
3389
3390 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3391 extra = {key: 'some value'}
3392 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3393 fn, lno, msg, args, exc_info,
3394 extra=extra, sinfo=sinfo)
3395
3396 def test_make_record_with_extra_no_overwrite(self):
3397 name = 'my record'
3398 level = 13
3399 fn = lno = msg = args = exc_info = func = sinfo = None
3400 extra = {'valid_key': 'some value'}
3401 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3402 exc_info, extra=extra, sinfo=sinfo)
3403 self.assertIn('valid_key', result.__dict__)
3404
3405 def test_has_handlers(self):
3406 self.assertTrue(self.logger.hasHandlers())
3407
3408 for handler in self.logger.handlers:
3409 self.logger.removeHandler(handler)
3410 assert not self.logger.hasHandlers()
3411
3412 self.assertFalse(self.logger.hasHandlers())
3413
3414 def test_has_handlers_no_propagate(self):
3415 child_logger = logging.getLogger('blah.child')
3416 child_logger.propagate = False
3417 assert child_logger.handlers == []
3418
3419 self.assertFalse(child_logger.hasHandlers())
3420
3421 def test_is_enabled_for(self):
3422 old_disable = self.logger.manager.disable
3423 self.logger.manager.disable = 23
3424 self.addCleanup(lambda: setattr(self.logger.manager,
3425 'disable', old_disable))
3426 self.assertFalse(self.logger.isEnabledFor(22))
3427
3428
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003429class BaseFileTest(BaseTest):
3430 "Base class for handler tests that write log files"
3431
3432 def setUp(self):
3433 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003434 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3435 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003436 self.rmfiles = []
3437
3438 def tearDown(self):
3439 for fn in self.rmfiles:
3440 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003441 if os.path.exists(self.fn):
3442 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003443 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003444
3445 def assertLogFile(self, filename):
3446 "Assert a log file is there and register it for deletion"
3447 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003448 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003449 self.rmfiles.append(filename)
3450
3451
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003452class FileHandlerTest(BaseFileTest):
3453 def test_delay(self):
3454 os.unlink(self.fn)
3455 fh = logging.FileHandler(self.fn, delay=True)
3456 self.assertIsNone(fh.stream)
3457 self.assertFalse(os.path.exists(self.fn))
3458 fh.handle(logging.makeLogRecord({}))
3459 self.assertIsNotNone(fh.stream)
3460 self.assertTrue(os.path.exists(self.fn))
3461 fh.close()
3462
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003463class RotatingFileHandlerTest(BaseFileTest):
3464 def next_rec(self):
3465 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3466 self.next_message(), None, None, None)
3467
3468 def test_should_not_rollover(self):
3469 # If maxbytes is zero rollover never occurs
3470 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3471 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003472 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003473
3474 def test_should_rollover(self):
3475 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3476 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003477 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003478
3479 def test_file_created(self):
3480 # checks that the file is created and assumes it was created
3481 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003482 rh = logging.handlers.RotatingFileHandler(self.fn)
3483 rh.emit(self.next_rec())
3484 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003485 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003486
3487 def test_rollover_filenames(self):
3488 rh = logging.handlers.RotatingFileHandler(
3489 self.fn, backupCount=2, maxBytes=1)
3490 rh.emit(self.next_rec())
3491 self.assertLogFile(self.fn)
3492 rh.emit(self.next_rec())
3493 self.assertLogFile(self.fn + ".1")
3494 rh.emit(self.next_rec())
3495 self.assertLogFile(self.fn + ".2")
3496 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003497 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003498
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003499class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003500 # other test methods added below
3501 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003502 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3503 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003504 r = logging.makeLogRecord({'msg': 'testing'})
3505 fh.emit(r)
3506 self.assertLogFile(self.fn)
3507 time.sleep(1.0)
3508 fh.emit(r)
3509 fh.close()
3510 # At this point, we should have a recent rotated file which we
3511 # can test for the existence of. However, in practice, on some
3512 # machines which run really slowly, we don't know how far back
3513 # in time to go to look for the log file. So, we go back a fair
3514 # bit, and stop as soon as we see a rotated file. In theory this
3515 # could of course still fail, but the chances are lower.
3516 found = False
3517 now = datetime.datetime.now()
3518 GO_BACK = 2 * 60 # seconds
3519 for secs in range(1, GO_BACK):
3520 prev = now - datetime.timedelta(seconds=secs)
3521 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3522 found = os.path.exists(fn)
3523 if found:
3524 self.rmfiles.append(fn)
3525 break
3526 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3527 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003528
Vinay Sajip0372e102011-05-05 12:59:14 +01003529 def test_invalid(self):
3530 assertRaises = self.assertRaises
3531 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003532 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003533 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003534 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003535 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003536 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003537
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003538def secs(**kw):
3539 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3540
3541for when, exp in (('S', 1),
3542 ('M', 60),
3543 ('H', 60 * 60),
3544 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003545 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003546 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003547 ('W0', secs(days=4, hours=24)),
3548 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003549 def test_compute_rollover(self, when=when, exp=exp):
3550 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003551 self.fn, when=when, interval=1, backupCount=0, utc=True)
3552 currentTime = 0.0
3553 actual = rh.computeRollover(currentTime)
3554 if exp != actual:
3555 # Failures occur on some systems for MIDNIGHT and W0.
3556 # Print detailed calculation for MIDNIGHT so we can try to see
3557 # what's going on
3558 import time
3559 if when == 'MIDNIGHT':
3560 try:
3561 if rh.utc:
3562 t = time.gmtime(currentTime)
3563 else:
3564 t = time.localtime(currentTime)
3565 currentHour = t[3]
3566 currentMinute = t[4]
3567 currentSecond = t[5]
3568 # r is the number of seconds left between now and midnight
3569 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3570 currentMinute) * 60 +
3571 currentSecond)
3572 result = currentTime + r
3573 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3574 print('currentHour: %s' % currentHour, file=sys.stderr)
3575 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3576 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3577 print('r: %s' % r, file=sys.stderr)
3578 print('result: %s' % result, file=sys.stderr)
3579 except Exception:
3580 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3581 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003582 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003583 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3584
Vinay Sajip60ccd822011-05-09 17:32:09 +01003585
3586@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3587class NTEventLogHandlerTest(BaseTest):
3588 def test_basic(self):
3589 logtype = 'Application'
3590 elh = win32evtlog.OpenEventLog(None, logtype)
3591 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3592 h = logging.handlers.NTEventLogHandler('test_logging')
3593 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3594 h.handle(r)
3595 h.close()
3596 # Now see if the event is recorded
3597 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3598 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3599 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3600 found = False
3601 GO_BACK = 100
3602 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3603 for e in events:
3604 if e.SourceName != 'test_logging':
3605 continue
3606 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3607 if msg != 'Test Log Message\r\n':
3608 continue
3609 found = True
3610 break
3611 msg = 'Record not found in event log, went back %d records' % GO_BACK
3612 self.assertTrue(found, msg=msg)
3613
Christian Heimes180510d2008-03-03 19:15:45 +00003614# Set the locale to the platform-dependent default. I have no idea
3615# why the test does this, but in any case we save the current locale
3616# first and restore it at the end.
3617@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003618def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003619 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003620 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003621 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3622 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3623 ManagerTest, FormatterTest, BufferingFormatterTest,
3624 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3625 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3626 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3627 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003628 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003629 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003630 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003631 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003632
Christian Heimes180510d2008-03-03 19:15:45 +00003633if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003634 test_main()