blob: 1768e8b36db6e8b8ca10e0b6ada91d2e30e80c73 [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)
1492 self.handled.set()
1493
1494 def test_output(self):
Vinay Sajipf2231162011-05-09 22:05:50 +01001495 # The log message sent to the HTTPHandler is properly received.
Vinay Sajip7367d082011-05-02 13:17:27 +01001496 logger = logging.getLogger("http")
Vinay Sajip0372e102011-05-05 12:59:14 +01001497 for method in ('GET', 'POST'):
1498 self.h_hdlr.method = method
1499 msg = "sp\xe4m"
1500 logger.error(msg)
1501 self.handled.wait()
1502 self.assertEqual(self.log_data.path, '/frob')
1503 self.assertEqual(self.command, method)
1504 if method == 'GET':
1505 d = parse_qs(self.log_data.query)
1506 else:
1507 d = parse_qs(self.post_data.decode('utf-8'))
1508 self.assertEqual(d['name'], ['http'])
1509 self.assertEqual(d['funcName'], ['test_output'])
1510 self.assertEqual(d['msg'], [msg])
Vinay Sajip7367d082011-05-02 13:17:27 +01001511
Christian Heimes180510d2008-03-03 19:15:45 +00001512class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001513
Christian Heimes180510d2008-03-03 19:15:45 +00001514 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001515
Christian Heimes180510d2008-03-03 19:15:45 +00001516 def setUp(self):
1517 """Create a dict to remember potentially destroyed objects."""
1518 BaseTest.setUp(self)
1519 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001520
Christian Heimes180510d2008-03-03 19:15:45 +00001521 def _watch_for_survival(self, *args):
1522 """Watch the given objects for survival, by creating weakrefs to
1523 them."""
1524 for obj in args:
1525 key = id(obj), repr(obj)
1526 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001527
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001528 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001529 """Assert that all objects watched for survival have survived."""
1530 # Trigger cycle breaking.
1531 gc.collect()
1532 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001533 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001534 if ref() is None:
1535 dead.append(repr_)
1536 if dead:
1537 self.fail("%d objects should have survived "
1538 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001539
Christian Heimes180510d2008-03-03 19:15:45 +00001540 def test_persistent_loggers(self):
1541 # Logger objects are persistent and retain their configuration, even
1542 # if visible references are destroyed.
1543 self.root_logger.setLevel(logging.INFO)
1544 foo = logging.getLogger("foo")
1545 self._watch_for_survival(foo)
1546 foo.setLevel(logging.DEBUG)
1547 self.root_logger.debug(self.next_message())
1548 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001549 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001550 ('foo', 'DEBUG', '2'),
1551 ])
1552 del foo
1553 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001554 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001555 # foo has retained its settings.
1556 bar = logging.getLogger("foo")
1557 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001558 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001559 ('foo', 'DEBUG', '2'),
1560 ('foo', 'DEBUG', '3'),
1561 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001562
Benjamin Petersonf91df042009-02-13 02:50:59 +00001563
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001564class EncodingTest(BaseTest):
1565 def test_encoding_plain_file(self):
1566 # In Python 2.x, a plain file object is treated as having no encoding.
1567 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001568 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1569 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001570 # the non-ascii data we write to the log.
1571 data = "foo\x80"
1572 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001573 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001574 log.addHandler(handler)
1575 try:
1576 # write non-ascii data to the log.
1577 log.warning(data)
1578 finally:
1579 log.removeHandler(handler)
1580 handler.close()
1581 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001582 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001583 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001584 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001585 finally:
1586 f.close()
1587 finally:
1588 if os.path.isfile(fn):
1589 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001590
Benjamin Petersonf91df042009-02-13 02:50:59 +00001591 def test_encoding_cyrillic_unicode(self):
1592 log = logging.getLogger("test")
1593 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1594 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1595 #Ensure it's written in a Cyrillic encoding
1596 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001597 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001598 stream = io.BytesIO()
1599 writer = writer_class(stream, 'strict')
1600 handler = logging.StreamHandler(writer)
1601 log.addHandler(handler)
1602 try:
1603 log.warning(message)
1604 finally:
1605 log.removeHandler(handler)
1606 handler.close()
1607 # check we wrote exactly those bytes, ignoring trailing \n etc
1608 s = stream.getvalue()
1609 #Compare against what the data should be when encoded in CP-1251
1610 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1611
1612
Georg Brandlf9734072008-12-07 15:30:06 +00001613class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001614
Georg Brandlf9734072008-12-07 15:30:06 +00001615 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001616 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001617 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001618 self.addCleanup(lambda: logging.captureWarnings(False))
1619 warnings.filterwarnings("always", category=UserWarning)
1620 stream = io.StringIO()
1621 h = logging.StreamHandler(stream)
1622 logger = logging.getLogger("py.warnings")
1623 logger.addHandler(h)
1624 warnings.warn("I'm warning you...")
1625 logger.removeHandler(h)
1626 s = stream.getvalue()
1627 h.close()
1628 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001629
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001630 #See if an explicit file uses the original implementation
1631 a_file = io.StringIO()
1632 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1633 a_file, "Dummy line")
1634 s = a_file.getvalue()
1635 a_file.close()
1636 self.assertEqual(s,
1637 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1638
1639 def test_warnings_no_handlers(self):
1640 with warnings.catch_warnings():
1641 logging.captureWarnings(True)
1642 self.addCleanup(lambda: logging.captureWarnings(False))
1643
1644 # confirm our assumption: no loggers are set
1645 logger = logging.getLogger("py.warnings")
1646 assert logger.handlers == []
1647
1648 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1649 self.assertTrue(len(logger.handlers) == 1)
1650 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001651
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001652
1653def formatFunc(format, datefmt=None):
1654 return logging.Formatter(format, datefmt)
1655
1656def handlerFunc():
1657 return logging.StreamHandler()
1658
1659class CustomHandler(logging.StreamHandler):
1660 pass
1661
1662class ConfigDictTest(BaseTest):
1663
1664 """Reading logging config from a dictionary."""
1665
1666 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1667
1668 # config0 is a standard configuration.
1669 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001670 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001671 'formatters': {
1672 'form1' : {
1673 'format' : '%(levelname)s ++ %(message)s',
1674 },
1675 },
1676 'handlers' : {
1677 'hand1' : {
1678 'class' : 'logging.StreamHandler',
1679 'formatter' : 'form1',
1680 'level' : 'NOTSET',
1681 'stream' : 'ext://sys.stdout',
1682 },
1683 },
1684 'root' : {
1685 'level' : 'WARNING',
1686 'handlers' : ['hand1'],
1687 },
1688 }
1689
1690 # config1 adds a little to the standard configuration.
1691 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001692 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001693 'formatters': {
1694 'form1' : {
1695 'format' : '%(levelname)s ++ %(message)s',
1696 },
1697 },
1698 'handlers' : {
1699 'hand1' : {
1700 'class' : 'logging.StreamHandler',
1701 'formatter' : 'form1',
1702 'level' : 'NOTSET',
1703 'stream' : 'ext://sys.stdout',
1704 },
1705 },
1706 'loggers' : {
1707 'compiler.parser' : {
1708 'level' : 'DEBUG',
1709 'handlers' : ['hand1'],
1710 },
1711 },
1712 'root' : {
1713 'level' : 'WARNING',
1714 },
1715 }
1716
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001717 # config1a moves the handler to the root. Used with config8a
1718 config1a = {
1719 'version': 1,
1720 'formatters': {
1721 'form1' : {
1722 'format' : '%(levelname)s ++ %(message)s',
1723 },
1724 },
1725 'handlers' : {
1726 'hand1' : {
1727 'class' : 'logging.StreamHandler',
1728 'formatter' : 'form1',
1729 'level' : 'NOTSET',
1730 'stream' : 'ext://sys.stdout',
1731 },
1732 },
1733 'loggers' : {
1734 'compiler.parser' : {
1735 'level' : 'DEBUG',
1736 },
1737 },
1738 'root' : {
1739 'level' : 'WARNING',
1740 'handlers' : ['hand1'],
1741 },
1742 }
1743
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001744 # config2 has a subtle configuration error that should be reported
1745 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001746 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001747 'formatters': {
1748 'form1' : {
1749 'format' : '%(levelname)s ++ %(message)s',
1750 },
1751 },
1752 'handlers' : {
1753 'hand1' : {
1754 'class' : 'logging.StreamHandler',
1755 'formatter' : 'form1',
1756 'level' : 'NOTSET',
1757 'stream' : 'ext://sys.stdbout',
1758 },
1759 },
1760 'loggers' : {
1761 'compiler.parser' : {
1762 'level' : 'DEBUG',
1763 'handlers' : ['hand1'],
1764 },
1765 },
1766 'root' : {
1767 'level' : 'WARNING',
1768 },
1769 }
1770
1771 #As config1 but with a misspelt level on a handler
1772 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001773 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001774 'formatters': {
1775 'form1' : {
1776 'format' : '%(levelname)s ++ %(message)s',
1777 },
1778 },
1779 'handlers' : {
1780 'hand1' : {
1781 'class' : 'logging.StreamHandler',
1782 'formatter' : 'form1',
1783 'level' : 'NTOSET',
1784 'stream' : 'ext://sys.stdout',
1785 },
1786 },
1787 'loggers' : {
1788 'compiler.parser' : {
1789 'level' : 'DEBUG',
1790 'handlers' : ['hand1'],
1791 },
1792 },
1793 'root' : {
1794 'level' : 'WARNING',
1795 },
1796 }
1797
1798
1799 #As config1 but with a misspelt level on a logger
1800 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001801 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001802 'formatters': {
1803 'form1' : {
1804 'format' : '%(levelname)s ++ %(message)s',
1805 },
1806 },
1807 'handlers' : {
1808 'hand1' : {
1809 'class' : 'logging.StreamHandler',
1810 'formatter' : 'form1',
1811 'level' : 'NOTSET',
1812 'stream' : 'ext://sys.stdout',
1813 },
1814 },
1815 'loggers' : {
1816 'compiler.parser' : {
1817 'level' : 'DEBUG',
1818 'handlers' : ['hand1'],
1819 },
1820 },
1821 'root' : {
1822 'level' : 'WRANING',
1823 },
1824 }
1825
1826 # config3 has a less subtle configuration error
1827 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001828 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001829 'formatters': {
1830 'form1' : {
1831 'format' : '%(levelname)s ++ %(message)s',
1832 },
1833 },
1834 'handlers' : {
1835 'hand1' : {
1836 'class' : 'logging.StreamHandler',
1837 'formatter' : 'misspelled_name',
1838 'level' : 'NOTSET',
1839 'stream' : 'ext://sys.stdout',
1840 },
1841 },
1842 'loggers' : {
1843 'compiler.parser' : {
1844 'level' : 'DEBUG',
1845 'handlers' : ['hand1'],
1846 },
1847 },
1848 'root' : {
1849 'level' : 'WARNING',
1850 },
1851 }
1852
1853 # config4 specifies a custom formatter class to be loaded
1854 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001855 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001856 'formatters': {
1857 'form1' : {
1858 '()' : __name__ + '.ExceptionFormatter',
1859 'format' : '%(levelname)s:%(name)s:%(message)s',
1860 },
1861 },
1862 'handlers' : {
1863 'hand1' : {
1864 'class' : 'logging.StreamHandler',
1865 'formatter' : 'form1',
1866 'level' : 'NOTSET',
1867 'stream' : 'ext://sys.stdout',
1868 },
1869 },
1870 'root' : {
1871 'level' : 'NOTSET',
1872 'handlers' : ['hand1'],
1873 },
1874 }
1875
1876 # As config4 but using an actual callable rather than a string
1877 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001878 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001879 'formatters': {
1880 'form1' : {
1881 '()' : ExceptionFormatter,
1882 'format' : '%(levelname)s:%(name)s:%(message)s',
1883 },
1884 'form2' : {
1885 '()' : __name__ + '.formatFunc',
1886 'format' : '%(levelname)s:%(name)s:%(message)s',
1887 },
1888 'form3' : {
1889 '()' : formatFunc,
1890 'format' : '%(levelname)s:%(name)s:%(message)s',
1891 },
1892 },
1893 'handlers' : {
1894 'hand1' : {
1895 'class' : 'logging.StreamHandler',
1896 'formatter' : 'form1',
1897 'level' : 'NOTSET',
1898 'stream' : 'ext://sys.stdout',
1899 },
1900 'hand2' : {
1901 '()' : handlerFunc,
1902 },
1903 },
1904 'root' : {
1905 'level' : 'NOTSET',
1906 'handlers' : ['hand1'],
1907 },
1908 }
1909
1910 # config5 specifies a custom handler class to be loaded
1911 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001912 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001913 'formatters': {
1914 'form1' : {
1915 'format' : '%(levelname)s ++ %(message)s',
1916 },
1917 },
1918 'handlers' : {
1919 'hand1' : {
1920 'class' : __name__ + '.CustomHandler',
1921 'formatter' : 'form1',
1922 'level' : 'NOTSET',
1923 'stream' : 'ext://sys.stdout',
1924 },
1925 },
1926 'loggers' : {
1927 'compiler.parser' : {
1928 'level' : 'DEBUG',
1929 'handlers' : ['hand1'],
1930 },
1931 },
1932 'root' : {
1933 'level' : 'WARNING',
1934 },
1935 }
1936
1937 # config6 specifies a custom handler class to be loaded
1938 # but has bad arguments
1939 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001940 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001941 'formatters': {
1942 'form1' : {
1943 'format' : '%(levelname)s ++ %(message)s',
1944 },
1945 },
1946 'handlers' : {
1947 'hand1' : {
1948 'class' : __name__ + '.CustomHandler',
1949 'formatter' : 'form1',
1950 'level' : 'NOTSET',
1951 'stream' : 'ext://sys.stdout',
1952 '9' : 'invalid parameter name',
1953 },
1954 },
1955 'loggers' : {
1956 'compiler.parser' : {
1957 'level' : 'DEBUG',
1958 'handlers' : ['hand1'],
1959 },
1960 },
1961 'root' : {
1962 'level' : 'WARNING',
1963 },
1964 }
1965
1966 #config 7 does not define compiler.parser but defines compiler.lexer
1967 #so compiler.parser should be disabled after applying it
1968 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001969 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001970 'formatters': {
1971 'form1' : {
1972 'format' : '%(levelname)s ++ %(message)s',
1973 },
1974 },
1975 'handlers' : {
1976 'hand1' : {
1977 'class' : 'logging.StreamHandler',
1978 'formatter' : 'form1',
1979 'level' : 'NOTSET',
1980 'stream' : 'ext://sys.stdout',
1981 },
1982 },
1983 'loggers' : {
1984 'compiler.lexer' : {
1985 'level' : 'DEBUG',
1986 'handlers' : ['hand1'],
1987 },
1988 },
1989 'root' : {
1990 'level' : 'WARNING',
1991 },
1992 }
1993
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001994 # config8 defines both compiler and compiler.lexer
1995 # so compiler.parser should not be disabled (since
1996 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001997 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001998 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001999 'disable_existing_loggers' : False,
2000 'formatters': {
2001 'form1' : {
2002 'format' : '%(levelname)s ++ %(message)s',
2003 },
2004 },
2005 'handlers' : {
2006 'hand1' : {
2007 'class' : 'logging.StreamHandler',
2008 'formatter' : 'form1',
2009 'level' : 'NOTSET',
2010 'stream' : 'ext://sys.stdout',
2011 },
2012 },
2013 'loggers' : {
2014 'compiler' : {
2015 'level' : 'DEBUG',
2016 'handlers' : ['hand1'],
2017 },
2018 'compiler.lexer' : {
2019 },
2020 },
2021 'root' : {
2022 'level' : 'WARNING',
2023 },
2024 }
2025
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002026 # config8a disables existing loggers
2027 config8a = {
2028 'version': 1,
2029 'disable_existing_loggers' : True,
2030 'formatters': {
2031 'form1' : {
2032 'format' : '%(levelname)s ++ %(message)s',
2033 },
2034 },
2035 'handlers' : {
2036 'hand1' : {
2037 'class' : 'logging.StreamHandler',
2038 'formatter' : 'form1',
2039 'level' : 'NOTSET',
2040 'stream' : 'ext://sys.stdout',
2041 },
2042 },
2043 'loggers' : {
2044 'compiler' : {
2045 'level' : 'DEBUG',
2046 'handlers' : ['hand1'],
2047 },
2048 'compiler.lexer' : {
2049 },
2050 },
2051 'root' : {
2052 'level' : 'WARNING',
2053 },
2054 }
2055
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002056 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002057 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002058 'formatters': {
2059 'form1' : {
2060 'format' : '%(levelname)s ++ %(message)s',
2061 },
2062 },
2063 'handlers' : {
2064 'hand1' : {
2065 'class' : 'logging.StreamHandler',
2066 'formatter' : 'form1',
2067 'level' : 'WARNING',
2068 'stream' : 'ext://sys.stdout',
2069 },
2070 },
2071 'loggers' : {
2072 'compiler.parser' : {
2073 'level' : 'WARNING',
2074 'handlers' : ['hand1'],
2075 },
2076 },
2077 'root' : {
2078 'level' : 'NOTSET',
2079 },
2080 }
2081
2082 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002083 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002084 'incremental' : True,
2085 'handlers' : {
2086 'hand1' : {
2087 'level' : 'WARNING',
2088 },
2089 },
2090 'loggers' : {
2091 'compiler.parser' : {
2092 'level' : 'INFO',
2093 },
2094 },
2095 }
2096
2097 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002098 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002099 'incremental' : True,
2100 'handlers' : {
2101 'hand1' : {
2102 'level' : 'INFO',
2103 },
2104 },
2105 'loggers' : {
2106 'compiler.parser' : {
2107 'level' : 'INFO',
2108 },
2109 },
2110 }
2111
2112 #As config1 but with a filter added
2113 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002114 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002115 'formatters': {
2116 'form1' : {
2117 'format' : '%(levelname)s ++ %(message)s',
2118 },
2119 },
2120 'filters' : {
2121 'filt1' : {
2122 'name' : 'compiler.parser',
2123 },
2124 },
2125 'handlers' : {
2126 'hand1' : {
2127 'class' : 'logging.StreamHandler',
2128 'formatter' : 'form1',
2129 'level' : 'NOTSET',
2130 'stream' : 'ext://sys.stdout',
2131 'filters' : ['filt1'],
2132 },
2133 },
2134 'loggers' : {
2135 'compiler.parser' : {
2136 'level' : 'DEBUG',
2137 'filters' : ['filt1'],
2138 },
2139 },
2140 'root' : {
2141 'level' : 'WARNING',
2142 'handlers' : ['hand1'],
2143 },
2144 }
2145
2146 #As config1 but using cfg:// references
2147 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002148 'version': 1,
2149 'true_formatters': {
2150 'form1' : {
2151 'format' : '%(levelname)s ++ %(message)s',
2152 },
2153 },
2154 'handler_configs': {
2155 'hand1' : {
2156 'class' : 'logging.StreamHandler',
2157 'formatter' : 'form1',
2158 'level' : 'NOTSET',
2159 'stream' : 'ext://sys.stdout',
2160 },
2161 },
2162 'formatters' : 'cfg://true_formatters',
2163 'handlers' : {
2164 'hand1' : 'cfg://handler_configs[hand1]',
2165 },
2166 'loggers' : {
2167 'compiler.parser' : {
2168 'level' : 'DEBUG',
2169 'handlers' : ['hand1'],
2170 },
2171 },
2172 'root' : {
2173 'level' : 'WARNING',
2174 },
2175 }
2176
2177 #As config11 but missing the version key
2178 config12 = {
2179 'true_formatters': {
2180 'form1' : {
2181 'format' : '%(levelname)s ++ %(message)s',
2182 },
2183 },
2184 'handler_configs': {
2185 'hand1' : {
2186 'class' : 'logging.StreamHandler',
2187 'formatter' : 'form1',
2188 'level' : 'NOTSET',
2189 'stream' : 'ext://sys.stdout',
2190 },
2191 },
2192 'formatters' : 'cfg://true_formatters',
2193 'handlers' : {
2194 'hand1' : 'cfg://handler_configs[hand1]',
2195 },
2196 'loggers' : {
2197 'compiler.parser' : {
2198 'level' : 'DEBUG',
2199 'handlers' : ['hand1'],
2200 },
2201 },
2202 'root' : {
2203 'level' : 'WARNING',
2204 },
2205 }
2206
2207 #As config11 but using an unsupported version
2208 config13 = {
2209 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002210 'true_formatters': {
2211 'form1' : {
2212 'format' : '%(levelname)s ++ %(message)s',
2213 },
2214 },
2215 'handler_configs': {
2216 'hand1' : {
2217 'class' : 'logging.StreamHandler',
2218 'formatter' : 'form1',
2219 'level' : 'NOTSET',
2220 'stream' : 'ext://sys.stdout',
2221 },
2222 },
2223 'formatters' : 'cfg://true_formatters',
2224 'handlers' : {
2225 'hand1' : 'cfg://handler_configs[hand1]',
2226 },
2227 'loggers' : {
2228 'compiler.parser' : {
2229 'level' : 'DEBUG',
2230 'handlers' : ['hand1'],
2231 },
2232 },
2233 'root' : {
2234 'level' : 'WARNING',
2235 },
2236 }
2237
2238 def apply_config(self, conf):
2239 logging.config.dictConfig(conf)
2240
2241 def test_config0_ok(self):
2242 # A simple config which overrides the default settings.
2243 with captured_stdout() as output:
2244 self.apply_config(self.config0)
2245 logger = logging.getLogger()
2246 # Won't output anything
2247 logger.info(self.next_message())
2248 # Outputs a message
2249 logger.error(self.next_message())
2250 self.assert_log_lines([
2251 ('ERROR', '2'),
2252 ], stream=output)
2253 # Original logger output is empty.
2254 self.assert_log_lines([])
2255
2256 def test_config1_ok(self, config=config1):
2257 # A config defining a sub-parser as well.
2258 with captured_stdout() as output:
2259 self.apply_config(config)
2260 logger = logging.getLogger("compiler.parser")
2261 # Both will output a message
2262 logger.info(self.next_message())
2263 logger.error(self.next_message())
2264 self.assert_log_lines([
2265 ('INFO', '1'),
2266 ('ERROR', '2'),
2267 ], stream=output)
2268 # Original logger output is empty.
2269 self.assert_log_lines([])
2270
2271 def test_config2_failure(self):
2272 # A simple config which overrides the default settings.
2273 self.assertRaises(Exception, self.apply_config, self.config2)
2274
2275 def test_config2a_failure(self):
2276 # A simple config which overrides the default settings.
2277 self.assertRaises(Exception, self.apply_config, self.config2a)
2278
2279 def test_config2b_failure(self):
2280 # A simple config which overrides the default settings.
2281 self.assertRaises(Exception, self.apply_config, self.config2b)
2282
2283 def test_config3_failure(self):
2284 # A simple config which overrides the default settings.
2285 self.assertRaises(Exception, self.apply_config, self.config3)
2286
2287 def test_config4_ok(self):
2288 # A config specifying a custom formatter class.
2289 with captured_stdout() as output:
2290 self.apply_config(self.config4)
2291 #logger = logging.getLogger()
2292 try:
2293 raise RuntimeError()
2294 except RuntimeError:
2295 logging.exception("just testing")
2296 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002297 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002298 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2299 # Original logger output is empty
2300 self.assert_log_lines([])
2301
2302 def test_config4a_ok(self):
2303 # A config specifying a custom formatter class.
2304 with captured_stdout() as output:
2305 self.apply_config(self.config4a)
2306 #logger = logging.getLogger()
2307 try:
2308 raise RuntimeError()
2309 except RuntimeError:
2310 logging.exception("just testing")
2311 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002312 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002313 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2314 # Original logger output is empty
2315 self.assert_log_lines([])
2316
2317 def test_config5_ok(self):
2318 self.test_config1_ok(config=self.config5)
2319
2320 def test_config6_failure(self):
2321 self.assertRaises(Exception, self.apply_config, self.config6)
2322
2323 def test_config7_ok(self):
2324 with captured_stdout() as output:
2325 self.apply_config(self.config1)
2326 logger = logging.getLogger("compiler.parser")
2327 # Both will output a message
2328 logger.info(self.next_message())
2329 logger.error(self.next_message())
2330 self.assert_log_lines([
2331 ('INFO', '1'),
2332 ('ERROR', '2'),
2333 ], stream=output)
2334 # Original logger output is empty.
2335 self.assert_log_lines([])
2336 with captured_stdout() as output:
2337 self.apply_config(self.config7)
2338 logger = logging.getLogger("compiler.parser")
2339 self.assertTrue(logger.disabled)
2340 logger = logging.getLogger("compiler.lexer")
2341 # Both will output a message
2342 logger.info(self.next_message())
2343 logger.error(self.next_message())
2344 self.assert_log_lines([
2345 ('INFO', '3'),
2346 ('ERROR', '4'),
2347 ], stream=output)
2348 # Original logger output is empty.
2349 self.assert_log_lines([])
2350
2351 #Same as test_config_7_ok but don't disable old loggers.
2352 def test_config_8_ok(self):
2353 with captured_stdout() as output:
2354 self.apply_config(self.config1)
2355 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002356 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002357 logger.info(self.next_message())
2358 logger.error(self.next_message())
2359 self.assert_log_lines([
2360 ('INFO', '1'),
2361 ('ERROR', '2'),
2362 ], stream=output)
2363 # Original logger output is empty.
2364 self.assert_log_lines([])
2365 with captured_stdout() as output:
2366 self.apply_config(self.config8)
2367 logger = logging.getLogger("compiler.parser")
2368 self.assertFalse(logger.disabled)
2369 # Both will output a message
2370 logger.info(self.next_message())
2371 logger.error(self.next_message())
2372 logger = logging.getLogger("compiler.lexer")
2373 # Both will output a message
2374 logger.info(self.next_message())
2375 logger.error(self.next_message())
2376 self.assert_log_lines([
2377 ('INFO', '3'),
2378 ('ERROR', '4'),
2379 ('INFO', '5'),
2380 ('ERROR', '6'),
2381 ], stream=output)
2382 # Original logger output is empty.
2383 self.assert_log_lines([])
2384
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002385 def test_config_8a_ok(self):
2386 with captured_stdout() as output:
2387 self.apply_config(self.config1a)
2388 logger = logging.getLogger("compiler.parser")
2389 # See issue #11424. compiler-hyphenated sorts
2390 # between compiler and compiler.xyz and this
2391 # was preventing compiler.xyz from being included
2392 # in the child loggers of compiler because of an
2393 # overzealous loop termination condition.
2394 hyphenated = logging.getLogger('compiler-hyphenated')
2395 # All will output a message
2396 logger.info(self.next_message())
2397 logger.error(self.next_message())
2398 hyphenated.critical(self.next_message())
2399 self.assert_log_lines([
2400 ('INFO', '1'),
2401 ('ERROR', '2'),
2402 ('CRITICAL', '3'),
2403 ], stream=output)
2404 # Original logger output is empty.
2405 self.assert_log_lines([])
2406 with captured_stdout() as output:
2407 self.apply_config(self.config8a)
2408 logger = logging.getLogger("compiler.parser")
2409 self.assertFalse(logger.disabled)
2410 # Both will output a message
2411 logger.info(self.next_message())
2412 logger.error(self.next_message())
2413 logger = logging.getLogger("compiler.lexer")
2414 # Both will output a message
2415 logger.info(self.next_message())
2416 logger.error(self.next_message())
2417 # Will not appear
2418 hyphenated.critical(self.next_message())
2419 self.assert_log_lines([
2420 ('INFO', '4'),
2421 ('ERROR', '5'),
2422 ('INFO', '6'),
2423 ('ERROR', '7'),
2424 ], stream=output)
2425 # Original logger output is empty.
2426 self.assert_log_lines([])
2427
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002428 def test_config_9_ok(self):
2429 with captured_stdout() as output:
2430 self.apply_config(self.config9)
2431 logger = logging.getLogger("compiler.parser")
2432 #Nothing will be output since both handler and logger are set to WARNING
2433 logger.info(self.next_message())
2434 self.assert_log_lines([], stream=output)
2435 self.apply_config(self.config9a)
2436 #Nothing will be output since both handler is still set to WARNING
2437 logger.info(self.next_message())
2438 self.assert_log_lines([], stream=output)
2439 self.apply_config(self.config9b)
2440 #Message should now be output
2441 logger.info(self.next_message())
2442 self.assert_log_lines([
2443 ('INFO', '3'),
2444 ], stream=output)
2445
2446 def test_config_10_ok(self):
2447 with captured_stdout() as output:
2448 self.apply_config(self.config10)
2449 logger = logging.getLogger("compiler.parser")
2450 logger.warning(self.next_message())
2451 logger = logging.getLogger('compiler')
2452 #Not output, because filtered
2453 logger.warning(self.next_message())
2454 logger = logging.getLogger('compiler.lexer')
2455 #Not output, because filtered
2456 logger.warning(self.next_message())
2457 logger = logging.getLogger("compiler.parser.codegen")
2458 #Output, as not filtered
2459 logger.error(self.next_message())
2460 self.assert_log_lines([
2461 ('WARNING', '1'),
2462 ('ERROR', '4'),
2463 ], stream=output)
2464
2465 def test_config11_ok(self):
2466 self.test_config1_ok(self.config11)
2467
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002468 def test_config12_failure(self):
2469 self.assertRaises(Exception, self.apply_config, self.config12)
2470
2471 def test_config13_failure(self):
2472 self.assertRaises(Exception, self.apply_config, self.config13)
2473
Victor Stinner45df8202010-04-28 22:31:17 +00002474 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002475 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002476 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002477 # Ask for a randomly assigned port (by using port 0)
2478 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002479 t.start()
2480 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002481 # Now get the port allocated
2482 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002483 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002484 try:
2485 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2486 sock.settimeout(2.0)
2487 sock.connect(('localhost', port))
2488
2489 slen = struct.pack('>L', len(text))
2490 s = slen + text
2491 sentsofar = 0
2492 left = len(s)
2493 while left > 0:
2494 sent = sock.send(s[sentsofar:])
2495 sentsofar += sent
2496 left -= sent
2497 sock.close()
2498 finally:
2499 t.ready.wait(2.0)
2500 logging.config.stopListening()
2501 t.join(2.0)
2502
2503 def test_listen_config_10_ok(self):
2504 with captured_stdout() as output:
2505 self.setup_via_listener(json.dumps(self.config10))
2506 logger = logging.getLogger("compiler.parser")
2507 logger.warning(self.next_message())
2508 logger = logging.getLogger('compiler')
2509 #Not output, because filtered
2510 logger.warning(self.next_message())
2511 logger = logging.getLogger('compiler.lexer')
2512 #Not output, because filtered
2513 logger.warning(self.next_message())
2514 logger = logging.getLogger("compiler.parser.codegen")
2515 #Output, as not filtered
2516 logger.error(self.next_message())
2517 self.assert_log_lines([
2518 ('WARNING', '1'),
2519 ('ERROR', '4'),
2520 ], stream=output)
2521
2522 def test_listen_config_1_ok(self):
2523 with captured_stdout() as output:
2524 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2525 logger = logging.getLogger("compiler.parser")
2526 # Both will output a message
2527 logger.info(self.next_message())
2528 logger.error(self.next_message())
2529 self.assert_log_lines([
2530 ('INFO', '1'),
2531 ('ERROR', '2'),
2532 ], stream=output)
2533 # Original logger output is empty.
2534 self.assert_log_lines([])
2535
Vinay Sajip373baef2011-04-26 20:05:24 +01002536 def test_baseconfig(self):
2537 d = {
2538 'atuple': (1, 2, 3),
2539 'alist': ['a', 'b', 'c'],
2540 'adict': {'d': 'e', 'f': 3 },
2541 'nest1': ('g', ('h', 'i'), 'j'),
2542 'nest2': ['k', ['l', 'm'], 'n'],
2543 'nest3': ['o', 'cfg://alist', 'p'],
2544 }
2545 bc = logging.config.BaseConfigurator(d)
2546 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2547 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2548 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2549 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2550 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2551 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2552 v = bc.convert('cfg://nest3')
2553 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2554 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2555 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2556 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002557
2558class ManagerTest(BaseTest):
2559 def test_manager_loggerclass(self):
2560 logged = []
2561
2562 class MyLogger(logging.Logger):
2563 def _log(self, level, msg, args, exc_info=None, extra=None):
2564 logged.append(msg)
2565
2566 man = logging.Manager(None)
2567 self.assertRaises(TypeError, man.setLoggerClass, int)
2568 man.setLoggerClass(MyLogger)
2569 logger = man.getLogger('test')
2570 logger.warning('should appear in logged')
2571 logging.warning('should not appear in logged')
2572
2573 self.assertEqual(logged, ['should appear in logged'])
2574
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002575 def test_set_log_record_factory(self):
2576 man = logging.Manager(None)
2577 expected = object()
2578 man.setLogRecordFactory(expected)
2579 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002580
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002581class ChildLoggerTest(BaseTest):
2582 def test_child_loggers(self):
2583 r = logging.getLogger()
2584 l1 = logging.getLogger('abc')
2585 l2 = logging.getLogger('def.ghi')
2586 c1 = r.getChild('xyz')
2587 c2 = r.getChild('uvw.xyz')
2588 self.assertTrue(c1 is logging.getLogger('xyz'))
2589 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2590 c1 = l1.getChild('def')
2591 c2 = c1.getChild('ghi')
2592 c3 = l1.getChild('def.ghi')
2593 self.assertTrue(c1 is logging.getLogger('abc.def'))
2594 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2595 self.assertTrue(c2 is c3)
2596
2597
Vinay Sajip6fac8172010-10-19 20:44:14 +00002598class DerivedLogRecord(logging.LogRecord):
2599 pass
2600
Vinay Sajip61561522010-12-03 11:50:38 +00002601class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002602
2603 def setUp(self):
2604 class CheckingFilter(logging.Filter):
2605 def __init__(self, cls):
2606 self.cls = cls
2607
2608 def filter(self, record):
2609 t = type(record)
2610 if t is not self.cls:
2611 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2612 self.cls)
2613 raise TypeError(msg)
2614 return True
2615
2616 BaseTest.setUp(self)
2617 self.filter = CheckingFilter(DerivedLogRecord)
2618 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002619 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002620
2621 def tearDown(self):
2622 self.root_logger.removeFilter(self.filter)
2623 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002624 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002625
2626 def test_logrecord_class(self):
2627 self.assertRaises(TypeError, self.root_logger.warning,
2628 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002629 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002630 self.root_logger.error(self.next_message())
2631 self.assert_log_lines([
2632 ('root', 'ERROR', '2'),
2633 ])
2634
2635
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002636class QueueHandlerTest(BaseTest):
2637 # Do not bother with a logger name group.
2638 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2639
2640 def setUp(self):
2641 BaseTest.setUp(self)
2642 self.queue = queue.Queue(-1)
2643 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2644 self.que_logger = logging.getLogger('que')
2645 self.que_logger.propagate = False
2646 self.que_logger.setLevel(logging.WARNING)
2647 self.que_logger.addHandler(self.que_hdlr)
2648
2649 def tearDown(self):
2650 self.que_hdlr.close()
2651 BaseTest.tearDown(self)
2652
2653 def test_queue_handler(self):
2654 self.que_logger.debug(self.next_message())
2655 self.assertRaises(queue.Empty, self.queue.get_nowait)
2656 self.que_logger.info(self.next_message())
2657 self.assertRaises(queue.Empty, self.queue.get_nowait)
2658 msg = self.next_message()
2659 self.que_logger.warning(msg)
2660 data = self.queue.get_nowait()
2661 self.assertTrue(isinstance(data, logging.LogRecord))
2662 self.assertEqual(data.name, self.que_logger.name)
2663 self.assertEqual((data.msg, data.args), (msg, None))
2664
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002665 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2666 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002667 def test_queue_listener(self):
2668 handler = TestHandler(Matcher())
2669 listener = logging.handlers.QueueListener(self.queue, handler)
2670 listener.start()
2671 try:
2672 self.que_logger.warning(self.next_message())
2673 self.que_logger.error(self.next_message())
2674 self.que_logger.critical(self.next_message())
2675 finally:
2676 listener.stop()
2677 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2678 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2679 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2680
Vinay Sajip37eb3382011-04-26 20:26:41 +01002681ZERO = datetime.timedelta(0)
2682
2683class UTC(datetime.tzinfo):
2684 def utcoffset(self, dt):
2685 return ZERO
2686
2687 dst = utcoffset
2688
2689 def tzname(self, dt):
2690 return 'UTC'
2691
2692utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002693
Vinay Sajipa39c5712010-10-25 13:57:39 +00002694class FormatterTest(unittest.TestCase):
2695 def setUp(self):
2696 self.common = {
2697 'name': 'formatter.test',
2698 'level': logging.DEBUG,
2699 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2700 'lineno': 42,
2701 'exc_info': None,
2702 'func': None,
2703 'msg': 'Message with %d %s',
2704 'args': (2, 'placeholders'),
2705 }
2706 self.variants = {
2707 }
2708
2709 def get_record(self, name=None):
2710 result = dict(self.common)
2711 if name is not None:
2712 result.update(self.variants[name])
2713 return logging.makeLogRecord(result)
2714
2715 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002716 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002717 r = self.get_record()
2718 f = logging.Formatter('${%(message)s}')
2719 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2720 f = logging.Formatter('%(random)s')
2721 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002722 self.assertFalse(f.usesTime())
2723 f = logging.Formatter('%(asctime)s')
2724 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002725 f = logging.Formatter('%(asctime)-15s')
2726 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002727 f = logging.Formatter('asctime')
2728 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002729
2730 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002731 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002732 r = self.get_record()
2733 f = logging.Formatter('$%{message}%$', style='{')
2734 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2735 f = logging.Formatter('{random}', style='{')
2736 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002737 self.assertFalse(f.usesTime())
2738 f = logging.Formatter('{asctime}', style='{')
2739 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002740 f = logging.Formatter('{asctime!s:15}', style='{')
2741 self.assertTrue(f.usesTime())
2742 f = logging.Formatter('{asctime:15}', style='{')
2743 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002744 f = logging.Formatter('asctime', style='{')
2745 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002746
2747 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002748 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002749 r = self.get_record()
2750 f = logging.Formatter('$message', style='$')
2751 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2752 f = logging.Formatter('$$%${message}%$$', style='$')
2753 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2754 f = logging.Formatter('${random}', style='$')
2755 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002756 self.assertFalse(f.usesTime())
2757 f = logging.Formatter('${asctime}', style='$')
2758 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002759 f = logging.Formatter('${asctime', style='$')
2760 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002761 f = logging.Formatter('$asctime', style='$')
2762 self.assertTrue(f.usesTime())
2763 f = logging.Formatter('asctime', style='$')
2764 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002765
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002766 def test_invalid_style(self):
2767 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2768
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002769 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002770 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002771 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002772 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002773 r.msecs = 123
2774 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002775 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002776 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2777 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002778 f.format(r)
2779 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2780
2781class TestBufferingFormatter(logging.BufferingFormatter):
2782 def formatHeader(self, records):
2783 return '[(%d)' % len(records)
2784
2785 def formatFooter(self, records):
2786 return '(%d)]' % len(records)
2787
2788class BufferingFormatterTest(unittest.TestCase):
2789 def setUp(self):
2790 self.records = [
2791 logging.makeLogRecord({'msg': 'one'}),
2792 logging.makeLogRecord({'msg': 'two'}),
2793 ]
2794
2795 def test_default(self):
2796 f = logging.BufferingFormatter()
2797 self.assertEqual('', f.format([]))
2798 self.assertEqual('onetwo', f.format(self.records))
2799
2800 def test_custom(self):
2801 f = TestBufferingFormatter()
2802 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2803 lf = logging.Formatter('<%(message)s>')
2804 f = TestBufferingFormatter(lf)
2805 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002806
2807class ExceptionTest(BaseTest):
2808 def test_formatting(self):
2809 r = self.root_logger
2810 h = RecordingHandler()
2811 r.addHandler(h)
2812 try:
2813 raise RuntimeError('deliberate mistake')
2814 except:
2815 logging.exception('failed', stack_info=True)
2816 r.removeHandler(h)
2817 h.close()
2818 r = h.records[0]
2819 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2820 'call last):\n'))
2821 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2822 'deliberate mistake'))
2823 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2824 'call last):\n'))
2825 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2826 'stack_info=True)'))
2827
2828
Vinay Sajip5a27d402010-12-10 11:42:57 +00002829class LastResortTest(BaseTest):
2830 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002831 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002832 root = self.root_logger
2833 root.removeHandler(self.root_hdlr)
2834 old_stderr = sys.stderr
2835 old_lastresort = logging.lastResort
2836 old_raise_exceptions = logging.raiseExceptions
2837 try:
2838 sys.stderr = sio = io.StringIO()
2839 root.warning('This is your final chance!')
2840 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2841 #No handlers and no last resort, so 'No handlers' message
2842 logging.lastResort = None
2843 sys.stderr = sio = io.StringIO()
2844 root.warning('This is your final chance!')
2845 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2846 # 'No handlers' message only printed once
2847 sys.stderr = sio = io.StringIO()
2848 root.warning('This is your final chance!')
2849 self.assertEqual(sio.getvalue(), '')
2850 root.manager.emittedNoHandlerWarning = False
2851 #If raiseExceptions is False, no message is printed
2852 logging.raiseExceptions = False
2853 sys.stderr = sio = io.StringIO()
2854 root.warning('This is your final chance!')
2855 self.assertEqual(sio.getvalue(), '')
2856 finally:
2857 sys.stderr = old_stderr
2858 root.addHandler(self.root_hdlr)
2859 logging.lastResort = old_lastresort
2860 logging.raiseExceptions = old_raise_exceptions
2861
2862
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002863class FakeHandler:
2864
2865 def __init__(self, identifier, called):
2866 for method in ('acquire', 'flush', 'close', 'release'):
2867 setattr(self, method, self.record_call(identifier, method, called))
2868
2869 def record_call(self, identifier, method_name, called):
2870 def inner():
2871 called.append('{} - {}'.format(identifier, method_name))
2872 return inner
2873
2874
2875class RecordingHandler(logging.NullHandler):
2876
2877 def __init__(self, *args, **kwargs):
2878 super(RecordingHandler, self).__init__(*args, **kwargs)
2879 self.records = []
2880
2881 def handle(self, record):
2882 """Keep track of all the emitted records."""
2883 self.records.append(record)
2884
2885
2886class ShutdownTest(BaseTest):
2887
Vinay Sajip5e66b162011-04-20 15:41:14 +01002888 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002889
2890 def setUp(self):
2891 super(ShutdownTest, self).setUp()
2892 self.called = []
2893
2894 raise_exceptions = logging.raiseExceptions
2895 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2896
2897 def raise_error(self, error):
2898 def inner():
2899 raise error()
2900 return inner
2901
2902 def test_no_failure(self):
2903 # create some fake handlers
2904 handler0 = FakeHandler(0, self.called)
2905 handler1 = FakeHandler(1, self.called)
2906 handler2 = FakeHandler(2, self.called)
2907
2908 # create live weakref to those handlers
2909 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2910
2911 logging.shutdown(handlerList=list(handlers))
2912
2913 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2914 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2915 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2916 self.assertEqual(expected, self.called)
2917
2918 def _test_with_failure_in_method(self, method, error):
2919 handler = FakeHandler(0, self.called)
2920 setattr(handler, method, self.raise_error(error))
2921 handlers = [logging.weakref.ref(handler)]
2922
2923 logging.shutdown(handlerList=list(handlers))
2924
2925 self.assertEqual('0 - release', self.called[-1])
2926
2927 def test_with_ioerror_in_acquire(self):
2928 self._test_with_failure_in_method('acquire', IOError)
2929
2930 def test_with_ioerror_in_flush(self):
2931 self._test_with_failure_in_method('flush', IOError)
2932
2933 def test_with_ioerror_in_close(self):
2934 self._test_with_failure_in_method('close', IOError)
2935
2936 def test_with_valueerror_in_acquire(self):
2937 self._test_with_failure_in_method('acquire', ValueError)
2938
2939 def test_with_valueerror_in_flush(self):
2940 self._test_with_failure_in_method('flush', ValueError)
2941
2942 def test_with_valueerror_in_close(self):
2943 self._test_with_failure_in_method('close', ValueError)
2944
2945 def test_with_other_error_in_acquire_without_raise(self):
2946 logging.raiseExceptions = False
2947 self._test_with_failure_in_method('acquire', IndexError)
2948
2949 def test_with_other_error_in_flush_without_raise(self):
2950 logging.raiseExceptions = False
2951 self._test_with_failure_in_method('flush', IndexError)
2952
2953 def test_with_other_error_in_close_without_raise(self):
2954 logging.raiseExceptions = False
2955 self._test_with_failure_in_method('close', IndexError)
2956
2957 def test_with_other_error_in_acquire_with_raise(self):
2958 logging.raiseExceptions = True
2959 self.assertRaises(IndexError, self._test_with_failure_in_method,
2960 'acquire', IndexError)
2961
2962 def test_with_other_error_in_flush_with_raise(self):
2963 logging.raiseExceptions = True
2964 self.assertRaises(IndexError, self._test_with_failure_in_method,
2965 'flush', IndexError)
2966
2967 def test_with_other_error_in_close_with_raise(self):
2968 logging.raiseExceptions = True
2969 self.assertRaises(IndexError, self._test_with_failure_in_method,
2970 'close', IndexError)
2971
2972
2973class ModuleLevelMiscTest(BaseTest):
2974
Vinay Sajip5e66b162011-04-20 15:41:14 +01002975 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002976
2977 def test_disable(self):
2978 old_disable = logging.root.manager.disable
2979 # confirm our assumptions are correct
2980 assert old_disable == 0
2981 self.addCleanup(lambda: logging.disable(old_disable))
2982
2983 logging.disable(83)
2984 self.assertEqual(logging.root.manager.disable, 83)
2985
2986 def _test_log(self, method, level=None):
2987 called = []
2988 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002989 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002990
2991 recording = RecordingHandler()
2992 logging.root.addHandler(recording)
2993
2994 log_method = getattr(logging, method)
2995 if level is not None:
2996 log_method(level, "test me: %r", recording)
2997 else:
2998 log_method("test me: %r", recording)
2999
3000 self.assertEqual(len(recording.records), 1)
3001 record = recording.records[0]
3002 self.assertEqual(record.getMessage(), "test me: %r" % recording)
3003
3004 expected_level = level if level is not None else getattr(logging, method.upper())
3005 self.assertEqual(record.levelno, expected_level)
3006
3007 # basicConfig was not called!
3008 self.assertEqual(called, [])
3009
3010 def test_log(self):
3011 self._test_log('log', logging.ERROR)
3012
3013 def test_debug(self):
3014 self._test_log('debug')
3015
3016 def test_info(self):
3017 self._test_log('info')
3018
3019 def test_warning(self):
3020 self._test_log('warning')
3021
3022 def test_error(self):
3023 self._test_log('error')
3024
3025 def test_critical(self):
3026 self._test_log('critical')
3027
3028 def test_set_logger_class(self):
3029 self.assertRaises(TypeError, logging.setLoggerClass, object)
3030
3031 class MyLogger(logging.Logger):
3032 pass
3033
3034 logging.setLoggerClass(MyLogger)
3035 self.assertEqual(logging.getLoggerClass(), MyLogger)
3036
3037 logging.setLoggerClass(logging.Logger)
3038 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3039
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003040class LogRecordTest(BaseTest):
3041 def test_str_rep(self):
3042 r = logging.makeLogRecord({})
3043 s = str(r)
3044 self.assertTrue(s.startswith('<LogRecord: '))
3045 self.assertTrue(s.endswith('>'))
3046
3047 def test_dict_arg(self):
3048 h = RecordingHandler()
3049 r = logging.getLogger()
3050 r.addHandler(h)
3051 d = {'less' : 'more' }
3052 logging.warning('less is %(less)s', d)
3053 self.assertIs(h.records[0].args, d)
3054 self.assertEqual(h.records[0].message, 'less is more')
3055 r.removeHandler(h)
3056 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003057
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003058 def test_multiprocessing(self):
3059 r = logging.makeLogRecord({})
3060 self.assertEqual(r.processName, 'MainProcess')
3061 import multiprocessing as mp
3062 r = logging.makeLogRecord({})
3063 self.assertEqual(r.processName, mp.current_process().name)
3064
3065 def test_optional(self):
3066 r = logging.makeLogRecord({})
3067 NOT_NONE = self.assertIsNotNone
3068 NOT_NONE(r.thread)
3069 NOT_NONE(r.threadName)
3070 NOT_NONE(r.process)
3071 NOT_NONE(r.processName)
3072 log_threads = logging.logThreads
3073 log_processes = logging.logProcesses
3074 log_multiprocessing = logging.logMultiprocessing
3075 try:
3076 logging.logThreads = False
3077 logging.logProcesses = False
3078 logging.logMultiprocessing = False
3079 r = logging.makeLogRecord({})
3080 NONE = self.assertIsNone
3081 NONE(r.thread)
3082 NONE(r.threadName)
3083 NONE(r.process)
3084 NONE(r.processName)
3085 finally:
3086 logging.logThreads = log_threads
3087 logging.logProcesses = log_processes
3088 logging.logMultiprocessing = log_multiprocessing
3089
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003090class BasicConfigTest(unittest.TestCase):
3091
Vinay Sajip95bf5042011-04-20 11:50:56 +01003092 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003093
3094 def setUp(self):
3095 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003096 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003097 self.saved_handlers = logging._handlers.copy()
3098 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003099 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003100 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003101 logging.root.handlers = []
3102
3103 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003104 for h in logging.root.handlers[:]:
3105 logging.root.removeHandler(h)
3106 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003107 super(BasicConfigTest, self).tearDown()
3108
Vinay Sajip3def7e02011-04-20 10:58:06 +01003109 def cleanup(self):
3110 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003111 logging._handlers.clear()
3112 logging._handlers.update(self.saved_handlers)
3113 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003114 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003115
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003116 def test_no_kwargs(self):
3117 logging.basicConfig()
3118
3119 # handler defaults to a StreamHandler to sys.stderr
3120 self.assertEqual(len(logging.root.handlers), 1)
3121 handler = logging.root.handlers[0]
3122 self.assertIsInstance(handler, logging.StreamHandler)
3123 self.assertEqual(handler.stream, sys.stderr)
3124
3125 formatter = handler.formatter
3126 # format defaults to logging.BASIC_FORMAT
3127 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3128 # datefmt defaults to None
3129 self.assertIsNone(formatter.datefmt)
3130 # style defaults to %
3131 self.assertIsInstance(formatter._style, logging.PercentStyle)
3132
3133 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003134 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003135
3136 def test_filename(self):
3137 logging.basicConfig(filename='test.log')
3138
3139 self.assertEqual(len(logging.root.handlers), 1)
3140 handler = logging.root.handlers[0]
3141 self.assertIsInstance(handler, logging.FileHandler)
3142
3143 expected = logging.FileHandler('test.log', 'a')
3144 self.addCleanup(expected.close)
3145 self.assertEqual(handler.stream.mode, expected.stream.mode)
3146 self.assertEqual(handler.stream.name, expected.stream.name)
3147
3148 def test_filemode(self):
3149 logging.basicConfig(filename='test.log', filemode='wb')
3150
3151 handler = logging.root.handlers[0]
3152 expected = logging.FileHandler('test.log', 'wb')
3153 self.addCleanup(expected.close)
3154 self.assertEqual(handler.stream.mode, expected.stream.mode)
3155
3156 def test_stream(self):
3157 stream = io.StringIO()
3158 self.addCleanup(stream.close)
3159 logging.basicConfig(stream=stream)
3160
3161 self.assertEqual(len(logging.root.handlers), 1)
3162 handler = logging.root.handlers[0]
3163 self.assertIsInstance(handler, logging.StreamHandler)
3164 self.assertEqual(handler.stream, stream)
3165
3166 def test_format(self):
3167 logging.basicConfig(format='foo')
3168
3169 formatter = logging.root.handlers[0].formatter
3170 self.assertEqual(formatter._style._fmt, 'foo')
3171
3172 def test_datefmt(self):
3173 logging.basicConfig(datefmt='bar')
3174
3175 formatter = logging.root.handlers[0].formatter
3176 self.assertEqual(formatter.datefmt, 'bar')
3177
3178 def test_style(self):
3179 logging.basicConfig(style='$')
3180
3181 formatter = logging.root.handlers[0].formatter
3182 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3183
3184 def test_level(self):
3185 old_level = logging.root.level
3186 self.addCleanup(lambda: logging.root.setLevel(old_level))
3187
3188 logging.basicConfig(level=57)
3189 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003190 # Test that second call has no effect
3191 logging.basicConfig(level=58)
3192 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003193
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003194 def test_incompatible(self):
3195 assertRaises = self.assertRaises
3196 handlers = [logging.StreamHandler()]
3197 stream = sys.stderr
3198 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3199 stream=stream)
3200 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3201 handlers=handlers)
3202 assertRaises(ValueError, logging.basicConfig, stream=stream,
3203 handlers=handlers)
3204
3205 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003206 handlers = [
3207 logging.StreamHandler(),
3208 logging.StreamHandler(sys.stdout),
3209 logging.StreamHandler(),
3210 ]
3211 f = logging.Formatter()
3212 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003213 logging.basicConfig(handlers=handlers)
3214 self.assertIs(handlers[0], logging.root.handlers[0])
3215 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003216 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003217 self.assertIsNotNone(handlers[0].formatter)
3218 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003219 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003220 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3221
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003222 def _test_log(self, method, level=None):
3223 # logging.root has no handlers so basicConfig should be called
3224 called = []
3225
3226 old_basic_config = logging.basicConfig
3227 def my_basic_config(*a, **kw):
3228 old_basic_config()
3229 old_level = logging.root.level
3230 logging.root.setLevel(100) # avoid having messages in stderr
3231 self.addCleanup(lambda: logging.root.setLevel(old_level))
3232 called.append((a, kw))
3233
3234 patch(self, logging, 'basicConfig', my_basic_config)
3235
3236 log_method = getattr(logging, method)
3237 if level is not None:
3238 log_method(level, "test me")
3239 else:
3240 log_method("test me")
3241
3242 # basicConfig was called with no arguments
3243 self.assertEqual(called, [((), {})])
3244
3245 def test_log(self):
3246 self._test_log('log', logging.WARNING)
3247
3248 def test_debug(self):
3249 self._test_log('debug')
3250
3251 def test_info(self):
3252 self._test_log('info')
3253
3254 def test_warning(self):
3255 self._test_log('warning')
3256
3257 def test_error(self):
3258 self._test_log('error')
3259
3260 def test_critical(self):
3261 self._test_log('critical')
3262
3263
3264class LoggerAdapterTest(unittest.TestCase):
3265
3266 def setUp(self):
3267 super(LoggerAdapterTest, self).setUp()
3268 old_handler_list = logging._handlerList[:]
3269
3270 self.recording = RecordingHandler()
3271 self.logger = logging.root
3272 self.logger.addHandler(self.recording)
3273 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3274 self.addCleanup(self.recording.close)
3275
3276 def cleanup():
3277 logging._handlerList[:] = old_handler_list
3278
3279 self.addCleanup(cleanup)
3280 self.addCleanup(logging.shutdown)
3281 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3282
3283 def test_exception(self):
3284 msg = 'testing exception: %r'
3285 exc = None
3286 try:
3287 assert False
3288 except AssertionError as e:
3289 exc = e
3290 self.adapter.exception(msg, self.recording)
3291
3292 self.assertEqual(len(self.recording.records), 1)
3293 record = self.recording.records[0]
3294 self.assertEqual(record.levelno, logging.ERROR)
3295 self.assertEqual(record.msg, msg)
3296 self.assertEqual(record.args, (self.recording,))
3297 self.assertEqual(record.exc_info,
3298 (exc.__class__, exc, exc.__traceback__))
3299
3300 def test_critical(self):
3301 msg = 'critical test! %r'
3302 self.adapter.critical(msg, self.recording)
3303
3304 self.assertEqual(len(self.recording.records), 1)
3305 record = self.recording.records[0]
3306 self.assertEqual(record.levelno, logging.CRITICAL)
3307 self.assertEqual(record.msg, msg)
3308 self.assertEqual(record.args, (self.recording,))
3309
3310 def test_is_enabled_for(self):
3311 old_disable = self.adapter.logger.manager.disable
3312 self.adapter.logger.manager.disable = 33
3313 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3314 'disable', old_disable))
3315 self.assertFalse(self.adapter.isEnabledFor(32))
3316
3317 def test_has_handlers(self):
3318 self.assertTrue(self.adapter.hasHandlers())
3319
3320 for handler in self.logger.handlers:
3321 self.logger.removeHandler(handler)
3322 assert not self.logger.hasHandlers()
3323
3324 self.assertFalse(self.adapter.hasHandlers())
3325
3326
3327class LoggerTest(BaseTest):
3328
3329 def setUp(self):
3330 super(LoggerTest, self).setUp()
3331 self.recording = RecordingHandler()
3332 self.logger = logging.Logger(name='blah')
3333 self.logger.addHandler(self.recording)
3334 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3335 self.addCleanup(self.recording.close)
3336 self.addCleanup(logging.shutdown)
3337
3338 def test_set_invalid_level(self):
3339 self.assertRaises(TypeError, self.logger.setLevel, object())
3340
3341 def test_exception(self):
3342 msg = 'testing exception: %r'
3343 exc = None
3344 try:
3345 assert False
3346 except AssertionError as e:
3347 exc = e
3348 self.logger.exception(msg, self.recording)
3349
3350 self.assertEqual(len(self.recording.records), 1)
3351 record = self.recording.records[0]
3352 self.assertEqual(record.levelno, logging.ERROR)
3353 self.assertEqual(record.msg, msg)
3354 self.assertEqual(record.args, (self.recording,))
3355 self.assertEqual(record.exc_info,
3356 (exc.__class__, exc, exc.__traceback__))
3357
3358 def test_log_invalid_level_with_raise(self):
3359 old_raise = logging.raiseExceptions
3360 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3361
3362 logging.raiseExceptions = True
3363 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3364
3365 def test_log_invalid_level_no_raise(self):
3366 old_raise = logging.raiseExceptions
3367 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3368
3369 logging.raiseExceptions = False
3370 self.logger.log('10', 'test message') # no exception happens
3371
3372 def test_find_caller_with_stack_info(self):
3373 called = []
3374 patch(self, logging.traceback, 'print_stack',
3375 lambda f, file: called.append(file.getvalue()))
3376
3377 self.logger.findCaller(stack_info=True)
3378
3379 self.assertEqual(len(called), 1)
3380 self.assertEqual('Stack (most recent call last):\n', called[0])
3381
3382 def test_make_record_with_extra_overwrite(self):
3383 name = 'my record'
3384 level = 13
3385 fn = lno = msg = args = exc_info = func = sinfo = None
3386 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3387 exc_info, func, sinfo)
3388
3389 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3390 extra = {key: 'some value'}
3391 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3392 fn, lno, msg, args, exc_info,
3393 extra=extra, sinfo=sinfo)
3394
3395 def test_make_record_with_extra_no_overwrite(self):
3396 name = 'my record'
3397 level = 13
3398 fn = lno = msg = args = exc_info = func = sinfo = None
3399 extra = {'valid_key': 'some value'}
3400 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3401 exc_info, extra=extra, sinfo=sinfo)
3402 self.assertIn('valid_key', result.__dict__)
3403
3404 def test_has_handlers(self):
3405 self.assertTrue(self.logger.hasHandlers())
3406
3407 for handler in self.logger.handlers:
3408 self.logger.removeHandler(handler)
3409 assert not self.logger.hasHandlers()
3410
3411 self.assertFalse(self.logger.hasHandlers())
3412
3413 def test_has_handlers_no_propagate(self):
3414 child_logger = logging.getLogger('blah.child')
3415 child_logger.propagate = False
3416 assert child_logger.handlers == []
3417
3418 self.assertFalse(child_logger.hasHandlers())
3419
3420 def test_is_enabled_for(self):
3421 old_disable = self.logger.manager.disable
3422 self.logger.manager.disable = 23
3423 self.addCleanup(lambda: setattr(self.logger.manager,
3424 'disable', old_disable))
3425 self.assertFalse(self.logger.isEnabledFor(22))
3426
3427
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003428class BaseFileTest(BaseTest):
3429 "Base class for handler tests that write log files"
3430
3431 def setUp(self):
3432 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003433 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3434 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003435 self.rmfiles = []
3436
3437 def tearDown(self):
3438 for fn in self.rmfiles:
3439 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003440 if os.path.exists(self.fn):
3441 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003442 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003443
3444 def assertLogFile(self, filename):
3445 "Assert a log file is there and register it for deletion"
3446 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003447 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003448 self.rmfiles.append(filename)
3449
3450
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003451class FileHandlerTest(BaseFileTest):
3452 def test_delay(self):
3453 os.unlink(self.fn)
3454 fh = logging.FileHandler(self.fn, delay=True)
3455 self.assertIsNone(fh.stream)
3456 self.assertFalse(os.path.exists(self.fn))
3457 fh.handle(logging.makeLogRecord({}))
3458 self.assertIsNotNone(fh.stream)
3459 self.assertTrue(os.path.exists(self.fn))
3460 fh.close()
3461
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003462class RotatingFileHandlerTest(BaseFileTest):
3463 def next_rec(self):
3464 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3465 self.next_message(), None, None, None)
3466
3467 def test_should_not_rollover(self):
3468 # If maxbytes is zero rollover never occurs
3469 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3470 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003471 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003472
3473 def test_should_rollover(self):
3474 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3475 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003476 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003477
3478 def test_file_created(self):
3479 # checks that the file is created and assumes it was created
3480 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003481 rh = logging.handlers.RotatingFileHandler(self.fn)
3482 rh.emit(self.next_rec())
3483 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003484 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003485
3486 def test_rollover_filenames(self):
3487 rh = logging.handlers.RotatingFileHandler(
3488 self.fn, backupCount=2, maxBytes=1)
3489 rh.emit(self.next_rec())
3490 self.assertLogFile(self.fn)
3491 rh.emit(self.next_rec())
3492 self.assertLogFile(self.fn + ".1")
3493 rh.emit(self.next_rec())
3494 self.assertLogFile(self.fn + ".2")
3495 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003496 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003497
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003498class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003499 # other test methods added below
3500 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003501 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3502 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003503 r = logging.makeLogRecord({'msg': 'testing'})
3504 fh.emit(r)
3505 self.assertLogFile(self.fn)
3506 time.sleep(1.0)
3507 fh.emit(r)
3508 fh.close()
3509 # At this point, we should have a recent rotated file which we
3510 # can test for the existence of. However, in practice, on some
3511 # machines which run really slowly, we don't know how far back
3512 # in time to go to look for the log file. So, we go back a fair
3513 # bit, and stop as soon as we see a rotated file. In theory this
3514 # could of course still fail, but the chances are lower.
3515 found = False
3516 now = datetime.datetime.now()
3517 GO_BACK = 2 * 60 # seconds
3518 for secs in range(1, GO_BACK):
3519 prev = now - datetime.timedelta(seconds=secs)
3520 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3521 found = os.path.exists(fn)
3522 if found:
3523 self.rmfiles.append(fn)
3524 break
3525 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3526 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003527
Vinay Sajip0372e102011-05-05 12:59:14 +01003528 def test_invalid(self):
3529 assertRaises = self.assertRaises
3530 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003531 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003532 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003533 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003534 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003535 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003536
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003537def secs(**kw):
3538 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3539
3540for when, exp in (('S', 1),
3541 ('M', 60),
3542 ('H', 60 * 60),
3543 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003544 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003545 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003546 ('W0', secs(days=4, hours=24)),
3547 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003548 def test_compute_rollover(self, when=when, exp=exp):
3549 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003550 self.fn, when=when, interval=1, backupCount=0, utc=True)
3551 currentTime = 0.0
3552 actual = rh.computeRollover(currentTime)
3553 if exp != actual:
3554 # Failures occur on some systems for MIDNIGHT and W0.
3555 # Print detailed calculation for MIDNIGHT so we can try to see
3556 # what's going on
3557 import time
3558 if when == 'MIDNIGHT':
3559 try:
3560 if rh.utc:
3561 t = time.gmtime(currentTime)
3562 else:
3563 t = time.localtime(currentTime)
3564 currentHour = t[3]
3565 currentMinute = t[4]
3566 currentSecond = t[5]
3567 # r is the number of seconds left between now and midnight
3568 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3569 currentMinute) * 60 +
3570 currentSecond)
3571 result = currentTime + r
3572 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3573 print('currentHour: %s' % currentHour, file=sys.stderr)
3574 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3575 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3576 print('r: %s' % r, file=sys.stderr)
3577 print('result: %s' % result, file=sys.stderr)
3578 except Exception:
3579 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3580 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003581 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003582 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3583
Vinay Sajip60ccd822011-05-09 17:32:09 +01003584
3585@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3586class NTEventLogHandlerTest(BaseTest):
3587 def test_basic(self):
3588 logtype = 'Application'
3589 elh = win32evtlog.OpenEventLog(None, logtype)
3590 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3591 h = logging.handlers.NTEventLogHandler('test_logging')
3592 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3593 h.handle(r)
3594 h.close()
3595 # Now see if the event is recorded
3596 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3597 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3598 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3599 found = False
3600 GO_BACK = 100
3601 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3602 for e in events:
3603 if e.SourceName != 'test_logging':
3604 continue
3605 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3606 if msg != 'Test Log Message\r\n':
3607 continue
3608 found = True
3609 break
3610 msg = 'Record not found in event log, went back %d records' % GO_BACK
3611 self.assertTrue(found, msg=msg)
3612
Christian Heimes180510d2008-03-03 19:15:45 +00003613# Set the locale to the platform-dependent default. I have no idea
3614# why the test does this, but in any case we save the current locale
3615# first and restore it at the end.
3616@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003617def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003618 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003619 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003620 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3621 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3622 ManagerTest, FormatterTest, BufferingFormatterTest,
3623 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3624 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3625 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3626 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003627 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003628 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003629 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003630 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003631
Christian Heimes180510d2008-03-03 19:15:45 +00003632if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003633 test_main()