blob: 44ab19f43358a3f9259e7b781bad78dcbf0db27f [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 Sajipa463d252011-04-30 21:52:48 +0100726 asyncore.loop(poll_interval, map=self.sockmap)
727
Vinay Sajip7367d082011-05-02 13:17:27 +0100728 def stop(self, timeout=None):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100729 """
730 Stop the thread by closing the server instance.
731 Wait for the server thread to terminate.
732
733 :param timeout: How long to wait for the server thread
734 to terminate.
735 """
Vinay Sajipa463d252011-04-30 21:52:48 +0100736 self.close()
Vinay Sajip7367d082011-05-02 13:17:27 +0100737 self._thread.join(timeout)
Vinay Sajipa463d252011-04-30 21:52:48 +0100738 self._thread = None
739
Vinay Sajip7367d082011-05-02 13:17:27 +0100740class ControlMixin(object):
741 """
742 This mixin is used to start a server on a separate thread, and
743 shut it down programmatically. Request handling is simplified - instead
744 of needing to derive a suitable RequestHandler subclass, you just
745 provide a callable which will be passed each received request to be
746 processed.
747
748 :param handler: A handler callable which will be called with a
749 single parameter - the request - in order to
750 process the request. This handler is called on the
751 server thread, effectively meaning that requests are
752 processed serially. While not quite Web scale ;-),
753 this should be fine for testing applications.
754 :param poll_interval: The polling interval in seconds.
755 """
756 def __init__(self, handler, poll_interval):
757 self._thread = None
758 self.poll_interval = poll_interval
759 self._handler = handler
760 self.ready = threading.Event()
761
762 def start(self):
763 """
764 Create a daemon thread to run the server, and start it.
765 """
766 self._thread = t = threading.Thread(target=self.serve_forever,
767 args=(self.poll_interval,))
768 t.setDaemon(True)
769 t.start()
770
771 def serve_forever(self, poll_interval):
Vinay Sajip314b92b2011-05-02 14:31:16 +0100772 """
773 Run the server. Set the ready flag before entering the
774 service loop.
775 """
Vinay Sajip7367d082011-05-02 13:17:27 +0100776 self.ready.set()
777 super(ControlMixin, self).serve_forever(poll_interval)
778
779 def stop(self, timeout=None):
780 """
781 Tell the server thread to stop, and wait for it to do so.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100782
783 :param timeout: How long to wait for the server thread
784 to terminate.
Vinay Sajip7367d082011-05-02 13:17:27 +0100785 """
786 self.shutdown()
787 if self._thread is not None:
788 self._thread.join(timeout)
789 self._thread = None
790 self.server_close()
791 self.ready.clear()
792
793class TestHTTPServer(ControlMixin, HTTPServer):
794 """
795 An HTTP server which is controllable using :class:`ControlMixin`.
796
797 :param addr: A tuple with the IP address and port to listen on.
798 :param handler: A handler callable which will be called with a
799 single parameter - the request - in order to
800 process the request.
801 :param poll_interval: The polling interval in seconds.
Vinay Sajip314b92b2011-05-02 14:31:16 +0100802 :param log: Pass ``True`` to enable log messages.
Vinay Sajip7367d082011-05-02 13:17:27 +0100803 """
804 def __init__(self, addr, handler, poll_interval=0.5, log=False):
805 class DelegatingHTTPRequestHandler(BaseHTTPRequestHandler):
806 def __getattr__(self, name, default=None):
807 if name.startswith('do_'):
808 return self.process_request
809 raise AttributeError(name)
810
811 def process_request(self):
812 self.server._handler(self)
813
814 def log_message(self, format, *args):
815 if log:
816 super(DelegatingHTTPRequestHandler,
817 self).log_message(format, *args)
818 HTTPServer.__init__(self, addr, DelegatingHTTPRequestHandler)
819 ControlMixin.__init__(self, handler, poll_interval)
820
821class TestTCPServer(ControlMixin, ThreadingTCPServer):
822 """
823 A TCP server which is controllable using :class:`ControlMixin`.
824
825 :param addr: A tuple with the IP address and port to listen on.
826 :param handler: A handler callable which will be called with a single
827 parameter - the request - in order to process the request.
828 :param poll_interval: The polling interval in seconds.
829 :bind_and_activate: If True (the default), binds the server and starts it
830 listening. If False, you need to call
831 :meth:`server_bind` and :meth:`server_activate` at
832 some later time before calling :meth:`start`, so that
833 the server will set up the socket and listen on it.
834 """
835
836 allow_reuse_address = True
837
838 def __init__(self, addr, handler, poll_interval=0.5,
839 bind_and_activate=True):
840 class DelegatingTCPRequestHandler(StreamRequestHandler):
841
842 def handle(self):
843 self.server._handler(self)
844 ThreadingTCPServer.__init__(self, addr, DelegatingTCPRequestHandler,
845 bind_and_activate)
846 ControlMixin.__init__(self, handler, poll_interval)
847
848 def server_bind(self):
849 super(TestTCPServer, self).server_bind()
850 self.port = self.socket.getsockname()[1]
851
852class TestUDPServer(ControlMixin, ThreadingUDPServer):
853 """
854 A UDP server which is controllable using :class:`ControlMixin`.
855
856 :param addr: A tuple with the IP address and port to listen on.
857 :param handler: A handler callable which will be called with a
858 single parameter - the request - in order to
859 process the request.
860 :param poll_interval: The polling interval for shutdown requests,
861 in seconds.
862 :bind_and_activate: If True (the default), binds the server and
863 starts it listening. If False, you need to
864 call :meth:`server_bind` and
865 :meth:`server_activate` at some later time
866 before calling :meth:`start`, so that the server will
867 set up the socket and listen on it.
868 """
869 def __init__(self, addr, handler, poll_interval=0.5, bind_and_activate=True):
870 class DelegatingUDPRequestHandler(DatagramRequestHandler):
871
872 def handle(self):
873 self.server._handler(self)
874 ThreadingUDPServer.__init__(self, addr, DelegatingUDPRequestHandler,
875 bind_and_activate)
876 ControlMixin.__init__(self, handler, poll_interval)
877
878 def server_bind(self):
879 super(TestUDPServer, self).server_bind()
880 self.port = self.socket.getsockname()[1]
881
882
883# - end of server_helper section
Vinay Sajipa463d252011-04-30 21:52:48 +0100884
885class SMTPHandlerTest(BaseTest):
886 def test_basic(self):
Vinay Sajip7367d082011-05-02 13:17:27 +0100887 sockmap = {}
888 server = TestSMTPServer(('localhost', 0), self.process_message, 0.001,
889 sockmap)
890 server.start()
891 addr = ('localhost', server.port)
Vinay Sajipa463d252011-04-30 21:52:48 +0100892 h = logging.handlers.SMTPHandler(addr, 'me', 'you', 'Log')
893 self.assertEqual(h.toaddrs, ['you'])
894 self.messages = []
Vinay Sajipa463d252011-04-30 21:52:48 +0100895 r = logging.makeLogRecord({'msg': 'Hello'})
Vinay Sajip7367d082011-05-02 13:17:27 +0100896 self.handled = threading.Event()
Vinay Sajipa463d252011-04-30 21:52:48 +0100897 h.handle(r)
Vinay Sajip7367d082011-05-02 13:17:27 +0100898 self.handled.wait()
Vinay Sajipa463d252011-04-30 21:52:48 +0100899 server.stop()
900 self.assertEqual(len(self.messages), 1)
901 peer, mailfrom, rcpttos, data = self.messages[0]
902 self.assertEqual(mailfrom, 'me')
903 self.assertEqual(rcpttos, ['you'])
904 self.assertTrue('\nSubject: Log\n' in data)
905 self.assertTrue(data.endswith('\n\nHello'))
906 h.close()
907
908 def process_message(self, *args):
909 self.messages.append(args)
Vinay Sajip7367d082011-05-02 13:17:27 +0100910 self.handled.set()
Vinay Sajipa463d252011-04-30 21:52:48 +0100911
Christian Heimes180510d2008-03-03 19:15:45 +0000912class MemoryHandlerTest(BaseTest):
913
914 """Tests for the MemoryHandler."""
915
916 # Do not bother with a logger name group.
917 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
918
919 def setUp(self):
920 BaseTest.setUp(self)
921 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
922 self.root_hdlr)
923 self.mem_logger = logging.getLogger('mem')
924 self.mem_logger.propagate = 0
925 self.mem_logger.addHandler(self.mem_hdlr)
926
927 def tearDown(self):
928 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000929 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000930
931 def test_flush(self):
932 # The memory handler flushes to its target handler based on specific
933 # criteria (message count and message level).
934 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000935 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000936 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000937 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000938 # This will flush because the level is >= logging.WARNING
939 self.mem_logger.warn(self.next_message())
940 lines = [
941 ('DEBUG', '1'),
942 ('INFO', '2'),
943 ('WARNING', '3'),
944 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000945 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000946 for n in (4, 14):
947 for i in range(9):
948 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000949 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000950 # This will flush because it's the 10th message since the last
951 # flush.
952 self.mem_logger.debug(self.next_message())
953 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000954 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000955
956 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000957 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000958
959
960class ExceptionFormatter(logging.Formatter):
961 """A special exception formatter."""
962 def formatException(self, ei):
963 return "Got a [%s]" % ei[0].__name__
964
965
966class ConfigFileTest(BaseTest):
967
968 """Reading logging config from a .ini-style config file."""
969
970 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
971
972 # config0 is a standard configuration.
973 config0 = """
974 [loggers]
975 keys=root
976
977 [handlers]
978 keys=hand1
979
980 [formatters]
981 keys=form1
982
983 [logger_root]
984 level=WARNING
985 handlers=hand1
986
987 [handler_hand1]
988 class=StreamHandler
989 level=NOTSET
990 formatter=form1
991 args=(sys.stdout,)
992
993 [formatter_form1]
994 format=%(levelname)s ++ %(message)s
995 datefmt=
996 """
997
998 # config1 adds a little to the standard configuration.
999 config1 = """
1000 [loggers]
1001 keys=root,parser
1002
1003 [handlers]
1004 keys=hand1
1005
1006 [formatters]
1007 keys=form1
1008
1009 [logger_root]
1010 level=WARNING
1011 handlers=
1012
1013 [logger_parser]
1014 level=DEBUG
1015 handlers=hand1
1016 propagate=1
1017 qualname=compiler.parser
1018
1019 [handler_hand1]
1020 class=StreamHandler
1021 level=NOTSET
1022 formatter=form1
1023 args=(sys.stdout,)
1024
1025 [formatter_form1]
1026 format=%(levelname)s ++ %(message)s
1027 datefmt=
1028 """
1029
Vinay Sajip3f84b072011-03-07 17:49:33 +00001030 # config1a moves the handler to the root.
1031 config1a = """
1032 [loggers]
1033 keys=root,parser
1034
1035 [handlers]
1036 keys=hand1
1037
1038 [formatters]
1039 keys=form1
1040
1041 [logger_root]
1042 level=WARNING
1043 handlers=hand1
1044
1045 [logger_parser]
1046 level=DEBUG
1047 handlers=
1048 propagate=1
1049 qualname=compiler.parser
1050
1051 [handler_hand1]
1052 class=StreamHandler
1053 level=NOTSET
1054 formatter=form1
1055 args=(sys.stdout,)
1056
1057 [formatter_form1]
1058 format=%(levelname)s ++ %(message)s
1059 datefmt=
1060 """
1061
Christian Heimes180510d2008-03-03 19:15:45 +00001062 # config2 has a subtle configuration error that should be reported
1063 config2 = config1.replace("sys.stdout", "sys.stbout")
1064
1065 # config3 has a less subtle configuration error
1066 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
1067
1068 # config4 specifies a custom formatter class to be loaded
1069 config4 = """
1070 [loggers]
1071 keys=root
1072
1073 [handlers]
1074 keys=hand1
1075
1076 [formatters]
1077 keys=form1
1078
1079 [logger_root]
1080 level=NOTSET
1081 handlers=hand1
1082
1083 [handler_hand1]
1084 class=StreamHandler
1085 level=NOTSET
1086 formatter=form1
1087 args=(sys.stdout,)
1088
1089 [formatter_form1]
1090 class=""" + __name__ + """.ExceptionFormatter
1091 format=%(levelname)s:%(name)s:%(message)s
1092 datefmt=
1093 """
1094
Georg Brandl3dbca812008-07-23 16:10:53 +00001095 # config5 specifies a custom handler class to be loaded
1096 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
1097
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001098 # config6 uses ', ' delimiters in the handlers and formatters sections
1099 config6 = """
1100 [loggers]
1101 keys=root,parser
1102
1103 [handlers]
1104 keys=hand1, hand2
1105
1106 [formatters]
1107 keys=form1, form2
1108
1109 [logger_root]
1110 level=WARNING
1111 handlers=
1112
1113 [logger_parser]
1114 level=DEBUG
1115 handlers=hand1
1116 propagate=1
1117 qualname=compiler.parser
1118
1119 [handler_hand1]
1120 class=StreamHandler
1121 level=NOTSET
1122 formatter=form1
1123 args=(sys.stdout,)
1124
1125 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001126 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001127 level=NOTSET
1128 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +00001129 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001130
1131 [formatter_form1]
1132 format=%(levelname)s ++ %(message)s
1133 datefmt=
1134
1135 [formatter_form2]
1136 format=%(message)s
1137 datefmt=
1138 """
1139
Vinay Sajip3f84b072011-03-07 17:49:33 +00001140 # config7 adds a compiler logger.
1141 config7 = """
1142 [loggers]
1143 keys=root,parser,compiler
1144
1145 [handlers]
1146 keys=hand1
1147
1148 [formatters]
1149 keys=form1
1150
1151 [logger_root]
1152 level=WARNING
1153 handlers=hand1
1154
1155 [logger_compiler]
1156 level=DEBUG
1157 handlers=
1158 propagate=1
1159 qualname=compiler
1160
1161 [logger_parser]
1162 level=DEBUG
1163 handlers=
1164 propagate=1
1165 qualname=compiler.parser
1166
1167 [handler_hand1]
1168 class=StreamHandler
1169 level=NOTSET
1170 formatter=form1
1171 args=(sys.stdout,)
1172
1173 [formatter_form1]
1174 format=%(levelname)s ++ %(message)s
1175 datefmt=
1176 """
1177
Christian Heimes180510d2008-03-03 19:15:45 +00001178 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001179 file = io.StringIO(textwrap.dedent(conf))
1180 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +00001181
1182 def test_config0_ok(self):
1183 # A simple config file which overrides the default settings.
1184 with captured_stdout() as output:
1185 self.apply_config(self.config0)
1186 logger = logging.getLogger()
1187 # Won't output anything
1188 logger.info(self.next_message())
1189 # Outputs a message
1190 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001191 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001192 ('ERROR', '2'),
1193 ], stream=output)
1194 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001195 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001196
Georg Brandl3dbca812008-07-23 16:10:53 +00001197 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +00001198 # A config file defining a sub-parser as well.
1199 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +00001200 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +00001201 logger = logging.getLogger("compiler.parser")
1202 # Both will output a message
1203 logger.info(self.next_message())
1204 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001205 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001206 ('INFO', '1'),
1207 ('ERROR', '2'),
1208 ], stream=output)
1209 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001210 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001211
1212 def test_config2_failure(self):
1213 # A simple config file which overrides the default settings.
1214 self.assertRaises(Exception, self.apply_config, self.config2)
1215
1216 def test_config3_failure(self):
1217 # A simple config file which overrides the default settings.
1218 self.assertRaises(Exception, self.apply_config, self.config3)
1219
1220 def test_config4_ok(self):
1221 # A config file specifying a custom formatter class.
1222 with captured_stdout() as output:
1223 self.apply_config(self.config4)
1224 logger = logging.getLogger()
1225 try:
1226 raise RuntimeError()
1227 except RuntimeError:
1228 logging.exception("just testing")
1229 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001230 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +00001231 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1232 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001233 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +00001234
Georg Brandl3dbca812008-07-23 16:10:53 +00001235 def test_config5_ok(self):
1236 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +00001237
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001238 def test_config6_ok(self):
1239 self.test_config1_ok(config=self.config6)
1240
Vinay Sajip3f84b072011-03-07 17:49:33 +00001241 def test_config7_ok(self):
1242 with captured_stdout() as output:
1243 self.apply_config(self.config1a)
1244 logger = logging.getLogger("compiler.parser")
1245 # See issue #11424. compiler-hyphenated sorts
1246 # between compiler and compiler.xyz and this
1247 # was preventing compiler.xyz from being included
1248 # in the child loggers of compiler because of an
1249 # overzealous loop termination condition.
1250 hyphenated = logging.getLogger('compiler-hyphenated')
1251 # All will output a message
1252 logger.info(self.next_message())
1253 logger.error(self.next_message())
1254 hyphenated.critical(self.next_message())
1255 self.assert_log_lines([
1256 ('INFO', '1'),
1257 ('ERROR', '2'),
1258 ('CRITICAL', '3'),
1259 ], stream=output)
1260 # Original logger output is empty.
1261 self.assert_log_lines([])
1262 with captured_stdout() as output:
1263 self.apply_config(self.config7)
1264 logger = logging.getLogger("compiler.parser")
1265 self.assertFalse(logger.disabled)
1266 # Both will output a message
1267 logger.info(self.next_message())
1268 logger.error(self.next_message())
1269 logger = logging.getLogger("compiler.lexer")
1270 # Both will output a message
1271 logger.info(self.next_message())
1272 logger.error(self.next_message())
1273 # Will not appear
1274 hyphenated.critical(self.next_message())
1275 self.assert_log_lines([
1276 ('INFO', '4'),
1277 ('ERROR', '5'),
1278 ('INFO', '6'),
1279 ('ERROR', '7'),
1280 ], stream=output)
1281 # Original logger output is empty.
1282 self.assert_log_lines([])
1283
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001284
Victor Stinner45df8202010-04-28 22:31:17 +00001285@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001286class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001287
Christian Heimes180510d2008-03-03 19:15:45 +00001288 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001289
Christian Heimes180510d2008-03-03 19:15:45 +00001290 def setUp(self):
1291 """Set up a TCP server to receive log messages, and a SocketHandler
1292 pointing to that server's address and port."""
1293 BaseTest.setUp(self)
Vinay Sajip7367d082011-05-02 13:17:27 +01001294 addr = ('localhost', 0)
1295 self.server = server = TestTCPServer(addr, self.handle_socket,
1296 0.01)
1297 server.start()
1298 server.ready.wait()
1299 self.sock_hdlr = logging.handlers.SocketHandler('localhost',
1300 server.port)
1301 self.log_output = ''
Christian Heimes180510d2008-03-03 19:15:45 +00001302 self.root_logger.removeHandler(self.root_logger.handlers[0])
1303 self.root_logger.addHandler(self.sock_hdlr)
Vinay Sajip7367d082011-05-02 13:17:27 +01001304 self.handled = threading.Semaphore(0)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001305
Christian Heimes180510d2008-03-03 19:15:45 +00001306 def tearDown(self):
1307 """Shutdown the TCP server."""
1308 try:
Vinay Sajip7367d082011-05-02 13:17:27 +01001309 self.server.stop(2.0)
Christian Heimes180510d2008-03-03 19:15:45 +00001310 self.root_logger.removeHandler(self.sock_hdlr)
1311 self.sock_hdlr.close()
Christian Heimes180510d2008-03-03 19:15:45 +00001312 finally:
1313 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001314
Vinay Sajip7367d082011-05-02 13:17:27 +01001315 def handle_socket(self, request):
1316 conn = request.connection
1317 while True:
1318 chunk = conn.recv(4)
1319 if len(chunk) < 4:
1320 break
1321 slen = struct.unpack(">L", chunk)[0]
1322 chunk = conn.recv(slen)
1323 while len(chunk) < slen:
1324 chunk = chunk + conn.recv(slen - len(chunk))
1325 obj = pickle.loads(chunk)
1326 record = logging.makeLogRecord(obj)
1327 self.log_output += record.msg + '\n'
1328 self.handled.release()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001329
Christian Heimes180510d2008-03-03 19:15:45 +00001330 def test_output(self):
1331 # The log message sent to the SocketHandler is properly received.
1332 logger = logging.getLogger("tcp")
1333 logger.error("spam")
Vinay Sajip7367d082011-05-02 13:17:27 +01001334 self.handled.acquire()
Christian Heimes180510d2008-03-03 19:15:45 +00001335 logger.debug("eggs")
Vinay Sajip7367d082011-05-02 13:17:27 +01001336 self.handled.acquire()
1337 self.assertEqual(self.log_output, "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001338
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001339 def test_noserver(self):
1340 # Kill the server
Vinay Sajip7367d082011-05-02 13:17:27 +01001341 self.server.stop(2.0)
Vinay Sajip7fe1d512011-04-28 12:04:58 +01001342 #The logging call should try to connect, which should fail
1343 try:
1344 raise RuntimeError('Deliberate mistake')
1345 except RuntimeError:
1346 self.root_logger.exception('Never sent')
1347 self.root_logger.error('Never sent, either')
1348 now = time.time()
1349 self.assertTrue(self.sock_hdlr.retryTime > now)
1350 time.sleep(self.sock_hdlr.retryTime - now + 0.001)
1351 self.root_logger.error('Nor this')
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001352
Vinay Sajip7367d082011-05-02 13:17:27 +01001353
1354@unittest.skipUnless(threading, 'Threading required for this test.')
1355class DatagramHandlerTest(BaseTest):
1356
1357 """Test for DatagramHandler."""
1358
1359 def setUp(self):
1360 """Set up a UDP server to receive log messages, and a DatagramHandler
1361 pointing to that server's address and port."""
1362 BaseTest.setUp(self)
1363 addr = ('localhost', 0)
1364 self.server = server = TestUDPServer(addr, self.handle_datagram,
1365 0.01)
1366 server.start()
1367 server.ready.wait()
1368 self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
1369 server.port)
1370 self.log_output = ''
1371 self.root_logger.removeHandler(self.root_logger.handlers[0])
1372 self.root_logger.addHandler(self.sock_hdlr)
1373 self.handled = threading.Event()
1374
1375 def tearDown(self):
1376 """Shutdown the UDP server."""
1377 try:
1378 self.server.stop(2.0)
1379 self.root_logger.removeHandler(self.sock_hdlr)
1380 self.sock_hdlr.close()
1381 finally:
1382 BaseTest.tearDown(self)
1383
1384 def handle_datagram(self, request):
1385 slen = struct.pack('>L', 0) # length of prefix
1386 packet = request.packet[len(slen):]
1387 obj = pickle.loads(packet)
1388 record = logging.makeLogRecord(obj)
1389 self.log_output += record.msg + '\n'
1390 self.handled.set()
1391
1392 def test_output(self):
1393 # The log message sent to the DatagramHandler is properly received.
1394 logger = logging.getLogger("udp")
1395 logger.error("spam")
1396 self.handled.wait()
Vinay Sajip714b8dc2011-05-02 14:43:00 +01001397 self.handled.clear()
1398 logger.error("eggs")
1399 self.handled.wait()
1400 self.assertEqual(self.log_output, "spam\neggs\n")
Vinay Sajip7367d082011-05-02 13:17:27 +01001401
1402
1403@unittest.skipUnless(threading, 'Threading required for this test.')
1404class SysLogHandlerTest(BaseTest):
1405
1406 """Test for SysLogHandler using UDP."""
1407
1408 def setUp(self):
1409 """Set up a UDP server to receive log messages, and a SysLogHandler
1410 pointing to that server's address and port."""
1411 BaseTest.setUp(self)
1412 addr = ('localhost', 0)
1413 self.server = server = TestUDPServer(addr, self.handle_datagram,
1414 0.01)
1415 server.start()
1416 server.ready.wait()
1417 self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
1418 server.port))
1419 self.log_output = ''
1420 self.root_logger.removeHandler(self.root_logger.handlers[0])
1421 self.root_logger.addHandler(self.sl_hdlr)
1422 self.handled = threading.Event()
1423
1424 def tearDown(self):
1425 """Shutdown the UDP server."""
1426 try:
1427 self.server.stop(2.0)
1428 self.root_logger.removeHandler(self.sl_hdlr)
1429 self.sl_hdlr.close()
1430 finally:
1431 BaseTest.tearDown(self)
1432
1433 def handle_datagram(self, request):
1434 self.log_output = request.packet
1435 self.handled.set()
1436
1437 def test_output(self):
1438 # The log message sent to the SysLogHandler is properly received.
1439 logger = logging.getLogger("slh")
1440 logger.error("sp\xe4m")
1441 self.handled.wait()
1442 self.assertEqual(self.log_output, b'<11>\xef\xbb\xbfsp\xc3\xa4m\x00')
1443
1444
1445@unittest.skipUnless(threading, 'Threading required for this test.')
1446class HTTPHandlerTest(BaseTest):
1447
1448 """Test for HTTPHandler."""
1449
1450 def setUp(self):
1451 """Set up an HTTP server to receive log messages, and a HTTPHandler
1452 pointing to that server's address and port."""
1453 BaseTest.setUp(self)
1454 addr = ('localhost', 0)
1455 self.server = server = TestHTTPServer(addr, self.handle_request,
1456 0.01)
1457 server.start()
1458 server.ready.wait()
1459 host = 'localhost:%d' % server.server_port
1460 self.h_hdlr = logging.handlers.HTTPHandler(host, '/frob')
1461 self.log_data = None
1462 self.root_logger.removeHandler(self.root_logger.handlers[0])
1463 self.root_logger.addHandler(self.h_hdlr)
1464 self.handled = threading.Event()
1465
1466 def tearDown(self):
1467 """Shutdown the UDP server."""
1468 try:
1469 self.server.stop(2.0)
1470 self.root_logger.removeHandler(self.h_hdlr)
1471 self.h_hdlr.close()
1472 finally:
1473 BaseTest.tearDown(self)
1474
1475 def handle_request(self, request):
Vinay Sajip0372e102011-05-05 12:59:14 +01001476 self.command = request.command
Vinay Sajip7367d082011-05-02 13:17:27 +01001477 self.log_data = urlparse(request.path)
Vinay Sajip0372e102011-05-05 12:59:14 +01001478 if self.command == 'POST':
1479 try:
1480 rlen = int(request.headers['Content-Length'])
1481 self.post_data = request.rfile.read(rlen)
1482 except:
1483 self.post_data = None
Vinay Sajip7367d082011-05-02 13:17:27 +01001484 request.send_response(200)
1485 self.handled.set()
1486
1487 def test_output(self):
1488 # The log message sent to the SysLogHandler is properly received.
1489 logger = logging.getLogger("http")
Vinay Sajip0372e102011-05-05 12:59:14 +01001490 for method in ('GET', 'POST'):
1491 self.h_hdlr.method = method
1492 msg = "sp\xe4m"
1493 logger.error(msg)
1494 self.handled.wait()
1495 self.assertEqual(self.log_data.path, '/frob')
1496 self.assertEqual(self.command, method)
1497 if method == 'GET':
1498 d = parse_qs(self.log_data.query)
1499 else:
1500 d = parse_qs(self.post_data.decode('utf-8'))
1501 self.assertEqual(d['name'], ['http'])
1502 self.assertEqual(d['funcName'], ['test_output'])
1503 self.assertEqual(d['msg'], [msg])
Vinay Sajip7367d082011-05-02 13:17:27 +01001504
Christian Heimes180510d2008-03-03 19:15:45 +00001505class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001506
Christian Heimes180510d2008-03-03 19:15:45 +00001507 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001508
Christian Heimes180510d2008-03-03 19:15:45 +00001509 def setUp(self):
1510 """Create a dict to remember potentially destroyed objects."""
1511 BaseTest.setUp(self)
1512 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001513
Christian Heimes180510d2008-03-03 19:15:45 +00001514 def _watch_for_survival(self, *args):
1515 """Watch the given objects for survival, by creating weakrefs to
1516 them."""
1517 for obj in args:
1518 key = id(obj), repr(obj)
1519 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001520
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001521 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001522 """Assert that all objects watched for survival have survived."""
1523 # Trigger cycle breaking.
1524 gc.collect()
1525 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001526 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001527 if ref() is None:
1528 dead.append(repr_)
1529 if dead:
1530 self.fail("%d objects should have survived "
1531 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001532
Christian Heimes180510d2008-03-03 19:15:45 +00001533 def test_persistent_loggers(self):
1534 # Logger objects are persistent and retain their configuration, even
1535 # if visible references are destroyed.
1536 self.root_logger.setLevel(logging.INFO)
1537 foo = logging.getLogger("foo")
1538 self._watch_for_survival(foo)
1539 foo.setLevel(logging.DEBUG)
1540 self.root_logger.debug(self.next_message())
1541 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001542 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001543 ('foo', 'DEBUG', '2'),
1544 ])
1545 del foo
1546 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001547 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001548 # foo has retained its settings.
1549 bar = logging.getLogger("foo")
1550 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001551 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001552 ('foo', 'DEBUG', '2'),
1553 ('foo', 'DEBUG', '3'),
1554 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001555
Benjamin Petersonf91df042009-02-13 02:50:59 +00001556
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001557class EncodingTest(BaseTest):
1558 def test_encoding_plain_file(self):
1559 # In Python 2.x, a plain file object is treated as having no encoding.
1560 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001561 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1562 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001563 # the non-ascii data we write to the log.
1564 data = "foo\x80"
1565 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001566 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001567 log.addHandler(handler)
1568 try:
1569 # write non-ascii data to the log.
1570 log.warning(data)
1571 finally:
1572 log.removeHandler(handler)
1573 handler.close()
1574 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001575 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001576 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001577 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001578 finally:
1579 f.close()
1580 finally:
1581 if os.path.isfile(fn):
1582 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001583
Benjamin Petersonf91df042009-02-13 02:50:59 +00001584 def test_encoding_cyrillic_unicode(self):
1585 log = logging.getLogger("test")
1586 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1587 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1588 #Ensure it's written in a Cyrillic encoding
1589 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001590 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001591 stream = io.BytesIO()
1592 writer = writer_class(stream, 'strict')
1593 handler = logging.StreamHandler(writer)
1594 log.addHandler(handler)
1595 try:
1596 log.warning(message)
1597 finally:
1598 log.removeHandler(handler)
1599 handler.close()
1600 # check we wrote exactly those bytes, ignoring trailing \n etc
1601 s = stream.getvalue()
1602 #Compare against what the data should be when encoded in CP-1251
1603 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1604
1605
Georg Brandlf9734072008-12-07 15:30:06 +00001606class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001607
Georg Brandlf9734072008-12-07 15:30:06 +00001608 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001609 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001610 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001611 self.addCleanup(lambda: logging.captureWarnings(False))
1612 warnings.filterwarnings("always", category=UserWarning)
1613 stream = io.StringIO()
1614 h = logging.StreamHandler(stream)
1615 logger = logging.getLogger("py.warnings")
1616 logger.addHandler(h)
1617 warnings.warn("I'm warning you...")
1618 logger.removeHandler(h)
1619 s = stream.getvalue()
1620 h.close()
1621 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001622
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001623 #See if an explicit file uses the original implementation
1624 a_file = io.StringIO()
1625 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1626 a_file, "Dummy line")
1627 s = a_file.getvalue()
1628 a_file.close()
1629 self.assertEqual(s,
1630 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1631
1632 def test_warnings_no_handlers(self):
1633 with warnings.catch_warnings():
1634 logging.captureWarnings(True)
1635 self.addCleanup(lambda: logging.captureWarnings(False))
1636
1637 # confirm our assumption: no loggers are set
1638 logger = logging.getLogger("py.warnings")
1639 assert logger.handlers == []
1640
1641 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1642 self.assertTrue(len(logger.handlers) == 1)
1643 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001644
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001645
1646def formatFunc(format, datefmt=None):
1647 return logging.Formatter(format, datefmt)
1648
1649def handlerFunc():
1650 return logging.StreamHandler()
1651
1652class CustomHandler(logging.StreamHandler):
1653 pass
1654
1655class ConfigDictTest(BaseTest):
1656
1657 """Reading logging config from a dictionary."""
1658
1659 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1660
1661 # config0 is a standard configuration.
1662 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001663 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001664 'formatters': {
1665 'form1' : {
1666 'format' : '%(levelname)s ++ %(message)s',
1667 },
1668 },
1669 'handlers' : {
1670 'hand1' : {
1671 'class' : 'logging.StreamHandler',
1672 'formatter' : 'form1',
1673 'level' : 'NOTSET',
1674 'stream' : 'ext://sys.stdout',
1675 },
1676 },
1677 'root' : {
1678 'level' : 'WARNING',
1679 'handlers' : ['hand1'],
1680 },
1681 }
1682
1683 # config1 adds a little to the standard configuration.
1684 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001685 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001686 'formatters': {
1687 'form1' : {
1688 'format' : '%(levelname)s ++ %(message)s',
1689 },
1690 },
1691 'handlers' : {
1692 'hand1' : {
1693 'class' : 'logging.StreamHandler',
1694 'formatter' : 'form1',
1695 'level' : 'NOTSET',
1696 'stream' : 'ext://sys.stdout',
1697 },
1698 },
1699 'loggers' : {
1700 'compiler.parser' : {
1701 'level' : 'DEBUG',
1702 'handlers' : ['hand1'],
1703 },
1704 },
1705 'root' : {
1706 'level' : 'WARNING',
1707 },
1708 }
1709
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001710 # config1a moves the handler to the root. Used with config8a
1711 config1a = {
1712 'version': 1,
1713 'formatters': {
1714 'form1' : {
1715 'format' : '%(levelname)s ++ %(message)s',
1716 },
1717 },
1718 'handlers' : {
1719 'hand1' : {
1720 'class' : 'logging.StreamHandler',
1721 'formatter' : 'form1',
1722 'level' : 'NOTSET',
1723 'stream' : 'ext://sys.stdout',
1724 },
1725 },
1726 'loggers' : {
1727 'compiler.parser' : {
1728 'level' : 'DEBUG',
1729 },
1730 },
1731 'root' : {
1732 'level' : 'WARNING',
1733 'handlers' : ['hand1'],
1734 },
1735 }
1736
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001737 # config2 has a subtle configuration error that should be reported
1738 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001739 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001740 'formatters': {
1741 'form1' : {
1742 'format' : '%(levelname)s ++ %(message)s',
1743 },
1744 },
1745 'handlers' : {
1746 'hand1' : {
1747 'class' : 'logging.StreamHandler',
1748 'formatter' : 'form1',
1749 'level' : 'NOTSET',
1750 'stream' : 'ext://sys.stdbout',
1751 },
1752 },
1753 'loggers' : {
1754 'compiler.parser' : {
1755 'level' : 'DEBUG',
1756 'handlers' : ['hand1'],
1757 },
1758 },
1759 'root' : {
1760 'level' : 'WARNING',
1761 },
1762 }
1763
1764 #As config1 but with a misspelt level on a handler
1765 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001766 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001767 'formatters': {
1768 'form1' : {
1769 'format' : '%(levelname)s ++ %(message)s',
1770 },
1771 },
1772 'handlers' : {
1773 'hand1' : {
1774 'class' : 'logging.StreamHandler',
1775 'formatter' : 'form1',
1776 'level' : 'NTOSET',
1777 'stream' : 'ext://sys.stdout',
1778 },
1779 },
1780 'loggers' : {
1781 'compiler.parser' : {
1782 'level' : 'DEBUG',
1783 'handlers' : ['hand1'],
1784 },
1785 },
1786 'root' : {
1787 'level' : 'WARNING',
1788 },
1789 }
1790
1791
1792 #As config1 but with a misspelt level on a logger
1793 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001794 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001795 'formatters': {
1796 'form1' : {
1797 'format' : '%(levelname)s ++ %(message)s',
1798 },
1799 },
1800 'handlers' : {
1801 'hand1' : {
1802 'class' : 'logging.StreamHandler',
1803 'formatter' : 'form1',
1804 'level' : 'NOTSET',
1805 'stream' : 'ext://sys.stdout',
1806 },
1807 },
1808 'loggers' : {
1809 'compiler.parser' : {
1810 'level' : 'DEBUG',
1811 'handlers' : ['hand1'],
1812 },
1813 },
1814 'root' : {
1815 'level' : 'WRANING',
1816 },
1817 }
1818
1819 # config3 has a less subtle configuration error
1820 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001821 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001822 'formatters': {
1823 'form1' : {
1824 'format' : '%(levelname)s ++ %(message)s',
1825 },
1826 },
1827 'handlers' : {
1828 'hand1' : {
1829 'class' : 'logging.StreamHandler',
1830 'formatter' : 'misspelled_name',
1831 'level' : 'NOTSET',
1832 'stream' : 'ext://sys.stdout',
1833 },
1834 },
1835 'loggers' : {
1836 'compiler.parser' : {
1837 'level' : 'DEBUG',
1838 'handlers' : ['hand1'],
1839 },
1840 },
1841 'root' : {
1842 'level' : 'WARNING',
1843 },
1844 }
1845
1846 # config4 specifies a custom formatter class to be loaded
1847 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001848 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001849 'formatters': {
1850 'form1' : {
1851 '()' : __name__ + '.ExceptionFormatter',
1852 'format' : '%(levelname)s:%(name)s:%(message)s',
1853 },
1854 },
1855 'handlers' : {
1856 'hand1' : {
1857 'class' : 'logging.StreamHandler',
1858 'formatter' : 'form1',
1859 'level' : 'NOTSET',
1860 'stream' : 'ext://sys.stdout',
1861 },
1862 },
1863 'root' : {
1864 'level' : 'NOTSET',
1865 'handlers' : ['hand1'],
1866 },
1867 }
1868
1869 # As config4 but using an actual callable rather than a string
1870 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001871 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001872 'formatters': {
1873 'form1' : {
1874 '()' : ExceptionFormatter,
1875 'format' : '%(levelname)s:%(name)s:%(message)s',
1876 },
1877 'form2' : {
1878 '()' : __name__ + '.formatFunc',
1879 'format' : '%(levelname)s:%(name)s:%(message)s',
1880 },
1881 'form3' : {
1882 '()' : formatFunc,
1883 'format' : '%(levelname)s:%(name)s:%(message)s',
1884 },
1885 },
1886 'handlers' : {
1887 'hand1' : {
1888 'class' : 'logging.StreamHandler',
1889 'formatter' : 'form1',
1890 'level' : 'NOTSET',
1891 'stream' : 'ext://sys.stdout',
1892 },
1893 'hand2' : {
1894 '()' : handlerFunc,
1895 },
1896 },
1897 'root' : {
1898 'level' : 'NOTSET',
1899 'handlers' : ['hand1'],
1900 },
1901 }
1902
1903 # config5 specifies a custom handler class to be loaded
1904 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001905 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001906 'formatters': {
1907 'form1' : {
1908 'format' : '%(levelname)s ++ %(message)s',
1909 },
1910 },
1911 'handlers' : {
1912 'hand1' : {
1913 'class' : __name__ + '.CustomHandler',
1914 'formatter' : 'form1',
1915 'level' : 'NOTSET',
1916 'stream' : 'ext://sys.stdout',
1917 },
1918 },
1919 'loggers' : {
1920 'compiler.parser' : {
1921 'level' : 'DEBUG',
1922 'handlers' : ['hand1'],
1923 },
1924 },
1925 'root' : {
1926 'level' : 'WARNING',
1927 },
1928 }
1929
1930 # config6 specifies a custom handler class to be loaded
1931 # but has bad arguments
1932 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001933 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001934 'formatters': {
1935 'form1' : {
1936 'format' : '%(levelname)s ++ %(message)s',
1937 },
1938 },
1939 'handlers' : {
1940 'hand1' : {
1941 'class' : __name__ + '.CustomHandler',
1942 'formatter' : 'form1',
1943 'level' : 'NOTSET',
1944 'stream' : 'ext://sys.stdout',
1945 '9' : 'invalid parameter name',
1946 },
1947 },
1948 'loggers' : {
1949 'compiler.parser' : {
1950 'level' : 'DEBUG',
1951 'handlers' : ['hand1'],
1952 },
1953 },
1954 'root' : {
1955 'level' : 'WARNING',
1956 },
1957 }
1958
1959 #config 7 does not define compiler.parser but defines compiler.lexer
1960 #so compiler.parser should be disabled after applying it
1961 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001962 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001963 'formatters': {
1964 'form1' : {
1965 'format' : '%(levelname)s ++ %(message)s',
1966 },
1967 },
1968 'handlers' : {
1969 'hand1' : {
1970 'class' : 'logging.StreamHandler',
1971 'formatter' : 'form1',
1972 'level' : 'NOTSET',
1973 'stream' : 'ext://sys.stdout',
1974 },
1975 },
1976 'loggers' : {
1977 'compiler.lexer' : {
1978 'level' : 'DEBUG',
1979 'handlers' : ['hand1'],
1980 },
1981 },
1982 'root' : {
1983 'level' : 'WARNING',
1984 },
1985 }
1986
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001987 # config8 defines both compiler and compiler.lexer
1988 # so compiler.parser should not be disabled (since
1989 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001990 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001991 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001992 'disable_existing_loggers' : False,
1993 'formatters': {
1994 'form1' : {
1995 'format' : '%(levelname)s ++ %(message)s',
1996 },
1997 },
1998 'handlers' : {
1999 'hand1' : {
2000 'class' : 'logging.StreamHandler',
2001 'formatter' : 'form1',
2002 'level' : 'NOTSET',
2003 'stream' : 'ext://sys.stdout',
2004 },
2005 },
2006 'loggers' : {
2007 'compiler' : {
2008 'level' : 'DEBUG',
2009 'handlers' : ['hand1'],
2010 },
2011 'compiler.lexer' : {
2012 },
2013 },
2014 'root' : {
2015 'level' : 'WARNING',
2016 },
2017 }
2018
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002019 # config8a disables existing loggers
2020 config8a = {
2021 'version': 1,
2022 'disable_existing_loggers' : True,
2023 'formatters': {
2024 'form1' : {
2025 'format' : '%(levelname)s ++ %(message)s',
2026 },
2027 },
2028 'handlers' : {
2029 'hand1' : {
2030 'class' : 'logging.StreamHandler',
2031 'formatter' : 'form1',
2032 'level' : 'NOTSET',
2033 'stream' : 'ext://sys.stdout',
2034 },
2035 },
2036 'loggers' : {
2037 'compiler' : {
2038 'level' : 'DEBUG',
2039 'handlers' : ['hand1'],
2040 },
2041 'compiler.lexer' : {
2042 },
2043 },
2044 'root' : {
2045 'level' : 'WARNING',
2046 },
2047 }
2048
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002049 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002050 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002051 'formatters': {
2052 'form1' : {
2053 'format' : '%(levelname)s ++ %(message)s',
2054 },
2055 },
2056 'handlers' : {
2057 'hand1' : {
2058 'class' : 'logging.StreamHandler',
2059 'formatter' : 'form1',
2060 'level' : 'WARNING',
2061 'stream' : 'ext://sys.stdout',
2062 },
2063 },
2064 'loggers' : {
2065 'compiler.parser' : {
2066 'level' : 'WARNING',
2067 'handlers' : ['hand1'],
2068 },
2069 },
2070 'root' : {
2071 'level' : 'NOTSET',
2072 },
2073 }
2074
2075 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002076 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002077 'incremental' : True,
2078 'handlers' : {
2079 'hand1' : {
2080 'level' : 'WARNING',
2081 },
2082 },
2083 'loggers' : {
2084 'compiler.parser' : {
2085 'level' : 'INFO',
2086 },
2087 },
2088 }
2089
2090 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002091 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002092 'incremental' : True,
2093 'handlers' : {
2094 'hand1' : {
2095 'level' : 'INFO',
2096 },
2097 },
2098 'loggers' : {
2099 'compiler.parser' : {
2100 'level' : 'INFO',
2101 },
2102 },
2103 }
2104
2105 #As config1 but with a filter added
2106 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002107 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002108 'formatters': {
2109 'form1' : {
2110 'format' : '%(levelname)s ++ %(message)s',
2111 },
2112 },
2113 'filters' : {
2114 'filt1' : {
2115 'name' : 'compiler.parser',
2116 },
2117 },
2118 'handlers' : {
2119 'hand1' : {
2120 'class' : 'logging.StreamHandler',
2121 'formatter' : 'form1',
2122 'level' : 'NOTSET',
2123 'stream' : 'ext://sys.stdout',
2124 'filters' : ['filt1'],
2125 },
2126 },
2127 'loggers' : {
2128 'compiler.parser' : {
2129 'level' : 'DEBUG',
2130 'filters' : ['filt1'],
2131 },
2132 },
2133 'root' : {
2134 'level' : 'WARNING',
2135 'handlers' : ['hand1'],
2136 },
2137 }
2138
2139 #As config1 but using cfg:// references
2140 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002141 'version': 1,
2142 'true_formatters': {
2143 'form1' : {
2144 'format' : '%(levelname)s ++ %(message)s',
2145 },
2146 },
2147 'handler_configs': {
2148 'hand1' : {
2149 'class' : 'logging.StreamHandler',
2150 'formatter' : 'form1',
2151 'level' : 'NOTSET',
2152 'stream' : 'ext://sys.stdout',
2153 },
2154 },
2155 'formatters' : 'cfg://true_formatters',
2156 'handlers' : {
2157 'hand1' : 'cfg://handler_configs[hand1]',
2158 },
2159 'loggers' : {
2160 'compiler.parser' : {
2161 'level' : 'DEBUG',
2162 'handlers' : ['hand1'],
2163 },
2164 },
2165 'root' : {
2166 'level' : 'WARNING',
2167 },
2168 }
2169
2170 #As config11 but missing the version key
2171 config12 = {
2172 'true_formatters': {
2173 'form1' : {
2174 'format' : '%(levelname)s ++ %(message)s',
2175 },
2176 },
2177 'handler_configs': {
2178 'hand1' : {
2179 'class' : 'logging.StreamHandler',
2180 'formatter' : 'form1',
2181 'level' : 'NOTSET',
2182 'stream' : 'ext://sys.stdout',
2183 },
2184 },
2185 'formatters' : 'cfg://true_formatters',
2186 'handlers' : {
2187 'hand1' : 'cfg://handler_configs[hand1]',
2188 },
2189 'loggers' : {
2190 'compiler.parser' : {
2191 'level' : 'DEBUG',
2192 'handlers' : ['hand1'],
2193 },
2194 },
2195 'root' : {
2196 'level' : 'WARNING',
2197 },
2198 }
2199
2200 #As config11 but using an unsupported version
2201 config13 = {
2202 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002203 'true_formatters': {
2204 'form1' : {
2205 'format' : '%(levelname)s ++ %(message)s',
2206 },
2207 },
2208 'handler_configs': {
2209 'hand1' : {
2210 'class' : 'logging.StreamHandler',
2211 'formatter' : 'form1',
2212 'level' : 'NOTSET',
2213 'stream' : 'ext://sys.stdout',
2214 },
2215 },
2216 'formatters' : 'cfg://true_formatters',
2217 'handlers' : {
2218 'hand1' : 'cfg://handler_configs[hand1]',
2219 },
2220 'loggers' : {
2221 'compiler.parser' : {
2222 'level' : 'DEBUG',
2223 'handlers' : ['hand1'],
2224 },
2225 },
2226 'root' : {
2227 'level' : 'WARNING',
2228 },
2229 }
2230
2231 def apply_config(self, conf):
2232 logging.config.dictConfig(conf)
2233
2234 def test_config0_ok(self):
2235 # A simple config which overrides the default settings.
2236 with captured_stdout() as output:
2237 self.apply_config(self.config0)
2238 logger = logging.getLogger()
2239 # Won't output anything
2240 logger.info(self.next_message())
2241 # Outputs a message
2242 logger.error(self.next_message())
2243 self.assert_log_lines([
2244 ('ERROR', '2'),
2245 ], stream=output)
2246 # Original logger output is empty.
2247 self.assert_log_lines([])
2248
2249 def test_config1_ok(self, config=config1):
2250 # A config defining a sub-parser as well.
2251 with captured_stdout() as output:
2252 self.apply_config(config)
2253 logger = logging.getLogger("compiler.parser")
2254 # Both will output a message
2255 logger.info(self.next_message())
2256 logger.error(self.next_message())
2257 self.assert_log_lines([
2258 ('INFO', '1'),
2259 ('ERROR', '2'),
2260 ], stream=output)
2261 # Original logger output is empty.
2262 self.assert_log_lines([])
2263
2264 def test_config2_failure(self):
2265 # A simple config which overrides the default settings.
2266 self.assertRaises(Exception, self.apply_config, self.config2)
2267
2268 def test_config2a_failure(self):
2269 # A simple config which overrides the default settings.
2270 self.assertRaises(Exception, self.apply_config, self.config2a)
2271
2272 def test_config2b_failure(self):
2273 # A simple config which overrides the default settings.
2274 self.assertRaises(Exception, self.apply_config, self.config2b)
2275
2276 def test_config3_failure(self):
2277 # A simple config which overrides the default settings.
2278 self.assertRaises(Exception, self.apply_config, self.config3)
2279
2280 def test_config4_ok(self):
2281 # A config specifying a custom formatter class.
2282 with captured_stdout() as output:
2283 self.apply_config(self.config4)
2284 #logger = logging.getLogger()
2285 try:
2286 raise RuntimeError()
2287 except RuntimeError:
2288 logging.exception("just testing")
2289 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002290 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002291 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2292 # Original logger output is empty
2293 self.assert_log_lines([])
2294
2295 def test_config4a_ok(self):
2296 # A config specifying a custom formatter class.
2297 with captured_stdout() as output:
2298 self.apply_config(self.config4a)
2299 #logger = logging.getLogger()
2300 try:
2301 raise RuntimeError()
2302 except RuntimeError:
2303 logging.exception("just testing")
2304 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00002305 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002306 "ERROR:root:just testing\nGot a [RuntimeError]\n")
2307 # Original logger output is empty
2308 self.assert_log_lines([])
2309
2310 def test_config5_ok(self):
2311 self.test_config1_ok(config=self.config5)
2312
2313 def test_config6_failure(self):
2314 self.assertRaises(Exception, self.apply_config, self.config6)
2315
2316 def test_config7_ok(self):
2317 with captured_stdout() as output:
2318 self.apply_config(self.config1)
2319 logger = logging.getLogger("compiler.parser")
2320 # Both will output a message
2321 logger.info(self.next_message())
2322 logger.error(self.next_message())
2323 self.assert_log_lines([
2324 ('INFO', '1'),
2325 ('ERROR', '2'),
2326 ], stream=output)
2327 # Original logger output is empty.
2328 self.assert_log_lines([])
2329 with captured_stdout() as output:
2330 self.apply_config(self.config7)
2331 logger = logging.getLogger("compiler.parser")
2332 self.assertTrue(logger.disabled)
2333 logger = logging.getLogger("compiler.lexer")
2334 # Both will output a message
2335 logger.info(self.next_message())
2336 logger.error(self.next_message())
2337 self.assert_log_lines([
2338 ('INFO', '3'),
2339 ('ERROR', '4'),
2340 ], stream=output)
2341 # Original logger output is empty.
2342 self.assert_log_lines([])
2343
2344 #Same as test_config_7_ok but don't disable old loggers.
2345 def test_config_8_ok(self):
2346 with captured_stdout() as output:
2347 self.apply_config(self.config1)
2348 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002349 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002350 logger.info(self.next_message())
2351 logger.error(self.next_message())
2352 self.assert_log_lines([
2353 ('INFO', '1'),
2354 ('ERROR', '2'),
2355 ], stream=output)
2356 # Original logger output is empty.
2357 self.assert_log_lines([])
2358 with captured_stdout() as output:
2359 self.apply_config(self.config8)
2360 logger = logging.getLogger("compiler.parser")
2361 self.assertFalse(logger.disabled)
2362 # Both will output a message
2363 logger.info(self.next_message())
2364 logger.error(self.next_message())
2365 logger = logging.getLogger("compiler.lexer")
2366 # Both will output a message
2367 logger.info(self.next_message())
2368 logger.error(self.next_message())
2369 self.assert_log_lines([
2370 ('INFO', '3'),
2371 ('ERROR', '4'),
2372 ('INFO', '5'),
2373 ('ERROR', '6'),
2374 ], stream=output)
2375 # Original logger output is empty.
2376 self.assert_log_lines([])
2377
Vinay Sajip9f9991c2011-03-07 18:02:57 +00002378 def test_config_8a_ok(self):
2379 with captured_stdout() as output:
2380 self.apply_config(self.config1a)
2381 logger = logging.getLogger("compiler.parser")
2382 # See issue #11424. compiler-hyphenated sorts
2383 # between compiler and compiler.xyz and this
2384 # was preventing compiler.xyz from being included
2385 # in the child loggers of compiler because of an
2386 # overzealous loop termination condition.
2387 hyphenated = logging.getLogger('compiler-hyphenated')
2388 # All will output a message
2389 logger.info(self.next_message())
2390 logger.error(self.next_message())
2391 hyphenated.critical(self.next_message())
2392 self.assert_log_lines([
2393 ('INFO', '1'),
2394 ('ERROR', '2'),
2395 ('CRITICAL', '3'),
2396 ], stream=output)
2397 # Original logger output is empty.
2398 self.assert_log_lines([])
2399 with captured_stdout() as output:
2400 self.apply_config(self.config8a)
2401 logger = logging.getLogger("compiler.parser")
2402 self.assertFalse(logger.disabled)
2403 # Both will output a message
2404 logger.info(self.next_message())
2405 logger.error(self.next_message())
2406 logger = logging.getLogger("compiler.lexer")
2407 # Both will output a message
2408 logger.info(self.next_message())
2409 logger.error(self.next_message())
2410 # Will not appear
2411 hyphenated.critical(self.next_message())
2412 self.assert_log_lines([
2413 ('INFO', '4'),
2414 ('ERROR', '5'),
2415 ('INFO', '6'),
2416 ('ERROR', '7'),
2417 ], stream=output)
2418 # Original logger output is empty.
2419 self.assert_log_lines([])
2420
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002421 def test_config_9_ok(self):
2422 with captured_stdout() as output:
2423 self.apply_config(self.config9)
2424 logger = logging.getLogger("compiler.parser")
2425 #Nothing will be output since both handler and logger are set to WARNING
2426 logger.info(self.next_message())
2427 self.assert_log_lines([], stream=output)
2428 self.apply_config(self.config9a)
2429 #Nothing will be output since both handler is still set to WARNING
2430 logger.info(self.next_message())
2431 self.assert_log_lines([], stream=output)
2432 self.apply_config(self.config9b)
2433 #Message should now be output
2434 logger.info(self.next_message())
2435 self.assert_log_lines([
2436 ('INFO', '3'),
2437 ], stream=output)
2438
2439 def test_config_10_ok(self):
2440 with captured_stdout() as output:
2441 self.apply_config(self.config10)
2442 logger = logging.getLogger("compiler.parser")
2443 logger.warning(self.next_message())
2444 logger = logging.getLogger('compiler')
2445 #Not output, because filtered
2446 logger.warning(self.next_message())
2447 logger = logging.getLogger('compiler.lexer')
2448 #Not output, because filtered
2449 logger.warning(self.next_message())
2450 logger = logging.getLogger("compiler.parser.codegen")
2451 #Output, as not filtered
2452 logger.error(self.next_message())
2453 self.assert_log_lines([
2454 ('WARNING', '1'),
2455 ('ERROR', '4'),
2456 ], stream=output)
2457
2458 def test_config11_ok(self):
2459 self.test_config1_ok(self.config11)
2460
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002461 def test_config12_failure(self):
2462 self.assertRaises(Exception, self.apply_config, self.config12)
2463
2464 def test_config13_failure(self):
2465 self.assertRaises(Exception, self.apply_config, self.config13)
2466
Victor Stinner45df8202010-04-28 22:31:17 +00002467 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002468 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002469 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002470 # Ask for a randomly assigned port (by using port 0)
2471 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002472 t.start()
2473 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002474 # Now get the port allocated
2475 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002476 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002477 try:
2478 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2479 sock.settimeout(2.0)
2480 sock.connect(('localhost', port))
2481
2482 slen = struct.pack('>L', len(text))
2483 s = slen + text
2484 sentsofar = 0
2485 left = len(s)
2486 while left > 0:
2487 sent = sock.send(s[sentsofar:])
2488 sentsofar += sent
2489 left -= sent
2490 sock.close()
2491 finally:
2492 t.ready.wait(2.0)
2493 logging.config.stopListening()
2494 t.join(2.0)
2495
2496 def test_listen_config_10_ok(self):
2497 with captured_stdout() as output:
2498 self.setup_via_listener(json.dumps(self.config10))
2499 logger = logging.getLogger("compiler.parser")
2500 logger.warning(self.next_message())
2501 logger = logging.getLogger('compiler')
2502 #Not output, because filtered
2503 logger.warning(self.next_message())
2504 logger = logging.getLogger('compiler.lexer')
2505 #Not output, because filtered
2506 logger.warning(self.next_message())
2507 logger = logging.getLogger("compiler.parser.codegen")
2508 #Output, as not filtered
2509 logger.error(self.next_message())
2510 self.assert_log_lines([
2511 ('WARNING', '1'),
2512 ('ERROR', '4'),
2513 ], stream=output)
2514
2515 def test_listen_config_1_ok(self):
2516 with captured_stdout() as output:
2517 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2518 logger = logging.getLogger("compiler.parser")
2519 # Both will output a message
2520 logger.info(self.next_message())
2521 logger.error(self.next_message())
2522 self.assert_log_lines([
2523 ('INFO', '1'),
2524 ('ERROR', '2'),
2525 ], stream=output)
2526 # Original logger output is empty.
2527 self.assert_log_lines([])
2528
Vinay Sajip373baef2011-04-26 20:05:24 +01002529 def test_baseconfig(self):
2530 d = {
2531 'atuple': (1, 2, 3),
2532 'alist': ['a', 'b', 'c'],
2533 'adict': {'d': 'e', 'f': 3 },
2534 'nest1': ('g', ('h', 'i'), 'j'),
2535 'nest2': ['k', ['l', 'm'], 'n'],
2536 'nest3': ['o', 'cfg://alist', 'p'],
2537 }
2538 bc = logging.config.BaseConfigurator(d)
2539 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2540 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2541 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2542 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2543 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2544 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2545 v = bc.convert('cfg://nest3')
2546 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2547 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2548 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2549 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002550
2551class ManagerTest(BaseTest):
2552 def test_manager_loggerclass(self):
2553 logged = []
2554
2555 class MyLogger(logging.Logger):
2556 def _log(self, level, msg, args, exc_info=None, extra=None):
2557 logged.append(msg)
2558
2559 man = logging.Manager(None)
2560 self.assertRaises(TypeError, man.setLoggerClass, int)
2561 man.setLoggerClass(MyLogger)
2562 logger = man.getLogger('test')
2563 logger.warning('should appear in logged')
2564 logging.warning('should not appear in logged')
2565
2566 self.assertEqual(logged, ['should appear in logged'])
2567
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002568 def test_set_log_record_factory(self):
2569 man = logging.Manager(None)
2570 expected = object()
2571 man.setLogRecordFactory(expected)
2572 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002573
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002574class ChildLoggerTest(BaseTest):
2575 def test_child_loggers(self):
2576 r = logging.getLogger()
2577 l1 = logging.getLogger('abc')
2578 l2 = logging.getLogger('def.ghi')
2579 c1 = r.getChild('xyz')
2580 c2 = r.getChild('uvw.xyz')
2581 self.assertTrue(c1 is logging.getLogger('xyz'))
2582 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2583 c1 = l1.getChild('def')
2584 c2 = c1.getChild('ghi')
2585 c3 = l1.getChild('def.ghi')
2586 self.assertTrue(c1 is logging.getLogger('abc.def'))
2587 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2588 self.assertTrue(c2 is c3)
2589
2590
Vinay Sajip6fac8172010-10-19 20:44:14 +00002591class DerivedLogRecord(logging.LogRecord):
2592 pass
2593
Vinay Sajip61561522010-12-03 11:50:38 +00002594class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002595
2596 def setUp(self):
2597 class CheckingFilter(logging.Filter):
2598 def __init__(self, cls):
2599 self.cls = cls
2600
2601 def filter(self, record):
2602 t = type(record)
2603 if t is not self.cls:
2604 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2605 self.cls)
2606 raise TypeError(msg)
2607 return True
2608
2609 BaseTest.setUp(self)
2610 self.filter = CheckingFilter(DerivedLogRecord)
2611 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002612 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002613
2614 def tearDown(self):
2615 self.root_logger.removeFilter(self.filter)
2616 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002617 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002618
2619 def test_logrecord_class(self):
2620 self.assertRaises(TypeError, self.root_logger.warning,
2621 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002622 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002623 self.root_logger.error(self.next_message())
2624 self.assert_log_lines([
2625 ('root', 'ERROR', '2'),
2626 ])
2627
2628
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002629class QueueHandlerTest(BaseTest):
2630 # Do not bother with a logger name group.
2631 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2632
2633 def setUp(self):
2634 BaseTest.setUp(self)
2635 self.queue = queue.Queue(-1)
2636 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2637 self.que_logger = logging.getLogger('que')
2638 self.que_logger.propagate = False
2639 self.que_logger.setLevel(logging.WARNING)
2640 self.que_logger.addHandler(self.que_hdlr)
2641
2642 def tearDown(self):
2643 self.que_hdlr.close()
2644 BaseTest.tearDown(self)
2645
2646 def test_queue_handler(self):
2647 self.que_logger.debug(self.next_message())
2648 self.assertRaises(queue.Empty, self.queue.get_nowait)
2649 self.que_logger.info(self.next_message())
2650 self.assertRaises(queue.Empty, self.queue.get_nowait)
2651 msg = self.next_message()
2652 self.que_logger.warning(msg)
2653 data = self.queue.get_nowait()
2654 self.assertTrue(isinstance(data, logging.LogRecord))
2655 self.assertEqual(data.name, self.que_logger.name)
2656 self.assertEqual((data.msg, data.args), (msg, None))
2657
Victor Stinnercafa2ef2011-05-02 16:11:28 +02002658 @unittest.skipUnless(hasattr(logging.handlers, 'QueueListener'),
2659 'logging.handlers.QueueListener required for this test')
Vinay Sajipe723e962011-04-15 22:27:17 +01002660 def test_queue_listener(self):
2661 handler = TestHandler(Matcher())
2662 listener = logging.handlers.QueueListener(self.queue, handler)
2663 listener.start()
2664 try:
2665 self.que_logger.warning(self.next_message())
2666 self.que_logger.error(self.next_message())
2667 self.que_logger.critical(self.next_message())
2668 finally:
2669 listener.stop()
2670 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2671 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2672 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2673
Vinay Sajip37eb3382011-04-26 20:26:41 +01002674ZERO = datetime.timedelta(0)
2675
2676class UTC(datetime.tzinfo):
2677 def utcoffset(self, dt):
2678 return ZERO
2679
2680 dst = utcoffset
2681
2682 def tzname(self, dt):
2683 return 'UTC'
2684
2685utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002686
Vinay Sajipa39c5712010-10-25 13:57:39 +00002687class FormatterTest(unittest.TestCase):
2688 def setUp(self):
2689 self.common = {
2690 'name': 'formatter.test',
2691 'level': logging.DEBUG,
2692 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2693 'lineno': 42,
2694 'exc_info': None,
2695 'func': None,
2696 'msg': 'Message with %d %s',
2697 'args': (2, 'placeholders'),
2698 }
2699 self.variants = {
2700 }
2701
2702 def get_record(self, name=None):
2703 result = dict(self.common)
2704 if name is not None:
2705 result.update(self.variants[name])
2706 return logging.makeLogRecord(result)
2707
2708 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002709 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002710 r = self.get_record()
2711 f = logging.Formatter('${%(message)s}')
2712 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2713 f = logging.Formatter('%(random)s')
2714 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002715 self.assertFalse(f.usesTime())
2716 f = logging.Formatter('%(asctime)s')
2717 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002718 f = logging.Formatter('%(asctime)-15s')
2719 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002720 f = logging.Formatter('asctime')
2721 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002722
2723 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002724 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002725 r = self.get_record()
2726 f = logging.Formatter('$%{message}%$', style='{')
2727 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2728 f = logging.Formatter('{random}', style='{')
2729 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002730 self.assertFalse(f.usesTime())
2731 f = logging.Formatter('{asctime}', style='{')
2732 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002733 f = logging.Formatter('{asctime!s:15}', style='{')
2734 self.assertTrue(f.usesTime())
2735 f = logging.Formatter('{asctime:15}', style='{')
2736 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002737 f = logging.Formatter('asctime', style='{')
2738 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002739
2740 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002741 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002742 r = self.get_record()
2743 f = logging.Formatter('$message', style='$')
2744 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2745 f = logging.Formatter('$$%${message}%$$', style='$')
2746 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2747 f = logging.Formatter('${random}', style='$')
2748 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002749 self.assertFalse(f.usesTime())
2750 f = logging.Formatter('${asctime}', style='$')
2751 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002752 f = logging.Formatter('${asctime', style='$')
2753 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002754 f = logging.Formatter('$asctime', style='$')
2755 self.assertTrue(f.usesTime())
2756 f = logging.Formatter('asctime', style='$')
2757 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002758
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002759 def test_invalid_style(self):
2760 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2761
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002762 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002763 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002764 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002765 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002766 r.msecs = 123
2767 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002768 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002769 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2770 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002771 f.format(r)
2772 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2773
2774class TestBufferingFormatter(logging.BufferingFormatter):
2775 def formatHeader(self, records):
2776 return '[(%d)' % len(records)
2777
2778 def formatFooter(self, records):
2779 return '(%d)]' % len(records)
2780
2781class BufferingFormatterTest(unittest.TestCase):
2782 def setUp(self):
2783 self.records = [
2784 logging.makeLogRecord({'msg': 'one'}),
2785 logging.makeLogRecord({'msg': 'two'}),
2786 ]
2787
2788 def test_default(self):
2789 f = logging.BufferingFormatter()
2790 self.assertEqual('', f.format([]))
2791 self.assertEqual('onetwo', f.format(self.records))
2792
2793 def test_custom(self):
2794 f = TestBufferingFormatter()
2795 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2796 lf = logging.Formatter('<%(message)s>')
2797 f = TestBufferingFormatter(lf)
2798 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002799
2800class ExceptionTest(BaseTest):
2801 def test_formatting(self):
2802 r = self.root_logger
2803 h = RecordingHandler()
2804 r.addHandler(h)
2805 try:
2806 raise RuntimeError('deliberate mistake')
2807 except:
2808 logging.exception('failed', stack_info=True)
2809 r.removeHandler(h)
2810 h.close()
2811 r = h.records[0]
2812 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2813 'call last):\n'))
2814 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2815 'deliberate mistake'))
2816 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2817 'call last):\n'))
2818 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2819 'stack_info=True)'))
2820
2821
Vinay Sajip5a27d402010-12-10 11:42:57 +00002822class LastResortTest(BaseTest):
2823 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002824 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002825 root = self.root_logger
2826 root.removeHandler(self.root_hdlr)
2827 old_stderr = sys.stderr
2828 old_lastresort = logging.lastResort
2829 old_raise_exceptions = logging.raiseExceptions
2830 try:
2831 sys.stderr = sio = io.StringIO()
2832 root.warning('This is your final chance!')
2833 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2834 #No handlers and no last resort, so 'No handlers' message
2835 logging.lastResort = None
2836 sys.stderr = sio = io.StringIO()
2837 root.warning('This is your final chance!')
2838 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2839 # 'No handlers' message only printed once
2840 sys.stderr = sio = io.StringIO()
2841 root.warning('This is your final chance!')
2842 self.assertEqual(sio.getvalue(), '')
2843 root.manager.emittedNoHandlerWarning = False
2844 #If raiseExceptions is False, no message is printed
2845 logging.raiseExceptions = False
2846 sys.stderr = sio = io.StringIO()
2847 root.warning('This is your final chance!')
2848 self.assertEqual(sio.getvalue(), '')
2849 finally:
2850 sys.stderr = old_stderr
2851 root.addHandler(self.root_hdlr)
2852 logging.lastResort = old_lastresort
2853 logging.raiseExceptions = old_raise_exceptions
2854
2855
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002856class FakeHandler:
2857
2858 def __init__(self, identifier, called):
2859 for method in ('acquire', 'flush', 'close', 'release'):
2860 setattr(self, method, self.record_call(identifier, method, called))
2861
2862 def record_call(self, identifier, method_name, called):
2863 def inner():
2864 called.append('{} - {}'.format(identifier, method_name))
2865 return inner
2866
2867
2868class RecordingHandler(logging.NullHandler):
2869
2870 def __init__(self, *args, **kwargs):
2871 super(RecordingHandler, self).__init__(*args, **kwargs)
2872 self.records = []
2873
2874 def handle(self, record):
2875 """Keep track of all the emitted records."""
2876 self.records.append(record)
2877
2878
2879class ShutdownTest(BaseTest):
2880
Vinay Sajip5e66b162011-04-20 15:41:14 +01002881 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002882
2883 def setUp(self):
2884 super(ShutdownTest, self).setUp()
2885 self.called = []
2886
2887 raise_exceptions = logging.raiseExceptions
2888 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2889
2890 def raise_error(self, error):
2891 def inner():
2892 raise error()
2893 return inner
2894
2895 def test_no_failure(self):
2896 # create some fake handlers
2897 handler0 = FakeHandler(0, self.called)
2898 handler1 = FakeHandler(1, self.called)
2899 handler2 = FakeHandler(2, self.called)
2900
2901 # create live weakref to those handlers
2902 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2903
2904 logging.shutdown(handlerList=list(handlers))
2905
2906 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2907 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2908 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2909 self.assertEqual(expected, self.called)
2910
2911 def _test_with_failure_in_method(self, method, error):
2912 handler = FakeHandler(0, self.called)
2913 setattr(handler, method, self.raise_error(error))
2914 handlers = [logging.weakref.ref(handler)]
2915
2916 logging.shutdown(handlerList=list(handlers))
2917
2918 self.assertEqual('0 - release', self.called[-1])
2919
2920 def test_with_ioerror_in_acquire(self):
2921 self._test_with_failure_in_method('acquire', IOError)
2922
2923 def test_with_ioerror_in_flush(self):
2924 self._test_with_failure_in_method('flush', IOError)
2925
2926 def test_with_ioerror_in_close(self):
2927 self._test_with_failure_in_method('close', IOError)
2928
2929 def test_with_valueerror_in_acquire(self):
2930 self._test_with_failure_in_method('acquire', ValueError)
2931
2932 def test_with_valueerror_in_flush(self):
2933 self._test_with_failure_in_method('flush', ValueError)
2934
2935 def test_with_valueerror_in_close(self):
2936 self._test_with_failure_in_method('close', ValueError)
2937
2938 def test_with_other_error_in_acquire_without_raise(self):
2939 logging.raiseExceptions = False
2940 self._test_with_failure_in_method('acquire', IndexError)
2941
2942 def test_with_other_error_in_flush_without_raise(self):
2943 logging.raiseExceptions = False
2944 self._test_with_failure_in_method('flush', IndexError)
2945
2946 def test_with_other_error_in_close_without_raise(self):
2947 logging.raiseExceptions = False
2948 self._test_with_failure_in_method('close', IndexError)
2949
2950 def test_with_other_error_in_acquire_with_raise(self):
2951 logging.raiseExceptions = True
2952 self.assertRaises(IndexError, self._test_with_failure_in_method,
2953 'acquire', IndexError)
2954
2955 def test_with_other_error_in_flush_with_raise(self):
2956 logging.raiseExceptions = True
2957 self.assertRaises(IndexError, self._test_with_failure_in_method,
2958 'flush', IndexError)
2959
2960 def test_with_other_error_in_close_with_raise(self):
2961 logging.raiseExceptions = True
2962 self.assertRaises(IndexError, self._test_with_failure_in_method,
2963 'close', IndexError)
2964
2965
2966class ModuleLevelMiscTest(BaseTest):
2967
Vinay Sajip5e66b162011-04-20 15:41:14 +01002968 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002969
2970 def test_disable(self):
2971 old_disable = logging.root.manager.disable
2972 # confirm our assumptions are correct
2973 assert old_disable == 0
2974 self.addCleanup(lambda: logging.disable(old_disable))
2975
2976 logging.disable(83)
2977 self.assertEqual(logging.root.manager.disable, 83)
2978
2979 def _test_log(self, method, level=None):
2980 called = []
2981 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002982 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002983
2984 recording = RecordingHandler()
2985 logging.root.addHandler(recording)
2986
2987 log_method = getattr(logging, method)
2988 if level is not None:
2989 log_method(level, "test me: %r", recording)
2990 else:
2991 log_method("test me: %r", recording)
2992
2993 self.assertEqual(len(recording.records), 1)
2994 record = recording.records[0]
2995 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2996
2997 expected_level = level if level is not None else getattr(logging, method.upper())
2998 self.assertEqual(record.levelno, expected_level)
2999
3000 # basicConfig was not called!
3001 self.assertEqual(called, [])
3002
3003 def test_log(self):
3004 self._test_log('log', logging.ERROR)
3005
3006 def test_debug(self):
3007 self._test_log('debug')
3008
3009 def test_info(self):
3010 self._test_log('info')
3011
3012 def test_warning(self):
3013 self._test_log('warning')
3014
3015 def test_error(self):
3016 self._test_log('error')
3017
3018 def test_critical(self):
3019 self._test_log('critical')
3020
3021 def test_set_logger_class(self):
3022 self.assertRaises(TypeError, logging.setLoggerClass, object)
3023
3024 class MyLogger(logging.Logger):
3025 pass
3026
3027 logging.setLoggerClass(MyLogger)
3028 self.assertEqual(logging.getLoggerClass(), MyLogger)
3029
3030 logging.setLoggerClass(logging.Logger)
3031 self.assertEqual(logging.getLoggerClass(), logging.Logger)
3032
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003033class LogRecordTest(BaseTest):
3034 def test_str_rep(self):
3035 r = logging.makeLogRecord({})
3036 s = str(r)
3037 self.assertTrue(s.startswith('<LogRecord: '))
3038 self.assertTrue(s.endswith('>'))
3039
3040 def test_dict_arg(self):
3041 h = RecordingHandler()
3042 r = logging.getLogger()
3043 r.addHandler(h)
3044 d = {'less' : 'more' }
3045 logging.warning('less is %(less)s', d)
3046 self.assertIs(h.records[0].args, d)
3047 self.assertEqual(h.records[0].message, 'less is more')
3048 r.removeHandler(h)
3049 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003050
Vinay Sajip7fe1d512011-04-28 12:04:58 +01003051 def test_multiprocessing(self):
3052 r = logging.makeLogRecord({})
3053 self.assertEqual(r.processName, 'MainProcess')
3054 import multiprocessing as mp
3055 r = logging.makeLogRecord({})
3056 self.assertEqual(r.processName, mp.current_process().name)
3057
3058 def test_optional(self):
3059 r = logging.makeLogRecord({})
3060 NOT_NONE = self.assertIsNotNone
3061 NOT_NONE(r.thread)
3062 NOT_NONE(r.threadName)
3063 NOT_NONE(r.process)
3064 NOT_NONE(r.processName)
3065 log_threads = logging.logThreads
3066 log_processes = logging.logProcesses
3067 log_multiprocessing = logging.logMultiprocessing
3068 try:
3069 logging.logThreads = False
3070 logging.logProcesses = False
3071 logging.logMultiprocessing = False
3072 r = logging.makeLogRecord({})
3073 NONE = self.assertIsNone
3074 NONE(r.thread)
3075 NONE(r.threadName)
3076 NONE(r.process)
3077 NONE(r.processName)
3078 finally:
3079 logging.logThreads = log_threads
3080 logging.logProcesses = log_processes
3081 logging.logMultiprocessing = log_multiprocessing
3082
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003083class BasicConfigTest(unittest.TestCase):
3084
Vinay Sajip95bf5042011-04-20 11:50:56 +01003085 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003086
3087 def setUp(self):
3088 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01003089 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01003090 self.saved_handlers = logging._handlers.copy()
3091 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003092 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003093 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003094 logging.root.handlers = []
3095
3096 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01003097 for h in logging.root.handlers[:]:
3098 logging.root.removeHandler(h)
3099 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003100 super(BasicConfigTest, self).tearDown()
3101
Vinay Sajip3def7e02011-04-20 10:58:06 +01003102 def cleanup(self):
3103 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01003104 logging._handlers.clear()
3105 logging._handlers.update(self.saved_handlers)
3106 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01003107 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01003108
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003109 def test_no_kwargs(self):
3110 logging.basicConfig()
3111
3112 # handler defaults to a StreamHandler to sys.stderr
3113 self.assertEqual(len(logging.root.handlers), 1)
3114 handler = logging.root.handlers[0]
3115 self.assertIsInstance(handler, logging.StreamHandler)
3116 self.assertEqual(handler.stream, sys.stderr)
3117
3118 formatter = handler.formatter
3119 # format defaults to logging.BASIC_FORMAT
3120 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
3121 # datefmt defaults to None
3122 self.assertIsNone(formatter.datefmt)
3123 # style defaults to %
3124 self.assertIsInstance(formatter._style, logging.PercentStyle)
3125
3126 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01003127 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003128
3129 def test_filename(self):
3130 logging.basicConfig(filename='test.log')
3131
3132 self.assertEqual(len(logging.root.handlers), 1)
3133 handler = logging.root.handlers[0]
3134 self.assertIsInstance(handler, logging.FileHandler)
3135
3136 expected = logging.FileHandler('test.log', 'a')
3137 self.addCleanup(expected.close)
3138 self.assertEqual(handler.stream.mode, expected.stream.mode)
3139 self.assertEqual(handler.stream.name, expected.stream.name)
3140
3141 def test_filemode(self):
3142 logging.basicConfig(filename='test.log', filemode='wb')
3143
3144 handler = logging.root.handlers[0]
3145 expected = logging.FileHandler('test.log', 'wb')
3146 self.addCleanup(expected.close)
3147 self.assertEqual(handler.stream.mode, expected.stream.mode)
3148
3149 def test_stream(self):
3150 stream = io.StringIO()
3151 self.addCleanup(stream.close)
3152 logging.basicConfig(stream=stream)
3153
3154 self.assertEqual(len(logging.root.handlers), 1)
3155 handler = logging.root.handlers[0]
3156 self.assertIsInstance(handler, logging.StreamHandler)
3157 self.assertEqual(handler.stream, stream)
3158
3159 def test_format(self):
3160 logging.basicConfig(format='foo')
3161
3162 formatter = logging.root.handlers[0].formatter
3163 self.assertEqual(formatter._style._fmt, 'foo')
3164
3165 def test_datefmt(self):
3166 logging.basicConfig(datefmt='bar')
3167
3168 formatter = logging.root.handlers[0].formatter
3169 self.assertEqual(formatter.datefmt, 'bar')
3170
3171 def test_style(self):
3172 logging.basicConfig(style='$')
3173
3174 formatter = logging.root.handlers[0].formatter
3175 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
3176
3177 def test_level(self):
3178 old_level = logging.root.level
3179 self.addCleanup(lambda: logging.root.setLevel(old_level))
3180
3181 logging.basicConfig(level=57)
3182 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003183 # Test that second call has no effect
3184 logging.basicConfig(level=58)
3185 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003186
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003187 def test_incompatible(self):
3188 assertRaises = self.assertRaises
3189 handlers = [logging.StreamHandler()]
3190 stream = sys.stderr
3191 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3192 stream=stream)
3193 assertRaises(ValueError, logging.basicConfig, filename='test.log',
3194 handlers=handlers)
3195 assertRaises(ValueError, logging.basicConfig, stream=stream,
3196 handlers=handlers)
3197
3198 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003199 handlers = [
3200 logging.StreamHandler(),
3201 logging.StreamHandler(sys.stdout),
3202 logging.StreamHandler(),
3203 ]
3204 f = logging.Formatter()
3205 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003206 logging.basicConfig(handlers=handlers)
3207 self.assertIs(handlers[0], logging.root.handlers[0])
3208 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003209 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003210 self.assertIsNotNone(handlers[0].formatter)
3211 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003212 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01003213 self.assertIs(handlers[0].formatter, handlers[1].formatter)
3214
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003215 def _test_log(self, method, level=None):
3216 # logging.root has no handlers so basicConfig should be called
3217 called = []
3218
3219 old_basic_config = logging.basicConfig
3220 def my_basic_config(*a, **kw):
3221 old_basic_config()
3222 old_level = logging.root.level
3223 logging.root.setLevel(100) # avoid having messages in stderr
3224 self.addCleanup(lambda: logging.root.setLevel(old_level))
3225 called.append((a, kw))
3226
3227 patch(self, logging, 'basicConfig', my_basic_config)
3228
3229 log_method = getattr(logging, method)
3230 if level is not None:
3231 log_method(level, "test me")
3232 else:
3233 log_method("test me")
3234
3235 # basicConfig was called with no arguments
3236 self.assertEqual(called, [((), {})])
3237
3238 def test_log(self):
3239 self._test_log('log', logging.WARNING)
3240
3241 def test_debug(self):
3242 self._test_log('debug')
3243
3244 def test_info(self):
3245 self._test_log('info')
3246
3247 def test_warning(self):
3248 self._test_log('warning')
3249
3250 def test_error(self):
3251 self._test_log('error')
3252
3253 def test_critical(self):
3254 self._test_log('critical')
3255
3256
3257class LoggerAdapterTest(unittest.TestCase):
3258
3259 def setUp(self):
3260 super(LoggerAdapterTest, self).setUp()
3261 old_handler_list = logging._handlerList[:]
3262
3263 self.recording = RecordingHandler()
3264 self.logger = logging.root
3265 self.logger.addHandler(self.recording)
3266 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3267 self.addCleanup(self.recording.close)
3268
3269 def cleanup():
3270 logging._handlerList[:] = old_handler_list
3271
3272 self.addCleanup(cleanup)
3273 self.addCleanup(logging.shutdown)
3274 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
3275
3276 def test_exception(self):
3277 msg = 'testing exception: %r'
3278 exc = None
3279 try:
3280 assert False
3281 except AssertionError as e:
3282 exc = e
3283 self.adapter.exception(msg, self.recording)
3284
3285 self.assertEqual(len(self.recording.records), 1)
3286 record = self.recording.records[0]
3287 self.assertEqual(record.levelno, logging.ERROR)
3288 self.assertEqual(record.msg, msg)
3289 self.assertEqual(record.args, (self.recording,))
3290 self.assertEqual(record.exc_info,
3291 (exc.__class__, exc, exc.__traceback__))
3292
3293 def test_critical(self):
3294 msg = 'critical test! %r'
3295 self.adapter.critical(msg, self.recording)
3296
3297 self.assertEqual(len(self.recording.records), 1)
3298 record = self.recording.records[0]
3299 self.assertEqual(record.levelno, logging.CRITICAL)
3300 self.assertEqual(record.msg, msg)
3301 self.assertEqual(record.args, (self.recording,))
3302
3303 def test_is_enabled_for(self):
3304 old_disable = self.adapter.logger.manager.disable
3305 self.adapter.logger.manager.disable = 33
3306 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
3307 'disable', old_disable))
3308 self.assertFalse(self.adapter.isEnabledFor(32))
3309
3310 def test_has_handlers(self):
3311 self.assertTrue(self.adapter.hasHandlers())
3312
3313 for handler in self.logger.handlers:
3314 self.logger.removeHandler(handler)
3315 assert not self.logger.hasHandlers()
3316
3317 self.assertFalse(self.adapter.hasHandlers())
3318
3319
3320class LoggerTest(BaseTest):
3321
3322 def setUp(self):
3323 super(LoggerTest, self).setUp()
3324 self.recording = RecordingHandler()
3325 self.logger = logging.Logger(name='blah')
3326 self.logger.addHandler(self.recording)
3327 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
3328 self.addCleanup(self.recording.close)
3329 self.addCleanup(logging.shutdown)
3330
3331 def test_set_invalid_level(self):
3332 self.assertRaises(TypeError, self.logger.setLevel, object())
3333
3334 def test_exception(self):
3335 msg = 'testing exception: %r'
3336 exc = None
3337 try:
3338 assert False
3339 except AssertionError as e:
3340 exc = e
3341 self.logger.exception(msg, self.recording)
3342
3343 self.assertEqual(len(self.recording.records), 1)
3344 record = self.recording.records[0]
3345 self.assertEqual(record.levelno, logging.ERROR)
3346 self.assertEqual(record.msg, msg)
3347 self.assertEqual(record.args, (self.recording,))
3348 self.assertEqual(record.exc_info,
3349 (exc.__class__, exc, exc.__traceback__))
3350
3351 def test_log_invalid_level_with_raise(self):
3352 old_raise = logging.raiseExceptions
3353 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3354
3355 logging.raiseExceptions = True
3356 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
3357
3358 def test_log_invalid_level_no_raise(self):
3359 old_raise = logging.raiseExceptions
3360 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
3361
3362 logging.raiseExceptions = False
3363 self.logger.log('10', 'test message') # no exception happens
3364
3365 def test_find_caller_with_stack_info(self):
3366 called = []
3367 patch(self, logging.traceback, 'print_stack',
3368 lambda f, file: called.append(file.getvalue()))
3369
3370 self.logger.findCaller(stack_info=True)
3371
3372 self.assertEqual(len(called), 1)
3373 self.assertEqual('Stack (most recent call last):\n', called[0])
3374
3375 def test_make_record_with_extra_overwrite(self):
3376 name = 'my record'
3377 level = 13
3378 fn = lno = msg = args = exc_info = func = sinfo = None
3379 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
3380 exc_info, func, sinfo)
3381
3382 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
3383 extra = {key: 'some value'}
3384 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
3385 fn, lno, msg, args, exc_info,
3386 extra=extra, sinfo=sinfo)
3387
3388 def test_make_record_with_extra_no_overwrite(self):
3389 name = 'my record'
3390 level = 13
3391 fn = lno = msg = args = exc_info = func = sinfo = None
3392 extra = {'valid_key': 'some value'}
3393 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
3394 exc_info, extra=extra, sinfo=sinfo)
3395 self.assertIn('valid_key', result.__dict__)
3396
3397 def test_has_handlers(self):
3398 self.assertTrue(self.logger.hasHandlers())
3399
3400 for handler in self.logger.handlers:
3401 self.logger.removeHandler(handler)
3402 assert not self.logger.hasHandlers()
3403
3404 self.assertFalse(self.logger.hasHandlers())
3405
3406 def test_has_handlers_no_propagate(self):
3407 child_logger = logging.getLogger('blah.child')
3408 child_logger.propagate = False
3409 assert child_logger.handlers == []
3410
3411 self.assertFalse(child_logger.hasHandlers())
3412
3413 def test_is_enabled_for(self):
3414 old_disable = self.logger.manager.disable
3415 self.logger.manager.disable = 23
3416 self.addCleanup(lambda: setattr(self.logger.manager,
3417 'disable', old_disable))
3418 self.assertFalse(self.logger.isEnabledFor(22))
3419
3420
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003421class BaseFileTest(BaseTest):
3422 "Base class for handler tests that write log files"
3423
3424 def setUp(self):
3425 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003426 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
3427 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003428 self.rmfiles = []
3429
3430 def tearDown(self):
3431 for fn in self.rmfiles:
3432 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00003433 if os.path.exists(self.fn):
3434 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00003435 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003436
3437 def assertLogFile(self, filename):
3438 "Assert a log file is there and register it for deletion"
3439 self.assertTrue(os.path.exists(filename),
Vinay Sajip7367d082011-05-02 13:17:27 +01003440 msg="Log file %r does not exist" % filename)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003441 self.rmfiles.append(filename)
3442
3443
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003444class FileHandlerTest(BaseFileTest):
3445 def test_delay(self):
3446 os.unlink(self.fn)
3447 fh = logging.FileHandler(self.fn, delay=True)
3448 self.assertIsNone(fh.stream)
3449 self.assertFalse(os.path.exists(self.fn))
3450 fh.handle(logging.makeLogRecord({}))
3451 self.assertIsNotNone(fh.stream)
3452 self.assertTrue(os.path.exists(self.fn))
3453 fh.close()
3454
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003455class RotatingFileHandlerTest(BaseFileTest):
3456 def next_rec(self):
3457 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
3458 self.next_message(), None, None, None)
3459
3460 def test_should_not_rollover(self):
3461 # If maxbytes is zero rollover never occurs
3462 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
3463 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00003464 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003465
3466 def test_should_rollover(self):
3467 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
3468 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00003469 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003470
3471 def test_file_created(self):
3472 # checks that the file is created and assumes it was created
3473 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003474 rh = logging.handlers.RotatingFileHandler(self.fn)
3475 rh.emit(self.next_rec())
3476 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003477 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003478
3479 def test_rollover_filenames(self):
3480 rh = logging.handlers.RotatingFileHandler(
3481 self.fn, backupCount=2, maxBytes=1)
3482 rh.emit(self.next_rec())
3483 self.assertLogFile(self.fn)
3484 rh.emit(self.next_rec())
3485 self.assertLogFile(self.fn + ".1")
3486 rh.emit(self.next_rec())
3487 self.assertLogFile(self.fn + ".2")
3488 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003489 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003490
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003491class TimedRotatingFileHandlerTest(BaseFileTest):
Vinay Sajip7367d082011-05-02 13:17:27 +01003492 # other test methods added below
3493 def test_rollover(self):
Vinay Sajip0372e102011-05-05 12:59:14 +01003494 fh = logging.handlers.TimedRotatingFileHandler(self.fn, 'S',
3495 backupCount=1)
Vinay Sajipfd285022011-05-07 17:01:22 +01003496 r = logging.makeLogRecord({'msg': 'testing'})
3497 fh.emit(r)
3498 self.assertLogFile(self.fn)
3499 time.sleep(1.0)
3500 fh.emit(r)
3501 fh.close()
3502 # At this point, we should have a recent rotated file which we
3503 # can test for the existence of. However, in practice, on some
3504 # machines which run really slowly, we don't know how far back
3505 # in time to go to look for the log file. So, we go back a fair
3506 # bit, and stop as soon as we see a rotated file. In theory this
3507 # could of course still fail, but the chances are lower.
3508 found = False
3509 now = datetime.datetime.now()
3510 GO_BACK = 2 * 60 # seconds
3511 for secs in range(1, GO_BACK):
3512 prev = now - datetime.timedelta(seconds=secs)
3513 fn = self.fn + prev.strftime(".%Y-%m-%d_%H-%M-%S")
3514 found = os.path.exists(fn)
3515 if found:
3516 self.rmfiles.append(fn)
3517 break
3518 msg = 'No rotated files found, went back %d seconds' % GO_BACK
3519 self.assertTrue(found, msg=msg)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003520
Vinay Sajip0372e102011-05-05 12:59:14 +01003521 def test_invalid(self):
3522 assertRaises = self.assertRaises
3523 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003524 self.fn, 'X', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003525 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003526 self.fn, 'W', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003527 assertRaises(ValueError, logging.handlers.TimedRotatingFileHandler,
Vinay Sajipfd285022011-05-07 17:01:22 +01003528 self.fn, 'W7', delay=True)
Vinay Sajip0372e102011-05-05 12:59:14 +01003529
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003530def secs(**kw):
3531 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3532
3533for when, exp in (('S', 1),
3534 ('M', 60),
3535 ('H', 60 * 60),
3536 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003537 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003538 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003539 ('W0', secs(days=4, hours=24)),
3540 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003541 def test_compute_rollover(self, when=when, exp=exp):
3542 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003543 self.fn, when=when, interval=1, backupCount=0, utc=True)
3544 currentTime = 0.0
3545 actual = rh.computeRollover(currentTime)
3546 if exp != actual:
3547 # Failures occur on some systems for MIDNIGHT and W0.
3548 # Print detailed calculation for MIDNIGHT so we can try to see
3549 # what's going on
3550 import time
3551 if when == 'MIDNIGHT':
3552 try:
3553 if rh.utc:
3554 t = time.gmtime(currentTime)
3555 else:
3556 t = time.localtime(currentTime)
3557 currentHour = t[3]
3558 currentMinute = t[4]
3559 currentSecond = t[5]
3560 # r is the number of seconds left between now and midnight
3561 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3562 currentMinute) * 60 +
3563 currentSecond)
3564 result = currentTime + r
3565 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3566 print('currentHour: %s' % currentHour, file=sys.stderr)
3567 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3568 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3569 print('r: %s' % r, file=sys.stderr)
3570 print('result: %s' % result, file=sys.stderr)
3571 except Exception:
3572 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3573 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003574 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003575 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3576
Vinay Sajip60ccd822011-05-09 17:32:09 +01003577
3578@unittest.skipUnless(win32evtlog, 'win32evtlog/win32evtlogutil required for this test.')
3579class NTEventLogHandlerTest(BaseTest):
3580 def test_basic(self):
3581 logtype = 'Application'
3582 elh = win32evtlog.OpenEventLog(None, logtype)
3583 num_recs = win32evtlog.GetNumberOfEventLogRecords(elh)
3584 h = logging.handlers.NTEventLogHandler('test_logging')
3585 r = logging.makeLogRecord({'msg': 'Test Log Message'})
3586 h.handle(r)
3587 h.close()
3588 # Now see if the event is recorded
3589 self.assertTrue(num_recs < win32evtlog.GetNumberOfEventLogRecords(elh))
3590 flags = win32evtlog.EVENTLOG_BACKWARDS_READ | \
3591 win32evtlog.EVENTLOG_SEQUENTIAL_READ
3592 found = False
3593 GO_BACK = 100
3594 events = win32evtlog.ReadEventLog(elh, flags, GO_BACK)
3595 for e in events:
3596 if e.SourceName != 'test_logging':
3597 continue
3598 msg = win32evtlogutil.SafeFormatMessage(e, logtype)
3599 if msg != 'Test Log Message\r\n':
3600 continue
3601 found = True
3602 break
3603 msg = 'Record not found in event log, went back %d records' % GO_BACK
3604 self.assertTrue(found, msg=msg)
3605
Christian Heimes180510d2008-03-03 19:15:45 +00003606# Set the locale to the platform-dependent default. I have no idea
3607# why the test does this, but in any case we save the current locale
3608# first and restore it at the end.
3609@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003610def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003611 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003612 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajip7367d082011-05-02 13:17:27 +01003613 ConfigFileTest, SocketHandlerTest, DatagramHandlerTest,
3614 MemoryTest, EncodingTest, WarningsTest, ConfigDictTest,
3615 ManagerTest, FormatterTest, BufferingFormatterTest,
3616 StreamHandlerTest, LogRecordFactoryTest, ChildLoggerTest,
3617 QueueHandlerTest, ShutdownTest, ModuleLevelMiscTest,
3618 BasicConfigTest, LoggerAdapterTest, LoggerTest,
3619 SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003620 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip60ccd822011-05-09 17:32:09 +01003621 SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003622 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003623 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003624
Christian Heimes180510d2008-03-03 19:15:45 +00003625if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003626 test_main()