blob: 567d084f0b7f83268bb39a4231c7e8e766ab8d5c [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
Benjamin Petersonf91df042009-02-13 02:50:59 +000028import codecs
Vinay Sajip19ec67a2010-09-17 18:57:36 +000029import datetime
Christian Heimes180510d2008-03-03 19:15:45 +000030import pickle
31import io
32import gc
Vinay Sajipdb81c4c2010-02-25 23:13:06 +000033import json
Christian Heimes180510d2008-03-03 19:15:45 +000034import os
Vinay Sajip8552d1f2010-09-14 09:34:09 +000035import queue
Christian Heimes180510d2008-03-03 19:15:45 +000036import re
Guido van Rossum2a1d5162003-01-21 21:05:22 +000037import select
Christian Heimes180510d2008-03-03 19:15:45 +000038import socket
Alexandre Vassalottice261952008-05-12 02:31:37 +000039from socketserver import ThreadingTCPServer, StreamRequestHandler
Christian Heimes180510d2008-03-03 19:15:45 +000040import struct
41import sys
42import tempfile
Vinay Sajipe6c1eb92011-03-29 17:20:34 +010043from test.support import captured_stdout, run_with_locale, run_unittest, patch
Vinay Sajipe723e962011-04-15 22:27:17 +010044from test.support import TestHandler, Matcher
Christian Heimes180510d2008-03-03 19:15:45 +000045import textwrap
Vinay Sajip37eb3382011-04-26 20:26:41 +010046import time
Christian Heimes180510d2008-03-03 19:15:45 +000047import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000048import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000049import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000050try:
51 import threading
52except ImportError:
53 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000054
55
Christian Heimes180510d2008-03-03 19:15:45 +000056class BaseTest(unittest.TestCase):
57
58 """Base class for logging tests."""
59
60 log_format = "%(name)s -> %(levelname)s: %(message)s"
61 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
62 message_num = 0
63
64 def setUp(self):
65 """Setup the default logging stream to an internal StringIO instance,
66 so that we can examine log output as we want."""
67 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000068 logging._acquireLock()
69 try:
Christian Heimes180510d2008-03-03 19:15:45 +000070 self.saved_handlers = logging._handlers.copy()
71 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000072 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000073 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000074 self.logger_states = logger_states = {}
75 for name in saved_loggers:
76 logger_states[name] = getattr(saved_loggers[name],
77 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000078 finally:
79 logging._releaseLock()
80
Benjamin Peterson22005fc2010-04-11 16:25:06 +000081 # Set two unused loggers: one non-ASCII and one Unicode.
82 # This is to test correct operation when sorting existing
83 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000084 self.logger1 = logging.getLogger("\xab\xd7\xbb")
85 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000086
Christian Heimes180510d2008-03-03 19:15:45 +000087 self.root_logger = logging.getLogger("")
88 self.original_logging_level = self.root_logger.getEffectiveLevel()
89
90 self.stream = io.StringIO()
91 self.root_logger.setLevel(logging.DEBUG)
92 self.root_hdlr = logging.StreamHandler(self.stream)
93 self.root_formatter = logging.Formatter(self.log_format)
94 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000095 if self.logger1.hasHandlers():
96 hlist = self.logger1.handlers + self.root_logger.handlers
97 raise AssertionError('Unexpected handlers: %s' % hlist)
98 if self.logger2.hasHandlers():
99 hlist = self.logger2.handlers + self.root_logger.handlers
100 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000101 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000102 self.assertTrue(self.logger1.hasHandlers())
103 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000104
105 def tearDown(self):
106 """Remove our logging stream, and restore the original logging
107 level."""
108 self.stream.close()
109 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000110 while self.root_logger.handlers:
111 h = self.root_logger.handlers[0]
112 self.root_logger.removeHandler(h)
113 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000114 self.root_logger.setLevel(self.original_logging_level)
115 logging._acquireLock()
116 try:
117 logging._levelNames.clear()
118 logging._levelNames.update(self.saved_level_names)
119 logging._handlers.clear()
120 logging._handlers.update(self.saved_handlers)
121 logging._handlerList[:] = self.saved_handler_list
122 loggerDict = logging.getLogger().manager.loggerDict
123 loggerDict.clear()
124 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000125 logger_states = self.logger_states
126 for name in self.logger_states:
127 if logger_states[name] is not None:
128 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000129 finally:
130 logging._releaseLock()
131
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000132 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000133 """Match the collected log lines against the regular expression
134 self.expected_log_pat, and compare the extracted group values to
135 the expected_values list of tuples."""
136 stream = stream or self.stream
137 pat = re.compile(self.expected_log_pat)
138 try:
139 stream.reset()
140 actual_lines = stream.readlines()
141 except AttributeError:
142 # StringIO.StringIO lacks a reset() method.
143 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000144 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000145 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000146 for actual, expected in zip(actual_lines, expected_values):
147 match = pat.search(actual)
148 if not match:
149 self.fail("Log line does not match expected pattern:\n" +
150 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000151 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000152 s = stream.read()
153 if s:
154 self.fail("Remaining output at end of log stream:\n" + s)
155
156 def next_message(self):
157 """Generate a message consisting solely of an auto-incrementing
158 integer."""
159 self.message_num += 1
160 return "%d" % self.message_num
161
162
163class BuiltinLevelsTest(BaseTest):
164 """Test builtin levels and their inheritance."""
165
166 def test_flat(self):
167 #Logging levels in a flat logger namespace.
168 m = self.next_message
169
170 ERR = logging.getLogger("ERR")
171 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000172 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000173 INF.setLevel(logging.INFO)
174 DEB = logging.getLogger("DEB")
175 DEB.setLevel(logging.DEBUG)
176
177 # These should log.
178 ERR.log(logging.CRITICAL, m())
179 ERR.error(m())
180
181 INF.log(logging.CRITICAL, m())
182 INF.error(m())
183 INF.warn(m())
184 INF.info(m())
185
186 DEB.log(logging.CRITICAL, m())
187 DEB.error(m())
188 DEB.warn (m())
189 DEB.info (m())
190 DEB.debug(m())
191
192 # These should not log.
193 ERR.warn(m())
194 ERR.info(m())
195 ERR.debug(m())
196
197 INF.debug(m())
198
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000199 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000200 ('ERR', 'CRITICAL', '1'),
201 ('ERR', 'ERROR', '2'),
202 ('INF', 'CRITICAL', '3'),
203 ('INF', 'ERROR', '4'),
204 ('INF', 'WARNING', '5'),
205 ('INF', 'INFO', '6'),
206 ('DEB', 'CRITICAL', '7'),
207 ('DEB', 'ERROR', '8'),
208 ('DEB', 'WARNING', '9'),
209 ('DEB', 'INFO', '10'),
210 ('DEB', 'DEBUG', '11'),
211 ])
212
213 def test_nested_explicit(self):
214 # Logging levels in a nested namespace, all explicitly set.
215 m = self.next_message
216
217 INF = logging.getLogger("INF")
218 INF.setLevel(logging.INFO)
219 INF_ERR = logging.getLogger("INF.ERR")
220 INF_ERR.setLevel(logging.ERROR)
221
222 # These should log.
223 INF_ERR.log(logging.CRITICAL, m())
224 INF_ERR.error(m())
225
226 # These should not log.
227 INF_ERR.warn(m())
228 INF_ERR.info(m())
229 INF_ERR.debug(m())
230
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000231 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000232 ('INF.ERR', 'CRITICAL', '1'),
233 ('INF.ERR', 'ERROR', '2'),
234 ])
235
236 def test_nested_inherited(self):
237 #Logging levels in a nested namespace, inherited from parent loggers.
238 m = self.next_message
239
240 INF = logging.getLogger("INF")
241 INF.setLevel(logging.INFO)
242 INF_ERR = logging.getLogger("INF.ERR")
243 INF_ERR.setLevel(logging.ERROR)
244 INF_UNDEF = logging.getLogger("INF.UNDEF")
245 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
246 UNDEF = logging.getLogger("UNDEF")
247
248 # These should log.
249 INF_UNDEF.log(logging.CRITICAL, m())
250 INF_UNDEF.error(m())
251 INF_UNDEF.warn(m())
252 INF_UNDEF.info(m())
253 INF_ERR_UNDEF.log(logging.CRITICAL, m())
254 INF_ERR_UNDEF.error(m())
255
256 # These should not log.
257 INF_UNDEF.debug(m())
258 INF_ERR_UNDEF.warn(m())
259 INF_ERR_UNDEF.info(m())
260 INF_ERR_UNDEF.debug(m())
261
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000262 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000263 ('INF.UNDEF', 'CRITICAL', '1'),
264 ('INF.UNDEF', 'ERROR', '2'),
265 ('INF.UNDEF', 'WARNING', '3'),
266 ('INF.UNDEF', 'INFO', '4'),
267 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
268 ('INF.ERR.UNDEF', 'ERROR', '6'),
269 ])
270
271 def test_nested_with_virtual_parent(self):
272 # Logging levels when some parent does not exist yet.
273 m = self.next_message
274
275 INF = logging.getLogger("INF")
276 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
277 CHILD = logging.getLogger("INF.BADPARENT")
278 INF.setLevel(logging.INFO)
279
280 # These should log.
281 GRANDCHILD.log(logging.FATAL, m())
282 GRANDCHILD.info(m())
283 CHILD.log(logging.FATAL, m())
284 CHILD.info(m())
285
286 # These should not log.
287 GRANDCHILD.debug(m())
288 CHILD.debug(m())
289
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000290 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000291 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
292 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
293 ('INF.BADPARENT', 'CRITICAL', '3'),
294 ('INF.BADPARENT', 'INFO', '4'),
295 ])
296
297
298class BasicFilterTest(BaseTest):
299
300 """Test the bundled Filter class."""
301
302 def test_filter(self):
303 # Only messages satisfying the specified criteria pass through the
304 # filter.
305 filter_ = logging.Filter("spam.eggs")
306 handler = self.root_logger.handlers[0]
307 try:
308 handler.addFilter(filter_)
309 spam = logging.getLogger("spam")
310 spam_eggs = logging.getLogger("spam.eggs")
311 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
312 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
313
314 spam.info(self.next_message())
315 spam_eggs.info(self.next_message()) # Good.
316 spam_eggs_fish.info(self.next_message()) # Good.
317 spam_bakedbeans.info(self.next_message())
318
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000319 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000320 ('spam.eggs', 'INFO', '2'),
321 ('spam.eggs.fish', 'INFO', '3'),
322 ])
323 finally:
324 handler.removeFilter(filter_)
325
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000326 def test_callable_filter(self):
327 # Only messages satisfying the specified criteria pass through the
328 # filter.
329
330 def filterfunc(record):
331 parts = record.name.split('.')
332 prefix = '.'.join(parts[:2])
333 return prefix == 'spam.eggs'
334
335 handler = self.root_logger.handlers[0]
336 try:
337 handler.addFilter(filterfunc)
338 spam = logging.getLogger("spam")
339 spam_eggs = logging.getLogger("spam.eggs")
340 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
341 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
342
343 spam.info(self.next_message())
344 spam_eggs.info(self.next_message()) # Good.
345 spam_eggs_fish.info(self.next_message()) # Good.
346 spam_bakedbeans.info(self.next_message())
347
348 self.assert_log_lines([
349 ('spam.eggs', 'INFO', '2'),
350 ('spam.eggs.fish', 'INFO', '3'),
351 ])
352 finally:
353 handler.removeFilter(filterfunc)
354
Vinay Sajip985ef872011-04-26 19:34:04 +0100355 def test_empty_filter(self):
356 f = logging.Filter()
357 r = logging.makeLogRecord({'name': 'spam.eggs'})
358 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000359
360#
361# First, we define our levels. There can be as many as you want - the only
362# limitations are that they should be integers, the lowest should be > 0 and
363# larger values mean less information being logged. If you need specific
364# level values which do not fit into these limitations, you can use a
365# mapping dictionary to convert between your application levels and the
366# logging system.
367#
368SILENT = 120
369TACITURN = 119
370TERSE = 118
371EFFUSIVE = 117
372SOCIABLE = 116
373VERBOSE = 115
374TALKATIVE = 114
375GARRULOUS = 113
376CHATTERBOX = 112
377BORING = 111
378
379LEVEL_RANGE = range(BORING, SILENT + 1)
380
381#
382# Next, we define names for our levels. You don't need to do this - in which
383# case the system will use "Level n" to denote the text for the level.
384#
385my_logging_levels = {
386 SILENT : 'Silent',
387 TACITURN : 'Taciturn',
388 TERSE : 'Terse',
389 EFFUSIVE : 'Effusive',
390 SOCIABLE : 'Sociable',
391 VERBOSE : 'Verbose',
392 TALKATIVE : 'Talkative',
393 GARRULOUS : 'Garrulous',
394 CHATTERBOX : 'Chatterbox',
395 BORING : 'Boring',
396}
397
398class GarrulousFilter(logging.Filter):
399
400 """A filter which blocks garrulous messages."""
401
402 def filter(self, record):
403 return record.levelno != GARRULOUS
404
405class VerySpecificFilter(logging.Filter):
406
407 """A filter which blocks sociable and taciturn messages."""
408
409 def filter(self, record):
410 return record.levelno not in [SOCIABLE, TACITURN]
411
412
413class CustomLevelsAndFiltersTest(BaseTest):
414
415 """Test various filtering possibilities with custom logging levels."""
416
417 # Skip the logger name group.
418 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
419
420 def setUp(self):
421 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000422 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000423 logging.addLevelName(k, v)
424
425 def log_at_all_levels(self, logger):
426 for lvl in LEVEL_RANGE:
427 logger.log(lvl, self.next_message())
428
429 def test_logger_filter(self):
430 # Filter at logger level.
431 self.root_logger.setLevel(VERBOSE)
432 # Levels >= 'Verbose' are good.
433 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000434 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000435 ('Verbose', '5'),
436 ('Sociable', '6'),
437 ('Effusive', '7'),
438 ('Terse', '8'),
439 ('Taciturn', '9'),
440 ('Silent', '10'),
441 ])
442
443 def test_handler_filter(self):
444 # Filter at handler level.
445 self.root_logger.handlers[0].setLevel(SOCIABLE)
446 try:
447 # Levels >= 'Sociable' are good.
448 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000449 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000450 ('Sociable', '6'),
451 ('Effusive', '7'),
452 ('Terse', '8'),
453 ('Taciturn', '9'),
454 ('Silent', '10'),
455 ])
456 finally:
457 self.root_logger.handlers[0].setLevel(logging.NOTSET)
458
459 def test_specific_filters(self):
460 # Set a specific filter object on the handler, and then add another
461 # filter object on the logger itself.
462 handler = self.root_logger.handlers[0]
463 specific_filter = None
464 garr = GarrulousFilter()
465 handler.addFilter(garr)
466 try:
467 self.log_at_all_levels(self.root_logger)
468 first_lines = [
469 # Notice how 'Garrulous' is missing
470 ('Boring', '1'),
471 ('Chatterbox', '2'),
472 ('Talkative', '4'),
473 ('Verbose', '5'),
474 ('Sociable', '6'),
475 ('Effusive', '7'),
476 ('Terse', '8'),
477 ('Taciturn', '9'),
478 ('Silent', '10'),
479 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000480 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000481
482 specific_filter = VerySpecificFilter()
483 self.root_logger.addFilter(specific_filter)
484 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000485 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000486 # Not only 'Garrulous' is still missing, but also 'Sociable'
487 # and 'Taciturn'
488 ('Boring', '11'),
489 ('Chatterbox', '12'),
490 ('Talkative', '14'),
491 ('Verbose', '15'),
492 ('Effusive', '17'),
493 ('Terse', '18'),
494 ('Silent', '20'),
495 ])
496 finally:
497 if specific_filter:
498 self.root_logger.removeFilter(specific_filter)
499 handler.removeFilter(garr)
500
501
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100502class HandlerTest(BaseTest):
503 def test_name(self):
504 h = logging.Handler()
505 h.name = 'generic'
506 self.assertEqual(h.name, 'generic')
507 h.name = 'anothergeneric'
508 self.assertEqual(h.name, 'anothergeneric')
509 self.assertRaises(NotImplementedError, h.emit, None)
510
Vinay Sajip5a35b062011-04-27 11:31:14 +0100511 def test_builtin_handlers(self):
512 # We can't actually *use* too many handlers in the tests,
513 # but we can try instantiating them with various options
514 if sys.platform in ('linux2', 'darwin'):
515 for existing in (True, False):
516 fd, fn = tempfile.mkstemp()
517 os.close(fd)
518 if not existing:
519 os.unlink(fn)
520 h = logging.handlers.WatchedFileHandler(fn, delay=True)
521 if existing:
522 self.assertNotEqual(h.dev, -1)
523 self.assertNotEqual(h.ino, -1)
524 else:
525 self.assertEqual(h.dev, -1)
526 self.assertEqual(h.ino, -1)
527 h.close()
528 if existing:
529 os.unlink(fn)
530 if sys.platform == 'darwin':
531 sockname = '/var/run/log'
532 else:
533 sockname = '/dev/log'
534 h = logging.handlers.SysLogHandler(sockname)
535 self.assertEqual(h.facility, h.LOG_USER)
536 self.assertTrue(h.unixsocket)
537 h.close()
538 h = logging.handlers.SMTPHandler('localhost', 'me', 'you', 'Log')
539 self.assertEqual(h.toaddrs, ['you'])
540 h.close()
541 for method in ('GET', 'POST', 'PUT'):
542 if method == 'PUT':
543 self.assertRaises(ValueError, logging.handlers.HTTPHandler,
544 'localhost', '/log', method)
545 else:
546 h = logging.handlers.HTTPHandler('localhost', '/log', method)
547 h.close()
548 h = logging.handlers.BufferingHandler(0)
549 r = logging.makeLogRecord({})
550 self.assertTrue(h.shouldFlush(r))
551 h.close()
552 h = logging.handlers.BufferingHandler(1)
553 self.assertFalse(h.shouldFlush(r))
554 h.close()
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100555
556class BadStream(object):
557 def write(self, data):
558 raise RuntimeError('deliberate mistake')
559
560class TestStreamHandler(logging.StreamHandler):
561 def handleError(self, record):
562 self.error_record = record
563
564class StreamHandlerTest(BaseTest):
565 def test_error_handling(self):
566 h = TestStreamHandler(BadStream())
567 r = logging.makeLogRecord({})
568 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100569 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100570 try:
571 h.handle(r)
572 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100573 h = logging.StreamHandler(BadStream())
574 sys.stderr = sio = io.StringIO()
575 h.handle(r)
576 self.assertTrue('\nRuntimeError: '
577 'deliberate mistake\n' in sio.getvalue())
578 logging.raiseExceptions = False
579 sys.stderr = sio = io.StringIO()
580 h.handle(r)
581 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100582 finally:
583 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100584 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100585
Christian Heimes180510d2008-03-03 19:15:45 +0000586class MemoryHandlerTest(BaseTest):
587
588 """Tests for the MemoryHandler."""
589
590 # Do not bother with a logger name group.
591 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
592
593 def setUp(self):
594 BaseTest.setUp(self)
595 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
596 self.root_hdlr)
597 self.mem_logger = logging.getLogger('mem')
598 self.mem_logger.propagate = 0
599 self.mem_logger.addHandler(self.mem_hdlr)
600
601 def tearDown(self):
602 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000603 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000604
605 def test_flush(self):
606 # The memory handler flushes to its target handler based on specific
607 # criteria (message count and message level).
608 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000609 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000610 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000611 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000612 # This will flush because the level is >= logging.WARNING
613 self.mem_logger.warn(self.next_message())
614 lines = [
615 ('DEBUG', '1'),
616 ('INFO', '2'),
617 ('WARNING', '3'),
618 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000619 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000620 for n in (4, 14):
621 for i in range(9):
622 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000623 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000624 # This will flush because it's the 10th message since the last
625 # flush.
626 self.mem_logger.debug(self.next_message())
627 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000628 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000629
630 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000631 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000632
633
634class ExceptionFormatter(logging.Formatter):
635 """A special exception formatter."""
636 def formatException(self, ei):
637 return "Got a [%s]" % ei[0].__name__
638
639
640class ConfigFileTest(BaseTest):
641
642 """Reading logging config from a .ini-style config file."""
643
644 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
645
646 # config0 is a standard configuration.
647 config0 = """
648 [loggers]
649 keys=root
650
651 [handlers]
652 keys=hand1
653
654 [formatters]
655 keys=form1
656
657 [logger_root]
658 level=WARNING
659 handlers=hand1
660
661 [handler_hand1]
662 class=StreamHandler
663 level=NOTSET
664 formatter=form1
665 args=(sys.stdout,)
666
667 [formatter_form1]
668 format=%(levelname)s ++ %(message)s
669 datefmt=
670 """
671
672 # config1 adds a little to the standard configuration.
673 config1 = """
674 [loggers]
675 keys=root,parser
676
677 [handlers]
678 keys=hand1
679
680 [formatters]
681 keys=form1
682
683 [logger_root]
684 level=WARNING
685 handlers=
686
687 [logger_parser]
688 level=DEBUG
689 handlers=hand1
690 propagate=1
691 qualname=compiler.parser
692
693 [handler_hand1]
694 class=StreamHandler
695 level=NOTSET
696 formatter=form1
697 args=(sys.stdout,)
698
699 [formatter_form1]
700 format=%(levelname)s ++ %(message)s
701 datefmt=
702 """
703
Vinay Sajip3f84b072011-03-07 17:49:33 +0000704 # config1a moves the handler to the root.
705 config1a = """
706 [loggers]
707 keys=root,parser
708
709 [handlers]
710 keys=hand1
711
712 [formatters]
713 keys=form1
714
715 [logger_root]
716 level=WARNING
717 handlers=hand1
718
719 [logger_parser]
720 level=DEBUG
721 handlers=
722 propagate=1
723 qualname=compiler.parser
724
725 [handler_hand1]
726 class=StreamHandler
727 level=NOTSET
728 formatter=form1
729 args=(sys.stdout,)
730
731 [formatter_form1]
732 format=%(levelname)s ++ %(message)s
733 datefmt=
734 """
735
Christian Heimes180510d2008-03-03 19:15:45 +0000736 # config2 has a subtle configuration error that should be reported
737 config2 = config1.replace("sys.stdout", "sys.stbout")
738
739 # config3 has a less subtle configuration error
740 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
741
742 # config4 specifies a custom formatter class to be loaded
743 config4 = """
744 [loggers]
745 keys=root
746
747 [handlers]
748 keys=hand1
749
750 [formatters]
751 keys=form1
752
753 [logger_root]
754 level=NOTSET
755 handlers=hand1
756
757 [handler_hand1]
758 class=StreamHandler
759 level=NOTSET
760 formatter=form1
761 args=(sys.stdout,)
762
763 [formatter_form1]
764 class=""" + __name__ + """.ExceptionFormatter
765 format=%(levelname)s:%(name)s:%(message)s
766 datefmt=
767 """
768
Georg Brandl3dbca812008-07-23 16:10:53 +0000769 # config5 specifies a custom handler class to be loaded
770 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
771
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000772 # config6 uses ', ' delimiters in the handlers and formatters sections
773 config6 = """
774 [loggers]
775 keys=root,parser
776
777 [handlers]
778 keys=hand1, hand2
779
780 [formatters]
781 keys=form1, form2
782
783 [logger_root]
784 level=WARNING
785 handlers=
786
787 [logger_parser]
788 level=DEBUG
789 handlers=hand1
790 propagate=1
791 qualname=compiler.parser
792
793 [handler_hand1]
794 class=StreamHandler
795 level=NOTSET
796 formatter=form1
797 args=(sys.stdout,)
798
799 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000800 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000801 level=NOTSET
802 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000803 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000804
805 [formatter_form1]
806 format=%(levelname)s ++ %(message)s
807 datefmt=
808
809 [formatter_form2]
810 format=%(message)s
811 datefmt=
812 """
813
Vinay Sajip3f84b072011-03-07 17:49:33 +0000814 # config7 adds a compiler logger.
815 config7 = """
816 [loggers]
817 keys=root,parser,compiler
818
819 [handlers]
820 keys=hand1
821
822 [formatters]
823 keys=form1
824
825 [logger_root]
826 level=WARNING
827 handlers=hand1
828
829 [logger_compiler]
830 level=DEBUG
831 handlers=
832 propagate=1
833 qualname=compiler
834
835 [logger_parser]
836 level=DEBUG
837 handlers=
838 propagate=1
839 qualname=compiler.parser
840
841 [handler_hand1]
842 class=StreamHandler
843 level=NOTSET
844 formatter=form1
845 args=(sys.stdout,)
846
847 [formatter_form1]
848 format=%(levelname)s ++ %(message)s
849 datefmt=
850 """
851
Christian Heimes180510d2008-03-03 19:15:45 +0000852 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000853 file = io.StringIO(textwrap.dedent(conf))
854 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000855
856 def test_config0_ok(self):
857 # A simple config file which overrides the default settings.
858 with captured_stdout() as output:
859 self.apply_config(self.config0)
860 logger = logging.getLogger()
861 # Won't output anything
862 logger.info(self.next_message())
863 # Outputs a message
864 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000865 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000866 ('ERROR', '2'),
867 ], stream=output)
868 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000869 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000870
Georg Brandl3dbca812008-07-23 16:10:53 +0000871 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000872 # A config file defining a sub-parser as well.
873 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000874 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000875 logger = logging.getLogger("compiler.parser")
876 # Both will output a message
877 logger.info(self.next_message())
878 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000879 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000880 ('INFO', '1'),
881 ('ERROR', '2'),
882 ], stream=output)
883 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000884 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000885
886 def test_config2_failure(self):
887 # A simple config file which overrides the default settings.
888 self.assertRaises(Exception, self.apply_config, self.config2)
889
890 def test_config3_failure(self):
891 # A simple config file which overrides the default settings.
892 self.assertRaises(Exception, self.apply_config, self.config3)
893
894 def test_config4_ok(self):
895 # A config file specifying a custom formatter class.
896 with captured_stdout() as output:
897 self.apply_config(self.config4)
898 logger = logging.getLogger()
899 try:
900 raise RuntimeError()
901 except RuntimeError:
902 logging.exception("just testing")
903 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000904 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000905 "ERROR:root:just testing\nGot a [RuntimeError]\n")
906 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000907 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000908
Georg Brandl3dbca812008-07-23 16:10:53 +0000909 def test_config5_ok(self):
910 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000911
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000912 def test_config6_ok(self):
913 self.test_config1_ok(config=self.config6)
914
Vinay Sajip3f84b072011-03-07 17:49:33 +0000915 def test_config7_ok(self):
916 with captured_stdout() as output:
917 self.apply_config(self.config1a)
918 logger = logging.getLogger("compiler.parser")
919 # See issue #11424. compiler-hyphenated sorts
920 # between compiler and compiler.xyz and this
921 # was preventing compiler.xyz from being included
922 # in the child loggers of compiler because of an
923 # overzealous loop termination condition.
924 hyphenated = logging.getLogger('compiler-hyphenated')
925 # All will output a message
926 logger.info(self.next_message())
927 logger.error(self.next_message())
928 hyphenated.critical(self.next_message())
929 self.assert_log_lines([
930 ('INFO', '1'),
931 ('ERROR', '2'),
932 ('CRITICAL', '3'),
933 ], stream=output)
934 # Original logger output is empty.
935 self.assert_log_lines([])
936 with captured_stdout() as output:
937 self.apply_config(self.config7)
938 logger = logging.getLogger("compiler.parser")
939 self.assertFalse(logger.disabled)
940 # Both will output a message
941 logger.info(self.next_message())
942 logger.error(self.next_message())
943 logger = logging.getLogger("compiler.lexer")
944 # Both will output a message
945 logger.info(self.next_message())
946 logger.error(self.next_message())
947 # Will not appear
948 hyphenated.critical(self.next_message())
949 self.assert_log_lines([
950 ('INFO', '4'),
951 ('ERROR', '5'),
952 ('INFO', '6'),
953 ('ERROR', '7'),
954 ], stream=output)
955 # Original logger output is empty.
956 self.assert_log_lines([])
957
Christian Heimes180510d2008-03-03 19:15:45 +0000958class LogRecordStreamHandler(StreamRequestHandler):
959
960 """Handler for a streaming logging request. It saves the log message in the
961 TCP server's 'log_output' attribute."""
962
963 TCP_LOG_END = "!!!END!!!"
964
965 def handle(self):
966 """Handle multiple requests - each expected to be of 4-byte length,
967 followed by the LogRecord in pickle format. Logs the record
968 according to whatever policy is configured locally."""
969 while True:
970 chunk = self.connection.recv(4)
971 if len(chunk) < 4:
972 break
973 slen = struct.unpack(">L", chunk)[0]
974 chunk = self.connection.recv(slen)
975 while len(chunk) < slen:
976 chunk = chunk + self.connection.recv(slen - len(chunk))
977 obj = self.unpickle(chunk)
978 record = logging.makeLogRecord(obj)
979 self.handle_log_record(record)
980
981 def unpickle(self, data):
982 return pickle.loads(data)
983
984 def handle_log_record(self, record):
985 # If the end-of-messages sentinel is seen, tell the server to
986 # terminate.
987 if self.TCP_LOG_END in record.msg:
988 self.server.abort = 1
989 return
990 self.server.log_output += record.msg + "\n"
991
Guido van Rossum376e6362003-04-25 14:22:00 +0000992
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000993class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000994
995 """A simple-minded TCP socket-based logging receiver suitable for test
996 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000997
998 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000999 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001000
1001 def __init__(self, host='localhost',
1002 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
1003 handler=LogRecordStreamHandler):
1004 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +00001005 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +00001006 self.timeout = 0.1
1007 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001008
1009 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +00001010 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +00001011 rd, wr, ex = select.select([self.socket.fileno()], [], [],
1012 self.timeout)
1013 if rd:
1014 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +00001015 # Notify the main thread that we're about to exit
1016 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +00001017 # close the listen socket
1018 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001019
Guido van Rossum2a1d5162003-01-21 21:05:22 +00001020
Victor Stinner45df8202010-04-28 22:31:17 +00001021@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +00001022class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001023
Christian Heimes180510d2008-03-03 19:15:45 +00001024 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001025
Christian Heimes180510d2008-03-03 19:15:45 +00001026 def setUp(self):
1027 """Set up a TCP server to receive log messages, and a SocketHandler
1028 pointing to that server's address and port."""
1029 BaseTest.setUp(self)
1030 self.tcpserver = LogRecordSocketReceiver(port=0)
1031 self.port = self.tcpserver.socket.getsockname()[1]
1032 self.threads = [
1033 threading.Thread(target=self.tcpserver.serve_until_stopped)]
1034 for thread in self.threads:
1035 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001036
Christian Heimes180510d2008-03-03 19:15:45 +00001037 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
1038 self.sock_hdlr.setFormatter(self.root_formatter)
1039 self.root_logger.removeHandler(self.root_logger.handlers[0])
1040 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001041
Christian Heimes180510d2008-03-03 19:15:45 +00001042 def tearDown(self):
1043 """Shutdown the TCP server."""
1044 try:
1045 self.tcpserver.abort = True
1046 del self.tcpserver
1047 self.root_logger.removeHandler(self.sock_hdlr)
1048 self.sock_hdlr.close()
1049 for thread in self.threads:
1050 thread.join(2.0)
1051 finally:
1052 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001053
Christian Heimes180510d2008-03-03 19:15:45 +00001054 def get_output(self):
1055 """Get the log output as received by the TCP server."""
1056 # Signal the TCP receiver and wait for it to terminate.
1057 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
1058 self.tcpserver.finished.wait(2.0)
1059 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001060
Christian Heimes180510d2008-03-03 19:15:45 +00001061 def test_output(self):
1062 # The log message sent to the SocketHandler is properly received.
1063 logger = logging.getLogger("tcp")
1064 logger.error("spam")
1065 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +00001066 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001067
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001068
Christian Heimes180510d2008-03-03 19:15:45 +00001069class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001070
Christian Heimes180510d2008-03-03 19:15:45 +00001071 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001072
Christian Heimes180510d2008-03-03 19:15:45 +00001073 def setUp(self):
1074 """Create a dict to remember potentially destroyed objects."""
1075 BaseTest.setUp(self)
1076 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001077
Christian Heimes180510d2008-03-03 19:15:45 +00001078 def _watch_for_survival(self, *args):
1079 """Watch the given objects for survival, by creating weakrefs to
1080 them."""
1081 for obj in args:
1082 key = id(obj), repr(obj)
1083 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001084
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001085 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001086 """Assert that all objects watched for survival have survived."""
1087 # Trigger cycle breaking.
1088 gc.collect()
1089 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001090 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001091 if ref() is None:
1092 dead.append(repr_)
1093 if dead:
1094 self.fail("%d objects should have survived "
1095 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001096
Christian Heimes180510d2008-03-03 19:15:45 +00001097 def test_persistent_loggers(self):
1098 # Logger objects are persistent and retain their configuration, even
1099 # if visible references are destroyed.
1100 self.root_logger.setLevel(logging.INFO)
1101 foo = logging.getLogger("foo")
1102 self._watch_for_survival(foo)
1103 foo.setLevel(logging.DEBUG)
1104 self.root_logger.debug(self.next_message())
1105 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001106 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001107 ('foo', 'DEBUG', '2'),
1108 ])
1109 del foo
1110 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001111 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001112 # foo has retained its settings.
1113 bar = logging.getLogger("foo")
1114 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001115 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001116 ('foo', 'DEBUG', '2'),
1117 ('foo', 'DEBUG', '3'),
1118 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001119
Benjamin Petersonf91df042009-02-13 02:50:59 +00001120
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001121class EncodingTest(BaseTest):
1122 def test_encoding_plain_file(self):
1123 # In Python 2.x, a plain file object is treated as having no encoding.
1124 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001125 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1126 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001127 # the non-ascii data we write to the log.
1128 data = "foo\x80"
1129 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001130 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001131 log.addHandler(handler)
1132 try:
1133 # write non-ascii data to the log.
1134 log.warning(data)
1135 finally:
1136 log.removeHandler(handler)
1137 handler.close()
1138 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001139 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001140 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001141 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001142 finally:
1143 f.close()
1144 finally:
1145 if os.path.isfile(fn):
1146 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001147
Benjamin Petersonf91df042009-02-13 02:50:59 +00001148 def test_encoding_cyrillic_unicode(self):
1149 log = logging.getLogger("test")
1150 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1151 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1152 #Ensure it's written in a Cyrillic encoding
1153 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001154 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001155 stream = io.BytesIO()
1156 writer = writer_class(stream, 'strict')
1157 handler = logging.StreamHandler(writer)
1158 log.addHandler(handler)
1159 try:
1160 log.warning(message)
1161 finally:
1162 log.removeHandler(handler)
1163 handler.close()
1164 # check we wrote exactly those bytes, ignoring trailing \n etc
1165 s = stream.getvalue()
1166 #Compare against what the data should be when encoded in CP-1251
1167 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1168
1169
Georg Brandlf9734072008-12-07 15:30:06 +00001170class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001171
Georg Brandlf9734072008-12-07 15:30:06 +00001172 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001173 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001174 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001175 self.addCleanup(lambda: logging.captureWarnings(False))
1176 warnings.filterwarnings("always", category=UserWarning)
1177 stream = io.StringIO()
1178 h = logging.StreamHandler(stream)
1179 logger = logging.getLogger("py.warnings")
1180 logger.addHandler(h)
1181 warnings.warn("I'm warning you...")
1182 logger.removeHandler(h)
1183 s = stream.getvalue()
1184 h.close()
1185 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001186
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001187 #See if an explicit file uses the original implementation
1188 a_file = io.StringIO()
1189 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1190 a_file, "Dummy line")
1191 s = a_file.getvalue()
1192 a_file.close()
1193 self.assertEqual(s,
1194 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1195
1196 def test_warnings_no_handlers(self):
1197 with warnings.catch_warnings():
1198 logging.captureWarnings(True)
1199 self.addCleanup(lambda: logging.captureWarnings(False))
1200
1201 # confirm our assumption: no loggers are set
1202 logger = logging.getLogger("py.warnings")
1203 assert logger.handlers == []
1204
1205 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1206 self.assertTrue(len(logger.handlers) == 1)
1207 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001208
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001209
1210def formatFunc(format, datefmt=None):
1211 return logging.Formatter(format, datefmt)
1212
1213def handlerFunc():
1214 return logging.StreamHandler()
1215
1216class CustomHandler(logging.StreamHandler):
1217 pass
1218
1219class ConfigDictTest(BaseTest):
1220
1221 """Reading logging config from a dictionary."""
1222
1223 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1224
1225 # config0 is a standard configuration.
1226 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001227 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001228 'formatters': {
1229 'form1' : {
1230 'format' : '%(levelname)s ++ %(message)s',
1231 },
1232 },
1233 'handlers' : {
1234 'hand1' : {
1235 'class' : 'logging.StreamHandler',
1236 'formatter' : 'form1',
1237 'level' : 'NOTSET',
1238 'stream' : 'ext://sys.stdout',
1239 },
1240 },
1241 'root' : {
1242 'level' : 'WARNING',
1243 'handlers' : ['hand1'],
1244 },
1245 }
1246
1247 # config1 adds a little to the standard configuration.
1248 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001249 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001250 'formatters': {
1251 'form1' : {
1252 'format' : '%(levelname)s ++ %(message)s',
1253 },
1254 },
1255 'handlers' : {
1256 'hand1' : {
1257 'class' : 'logging.StreamHandler',
1258 'formatter' : 'form1',
1259 'level' : 'NOTSET',
1260 'stream' : 'ext://sys.stdout',
1261 },
1262 },
1263 'loggers' : {
1264 'compiler.parser' : {
1265 'level' : 'DEBUG',
1266 'handlers' : ['hand1'],
1267 },
1268 },
1269 'root' : {
1270 'level' : 'WARNING',
1271 },
1272 }
1273
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001274 # config1a moves the handler to the root. Used with config8a
1275 config1a = {
1276 'version': 1,
1277 'formatters': {
1278 'form1' : {
1279 'format' : '%(levelname)s ++ %(message)s',
1280 },
1281 },
1282 'handlers' : {
1283 'hand1' : {
1284 'class' : 'logging.StreamHandler',
1285 'formatter' : 'form1',
1286 'level' : 'NOTSET',
1287 'stream' : 'ext://sys.stdout',
1288 },
1289 },
1290 'loggers' : {
1291 'compiler.parser' : {
1292 'level' : 'DEBUG',
1293 },
1294 },
1295 'root' : {
1296 'level' : 'WARNING',
1297 'handlers' : ['hand1'],
1298 },
1299 }
1300
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001301 # config2 has a subtle configuration error that should be reported
1302 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001303 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001304 'formatters': {
1305 'form1' : {
1306 'format' : '%(levelname)s ++ %(message)s',
1307 },
1308 },
1309 'handlers' : {
1310 'hand1' : {
1311 'class' : 'logging.StreamHandler',
1312 'formatter' : 'form1',
1313 'level' : 'NOTSET',
1314 'stream' : 'ext://sys.stdbout',
1315 },
1316 },
1317 'loggers' : {
1318 'compiler.parser' : {
1319 'level' : 'DEBUG',
1320 'handlers' : ['hand1'],
1321 },
1322 },
1323 'root' : {
1324 'level' : 'WARNING',
1325 },
1326 }
1327
1328 #As config1 but with a misspelt level on a handler
1329 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001330 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001331 'formatters': {
1332 'form1' : {
1333 'format' : '%(levelname)s ++ %(message)s',
1334 },
1335 },
1336 'handlers' : {
1337 'hand1' : {
1338 'class' : 'logging.StreamHandler',
1339 'formatter' : 'form1',
1340 'level' : 'NTOSET',
1341 'stream' : 'ext://sys.stdout',
1342 },
1343 },
1344 'loggers' : {
1345 'compiler.parser' : {
1346 'level' : 'DEBUG',
1347 'handlers' : ['hand1'],
1348 },
1349 },
1350 'root' : {
1351 'level' : 'WARNING',
1352 },
1353 }
1354
1355
1356 #As config1 but with a misspelt level on a logger
1357 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001358 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001359 'formatters': {
1360 'form1' : {
1361 'format' : '%(levelname)s ++ %(message)s',
1362 },
1363 },
1364 'handlers' : {
1365 'hand1' : {
1366 'class' : 'logging.StreamHandler',
1367 'formatter' : 'form1',
1368 'level' : 'NOTSET',
1369 'stream' : 'ext://sys.stdout',
1370 },
1371 },
1372 'loggers' : {
1373 'compiler.parser' : {
1374 'level' : 'DEBUG',
1375 'handlers' : ['hand1'],
1376 },
1377 },
1378 'root' : {
1379 'level' : 'WRANING',
1380 },
1381 }
1382
1383 # config3 has a less subtle configuration error
1384 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001385 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001386 'formatters': {
1387 'form1' : {
1388 'format' : '%(levelname)s ++ %(message)s',
1389 },
1390 },
1391 'handlers' : {
1392 'hand1' : {
1393 'class' : 'logging.StreamHandler',
1394 'formatter' : 'misspelled_name',
1395 'level' : 'NOTSET',
1396 'stream' : 'ext://sys.stdout',
1397 },
1398 },
1399 'loggers' : {
1400 'compiler.parser' : {
1401 'level' : 'DEBUG',
1402 'handlers' : ['hand1'],
1403 },
1404 },
1405 'root' : {
1406 'level' : 'WARNING',
1407 },
1408 }
1409
1410 # config4 specifies a custom formatter class to be loaded
1411 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001412 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001413 'formatters': {
1414 'form1' : {
1415 '()' : __name__ + '.ExceptionFormatter',
1416 'format' : '%(levelname)s:%(name)s:%(message)s',
1417 },
1418 },
1419 'handlers' : {
1420 'hand1' : {
1421 'class' : 'logging.StreamHandler',
1422 'formatter' : 'form1',
1423 'level' : 'NOTSET',
1424 'stream' : 'ext://sys.stdout',
1425 },
1426 },
1427 'root' : {
1428 'level' : 'NOTSET',
1429 'handlers' : ['hand1'],
1430 },
1431 }
1432
1433 # As config4 but using an actual callable rather than a string
1434 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001435 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001436 'formatters': {
1437 'form1' : {
1438 '()' : ExceptionFormatter,
1439 'format' : '%(levelname)s:%(name)s:%(message)s',
1440 },
1441 'form2' : {
1442 '()' : __name__ + '.formatFunc',
1443 'format' : '%(levelname)s:%(name)s:%(message)s',
1444 },
1445 'form3' : {
1446 '()' : formatFunc,
1447 'format' : '%(levelname)s:%(name)s:%(message)s',
1448 },
1449 },
1450 'handlers' : {
1451 'hand1' : {
1452 'class' : 'logging.StreamHandler',
1453 'formatter' : 'form1',
1454 'level' : 'NOTSET',
1455 'stream' : 'ext://sys.stdout',
1456 },
1457 'hand2' : {
1458 '()' : handlerFunc,
1459 },
1460 },
1461 'root' : {
1462 'level' : 'NOTSET',
1463 'handlers' : ['hand1'],
1464 },
1465 }
1466
1467 # config5 specifies a custom handler class to be loaded
1468 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001469 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001470 'formatters': {
1471 'form1' : {
1472 'format' : '%(levelname)s ++ %(message)s',
1473 },
1474 },
1475 'handlers' : {
1476 'hand1' : {
1477 'class' : __name__ + '.CustomHandler',
1478 'formatter' : 'form1',
1479 'level' : 'NOTSET',
1480 'stream' : 'ext://sys.stdout',
1481 },
1482 },
1483 'loggers' : {
1484 'compiler.parser' : {
1485 'level' : 'DEBUG',
1486 'handlers' : ['hand1'],
1487 },
1488 },
1489 'root' : {
1490 'level' : 'WARNING',
1491 },
1492 }
1493
1494 # config6 specifies a custom handler class to be loaded
1495 # but has bad arguments
1496 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001497 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001498 'formatters': {
1499 'form1' : {
1500 'format' : '%(levelname)s ++ %(message)s',
1501 },
1502 },
1503 'handlers' : {
1504 'hand1' : {
1505 'class' : __name__ + '.CustomHandler',
1506 'formatter' : 'form1',
1507 'level' : 'NOTSET',
1508 'stream' : 'ext://sys.stdout',
1509 '9' : 'invalid parameter name',
1510 },
1511 },
1512 'loggers' : {
1513 'compiler.parser' : {
1514 'level' : 'DEBUG',
1515 'handlers' : ['hand1'],
1516 },
1517 },
1518 'root' : {
1519 'level' : 'WARNING',
1520 },
1521 }
1522
1523 #config 7 does not define compiler.parser but defines compiler.lexer
1524 #so compiler.parser should be disabled after applying it
1525 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001526 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001527 'formatters': {
1528 'form1' : {
1529 'format' : '%(levelname)s ++ %(message)s',
1530 },
1531 },
1532 'handlers' : {
1533 'hand1' : {
1534 'class' : 'logging.StreamHandler',
1535 'formatter' : 'form1',
1536 'level' : 'NOTSET',
1537 'stream' : 'ext://sys.stdout',
1538 },
1539 },
1540 'loggers' : {
1541 'compiler.lexer' : {
1542 'level' : 'DEBUG',
1543 'handlers' : ['hand1'],
1544 },
1545 },
1546 'root' : {
1547 'level' : 'WARNING',
1548 },
1549 }
1550
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001551 # config8 defines both compiler and compiler.lexer
1552 # so compiler.parser should not be disabled (since
1553 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001554 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001555 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001556 'disable_existing_loggers' : False,
1557 'formatters': {
1558 'form1' : {
1559 'format' : '%(levelname)s ++ %(message)s',
1560 },
1561 },
1562 'handlers' : {
1563 'hand1' : {
1564 'class' : 'logging.StreamHandler',
1565 'formatter' : 'form1',
1566 'level' : 'NOTSET',
1567 'stream' : 'ext://sys.stdout',
1568 },
1569 },
1570 'loggers' : {
1571 'compiler' : {
1572 'level' : 'DEBUG',
1573 'handlers' : ['hand1'],
1574 },
1575 'compiler.lexer' : {
1576 },
1577 },
1578 'root' : {
1579 'level' : 'WARNING',
1580 },
1581 }
1582
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001583 # config8a disables existing loggers
1584 config8a = {
1585 'version': 1,
1586 'disable_existing_loggers' : True,
1587 'formatters': {
1588 'form1' : {
1589 'format' : '%(levelname)s ++ %(message)s',
1590 },
1591 },
1592 'handlers' : {
1593 'hand1' : {
1594 'class' : 'logging.StreamHandler',
1595 'formatter' : 'form1',
1596 'level' : 'NOTSET',
1597 'stream' : 'ext://sys.stdout',
1598 },
1599 },
1600 'loggers' : {
1601 'compiler' : {
1602 'level' : 'DEBUG',
1603 'handlers' : ['hand1'],
1604 },
1605 'compiler.lexer' : {
1606 },
1607 },
1608 'root' : {
1609 'level' : 'WARNING',
1610 },
1611 }
1612
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001613 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001614 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001615 'formatters': {
1616 'form1' : {
1617 'format' : '%(levelname)s ++ %(message)s',
1618 },
1619 },
1620 'handlers' : {
1621 'hand1' : {
1622 'class' : 'logging.StreamHandler',
1623 'formatter' : 'form1',
1624 'level' : 'WARNING',
1625 'stream' : 'ext://sys.stdout',
1626 },
1627 },
1628 'loggers' : {
1629 'compiler.parser' : {
1630 'level' : 'WARNING',
1631 'handlers' : ['hand1'],
1632 },
1633 },
1634 'root' : {
1635 'level' : 'NOTSET',
1636 },
1637 }
1638
1639 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001640 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001641 'incremental' : True,
1642 'handlers' : {
1643 'hand1' : {
1644 'level' : 'WARNING',
1645 },
1646 },
1647 'loggers' : {
1648 'compiler.parser' : {
1649 'level' : 'INFO',
1650 },
1651 },
1652 }
1653
1654 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001655 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001656 'incremental' : True,
1657 'handlers' : {
1658 'hand1' : {
1659 'level' : 'INFO',
1660 },
1661 },
1662 'loggers' : {
1663 'compiler.parser' : {
1664 'level' : 'INFO',
1665 },
1666 },
1667 }
1668
1669 #As config1 but with a filter added
1670 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001671 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001672 'formatters': {
1673 'form1' : {
1674 'format' : '%(levelname)s ++ %(message)s',
1675 },
1676 },
1677 'filters' : {
1678 'filt1' : {
1679 'name' : 'compiler.parser',
1680 },
1681 },
1682 'handlers' : {
1683 'hand1' : {
1684 'class' : 'logging.StreamHandler',
1685 'formatter' : 'form1',
1686 'level' : 'NOTSET',
1687 'stream' : 'ext://sys.stdout',
1688 'filters' : ['filt1'],
1689 },
1690 },
1691 'loggers' : {
1692 'compiler.parser' : {
1693 'level' : 'DEBUG',
1694 'filters' : ['filt1'],
1695 },
1696 },
1697 'root' : {
1698 'level' : 'WARNING',
1699 'handlers' : ['hand1'],
1700 },
1701 }
1702
1703 #As config1 but using cfg:// references
1704 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001705 'version': 1,
1706 'true_formatters': {
1707 'form1' : {
1708 'format' : '%(levelname)s ++ %(message)s',
1709 },
1710 },
1711 'handler_configs': {
1712 'hand1' : {
1713 'class' : 'logging.StreamHandler',
1714 'formatter' : 'form1',
1715 'level' : 'NOTSET',
1716 'stream' : 'ext://sys.stdout',
1717 },
1718 },
1719 'formatters' : 'cfg://true_formatters',
1720 'handlers' : {
1721 'hand1' : 'cfg://handler_configs[hand1]',
1722 },
1723 'loggers' : {
1724 'compiler.parser' : {
1725 'level' : 'DEBUG',
1726 'handlers' : ['hand1'],
1727 },
1728 },
1729 'root' : {
1730 'level' : 'WARNING',
1731 },
1732 }
1733
1734 #As config11 but missing the version key
1735 config12 = {
1736 'true_formatters': {
1737 'form1' : {
1738 'format' : '%(levelname)s ++ %(message)s',
1739 },
1740 },
1741 'handler_configs': {
1742 'hand1' : {
1743 'class' : 'logging.StreamHandler',
1744 'formatter' : 'form1',
1745 'level' : 'NOTSET',
1746 'stream' : 'ext://sys.stdout',
1747 },
1748 },
1749 'formatters' : 'cfg://true_formatters',
1750 'handlers' : {
1751 'hand1' : 'cfg://handler_configs[hand1]',
1752 },
1753 'loggers' : {
1754 'compiler.parser' : {
1755 'level' : 'DEBUG',
1756 'handlers' : ['hand1'],
1757 },
1758 },
1759 'root' : {
1760 'level' : 'WARNING',
1761 },
1762 }
1763
1764 #As config11 but using an unsupported version
1765 config13 = {
1766 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001767 'true_formatters': {
1768 'form1' : {
1769 'format' : '%(levelname)s ++ %(message)s',
1770 },
1771 },
1772 'handler_configs': {
1773 'hand1' : {
1774 'class' : 'logging.StreamHandler',
1775 'formatter' : 'form1',
1776 'level' : 'NOTSET',
1777 'stream' : 'ext://sys.stdout',
1778 },
1779 },
1780 'formatters' : 'cfg://true_formatters',
1781 'handlers' : {
1782 'hand1' : 'cfg://handler_configs[hand1]',
1783 },
1784 'loggers' : {
1785 'compiler.parser' : {
1786 'level' : 'DEBUG',
1787 'handlers' : ['hand1'],
1788 },
1789 },
1790 'root' : {
1791 'level' : 'WARNING',
1792 },
1793 }
1794
1795 def apply_config(self, conf):
1796 logging.config.dictConfig(conf)
1797
1798 def test_config0_ok(self):
1799 # A simple config which overrides the default settings.
1800 with captured_stdout() as output:
1801 self.apply_config(self.config0)
1802 logger = logging.getLogger()
1803 # Won't output anything
1804 logger.info(self.next_message())
1805 # Outputs a message
1806 logger.error(self.next_message())
1807 self.assert_log_lines([
1808 ('ERROR', '2'),
1809 ], stream=output)
1810 # Original logger output is empty.
1811 self.assert_log_lines([])
1812
1813 def test_config1_ok(self, config=config1):
1814 # A config defining a sub-parser as well.
1815 with captured_stdout() as output:
1816 self.apply_config(config)
1817 logger = logging.getLogger("compiler.parser")
1818 # Both will output a message
1819 logger.info(self.next_message())
1820 logger.error(self.next_message())
1821 self.assert_log_lines([
1822 ('INFO', '1'),
1823 ('ERROR', '2'),
1824 ], stream=output)
1825 # Original logger output is empty.
1826 self.assert_log_lines([])
1827
1828 def test_config2_failure(self):
1829 # A simple config which overrides the default settings.
1830 self.assertRaises(Exception, self.apply_config, self.config2)
1831
1832 def test_config2a_failure(self):
1833 # A simple config which overrides the default settings.
1834 self.assertRaises(Exception, self.apply_config, self.config2a)
1835
1836 def test_config2b_failure(self):
1837 # A simple config which overrides the default settings.
1838 self.assertRaises(Exception, self.apply_config, self.config2b)
1839
1840 def test_config3_failure(self):
1841 # A simple config which overrides the default settings.
1842 self.assertRaises(Exception, self.apply_config, self.config3)
1843
1844 def test_config4_ok(self):
1845 # A config specifying a custom formatter class.
1846 with captured_stdout() as output:
1847 self.apply_config(self.config4)
1848 #logger = logging.getLogger()
1849 try:
1850 raise RuntimeError()
1851 except RuntimeError:
1852 logging.exception("just testing")
1853 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001854 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001855 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1856 # Original logger output is empty
1857 self.assert_log_lines([])
1858
1859 def test_config4a_ok(self):
1860 # A config specifying a custom formatter class.
1861 with captured_stdout() as output:
1862 self.apply_config(self.config4a)
1863 #logger = logging.getLogger()
1864 try:
1865 raise RuntimeError()
1866 except RuntimeError:
1867 logging.exception("just testing")
1868 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001869 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001870 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1871 # Original logger output is empty
1872 self.assert_log_lines([])
1873
1874 def test_config5_ok(self):
1875 self.test_config1_ok(config=self.config5)
1876
1877 def test_config6_failure(self):
1878 self.assertRaises(Exception, self.apply_config, self.config6)
1879
1880 def test_config7_ok(self):
1881 with captured_stdout() as output:
1882 self.apply_config(self.config1)
1883 logger = logging.getLogger("compiler.parser")
1884 # Both will output a message
1885 logger.info(self.next_message())
1886 logger.error(self.next_message())
1887 self.assert_log_lines([
1888 ('INFO', '1'),
1889 ('ERROR', '2'),
1890 ], stream=output)
1891 # Original logger output is empty.
1892 self.assert_log_lines([])
1893 with captured_stdout() as output:
1894 self.apply_config(self.config7)
1895 logger = logging.getLogger("compiler.parser")
1896 self.assertTrue(logger.disabled)
1897 logger = logging.getLogger("compiler.lexer")
1898 # Both will output a message
1899 logger.info(self.next_message())
1900 logger.error(self.next_message())
1901 self.assert_log_lines([
1902 ('INFO', '3'),
1903 ('ERROR', '4'),
1904 ], stream=output)
1905 # Original logger output is empty.
1906 self.assert_log_lines([])
1907
1908 #Same as test_config_7_ok but don't disable old loggers.
1909 def test_config_8_ok(self):
1910 with captured_stdout() as output:
1911 self.apply_config(self.config1)
1912 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001913 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001914 logger.info(self.next_message())
1915 logger.error(self.next_message())
1916 self.assert_log_lines([
1917 ('INFO', '1'),
1918 ('ERROR', '2'),
1919 ], stream=output)
1920 # Original logger output is empty.
1921 self.assert_log_lines([])
1922 with captured_stdout() as output:
1923 self.apply_config(self.config8)
1924 logger = logging.getLogger("compiler.parser")
1925 self.assertFalse(logger.disabled)
1926 # Both will output a message
1927 logger.info(self.next_message())
1928 logger.error(self.next_message())
1929 logger = logging.getLogger("compiler.lexer")
1930 # Both will output a message
1931 logger.info(self.next_message())
1932 logger.error(self.next_message())
1933 self.assert_log_lines([
1934 ('INFO', '3'),
1935 ('ERROR', '4'),
1936 ('INFO', '5'),
1937 ('ERROR', '6'),
1938 ], stream=output)
1939 # Original logger output is empty.
1940 self.assert_log_lines([])
1941
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001942 def test_config_8a_ok(self):
1943 with captured_stdout() as output:
1944 self.apply_config(self.config1a)
1945 logger = logging.getLogger("compiler.parser")
1946 # See issue #11424. compiler-hyphenated sorts
1947 # between compiler and compiler.xyz and this
1948 # was preventing compiler.xyz from being included
1949 # in the child loggers of compiler because of an
1950 # overzealous loop termination condition.
1951 hyphenated = logging.getLogger('compiler-hyphenated')
1952 # All will output a message
1953 logger.info(self.next_message())
1954 logger.error(self.next_message())
1955 hyphenated.critical(self.next_message())
1956 self.assert_log_lines([
1957 ('INFO', '1'),
1958 ('ERROR', '2'),
1959 ('CRITICAL', '3'),
1960 ], stream=output)
1961 # Original logger output is empty.
1962 self.assert_log_lines([])
1963 with captured_stdout() as output:
1964 self.apply_config(self.config8a)
1965 logger = logging.getLogger("compiler.parser")
1966 self.assertFalse(logger.disabled)
1967 # Both will output a message
1968 logger.info(self.next_message())
1969 logger.error(self.next_message())
1970 logger = logging.getLogger("compiler.lexer")
1971 # Both will output a message
1972 logger.info(self.next_message())
1973 logger.error(self.next_message())
1974 # Will not appear
1975 hyphenated.critical(self.next_message())
1976 self.assert_log_lines([
1977 ('INFO', '4'),
1978 ('ERROR', '5'),
1979 ('INFO', '6'),
1980 ('ERROR', '7'),
1981 ], stream=output)
1982 # Original logger output is empty.
1983 self.assert_log_lines([])
1984
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001985 def test_config_9_ok(self):
1986 with captured_stdout() as output:
1987 self.apply_config(self.config9)
1988 logger = logging.getLogger("compiler.parser")
1989 #Nothing will be output since both handler and logger are set to WARNING
1990 logger.info(self.next_message())
1991 self.assert_log_lines([], stream=output)
1992 self.apply_config(self.config9a)
1993 #Nothing will be output since both handler is still set to WARNING
1994 logger.info(self.next_message())
1995 self.assert_log_lines([], stream=output)
1996 self.apply_config(self.config9b)
1997 #Message should now be output
1998 logger.info(self.next_message())
1999 self.assert_log_lines([
2000 ('INFO', '3'),
2001 ], stream=output)
2002
2003 def test_config_10_ok(self):
2004 with captured_stdout() as output:
2005 self.apply_config(self.config10)
2006 logger = logging.getLogger("compiler.parser")
2007 logger.warning(self.next_message())
2008 logger = logging.getLogger('compiler')
2009 #Not output, because filtered
2010 logger.warning(self.next_message())
2011 logger = logging.getLogger('compiler.lexer')
2012 #Not output, because filtered
2013 logger.warning(self.next_message())
2014 logger = logging.getLogger("compiler.parser.codegen")
2015 #Output, as not filtered
2016 logger.error(self.next_message())
2017 self.assert_log_lines([
2018 ('WARNING', '1'),
2019 ('ERROR', '4'),
2020 ], stream=output)
2021
2022 def test_config11_ok(self):
2023 self.test_config1_ok(self.config11)
2024
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002025 def test_config12_failure(self):
2026 self.assertRaises(Exception, self.apply_config, self.config12)
2027
2028 def test_config13_failure(self):
2029 self.assertRaises(Exception, self.apply_config, self.config13)
2030
Victor Stinner45df8202010-04-28 22:31:17 +00002031 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002032 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00002033 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00002034 # Ask for a randomly assigned port (by using port 0)
2035 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002036 t.start()
2037 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00002038 # Now get the port allocated
2039 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002040 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002041 try:
2042 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2043 sock.settimeout(2.0)
2044 sock.connect(('localhost', port))
2045
2046 slen = struct.pack('>L', len(text))
2047 s = slen + text
2048 sentsofar = 0
2049 left = len(s)
2050 while left > 0:
2051 sent = sock.send(s[sentsofar:])
2052 sentsofar += sent
2053 left -= sent
2054 sock.close()
2055 finally:
2056 t.ready.wait(2.0)
2057 logging.config.stopListening()
2058 t.join(2.0)
2059
2060 def test_listen_config_10_ok(self):
2061 with captured_stdout() as output:
2062 self.setup_via_listener(json.dumps(self.config10))
2063 logger = logging.getLogger("compiler.parser")
2064 logger.warning(self.next_message())
2065 logger = logging.getLogger('compiler')
2066 #Not output, because filtered
2067 logger.warning(self.next_message())
2068 logger = logging.getLogger('compiler.lexer')
2069 #Not output, because filtered
2070 logger.warning(self.next_message())
2071 logger = logging.getLogger("compiler.parser.codegen")
2072 #Output, as not filtered
2073 logger.error(self.next_message())
2074 self.assert_log_lines([
2075 ('WARNING', '1'),
2076 ('ERROR', '4'),
2077 ], stream=output)
2078
2079 def test_listen_config_1_ok(self):
2080 with captured_stdout() as output:
2081 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2082 logger = logging.getLogger("compiler.parser")
2083 # Both will output a message
2084 logger.info(self.next_message())
2085 logger.error(self.next_message())
2086 self.assert_log_lines([
2087 ('INFO', '1'),
2088 ('ERROR', '2'),
2089 ], stream=output)
2090 # Original logger output is empty.
2091 self.assert_log_lines([])
2092
Vinay Sajip373baef2011-04-26 20:05:24 +01002093 def test_baseconfig(self):
2094 d = {
2095 'atuple': (1, 2, 3),
2096 'alist': ['a', 'b', 'c'],
2097 'adict': {'d': 'e', 'f': 3 },
2098 'nest1': ('g', ('h', 'i'), 'j'),
2099 'nest2': ['k', ['l', 'm'], 'n'],
2100 'nest3': ['o', 'cfg://alist', 'p'],
2101 }
2102 bc = logging.config.BaseConfigurator(d)
2103 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2104 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2105 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2106 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2107 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2108 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2109 v = bc.convert('cfg://nest3')
2110 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2111 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2112 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2113 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002114
2115class ManagerTest(BaseTest):
2116 def test_manager_loggerclass(self):
2117 logged = []
2118
2119 class MyLogger(logging.Logger):
2120 def _log(self, level, msg, args, exc_info=None, extra=None):
2121 logged.append(msg)
2122
2123 man = logging.Manager(None)
2124 self.assertRaises(TypeError, man.setLoggerClass, int)
2125 man.setLoggerClass(MyLogger)
2126 logger = man.getLogger('test')
2127 logger.warning('should appear in logged')
2128 logging.warning('should not appear in logged')
2129
2130 self.assertEqual(logged, ['should appear in logged'])
2131
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002132 def test_set_log_record_factory(self):
2133 man = logging.Manager(None)
2134 expected = object()
2135 man.setLogRecordFactory(expected)
2136 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002137
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002138class ChildLoggerTest(BaseTest):
2139 def test_child_loggers(self):
2140 r = logging.getLogger()
2141 l1 = logging.getLogger('abc')
2142 l2 = logging.getLogger('def.ghi')
2143 c1 = r.getChild('xyz')
2144 c2 = r.getChild('uvw.xyz')
2145 self.assertTrue(c1 is logging.getLogger('xyz'))
2146 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2147 c1 = l1.getChild('def')
2148 c2 = c1.getChild('ghi')
2149 c3 = l1.getChild('def.ghi')
2150 self.assertTrue(c1 is logging.getLogger('abc.def'))
2151 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2152 self.assertTrue(c2 is c3)
2153
2154
Vinay Sajip6fac8172010-10-19 20:44:14 +00002155class DerivedLogRecord(logging.LogRecord):
2156 pass
2157
Vinay Sajip61561522010-12-03 11:50:38 +00002158class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002159
2160 def setUp(self):
2161 class CheckingFilter(logging.Filter):
2162 def __init__(self, cls):
2163 self.cls = cls
2164
2165 def filter(self, record):
2166 t = type(record)
2167 if t is not self.cls:
2168 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2169 self.cls)
2170 raise TypeError(msg)
2171 return True
2172
2173 BaseTest.setUp(self)
2174 self.filter = CheckingFilter(DerivedLogRecord)
2175 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002176 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002177
2178 def tearDown(self):
2179 self.root_logger.removeFilter(self.filter)
2180 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002181 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002182
2183 def test_logrecord_class(self):
2184 self.assertRaises(TypeError, self.root_logger.warning,
2185 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002186 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002187 self.root_logger.error(self.next_message())
2188 self.assert_log_lines([
2189 ('root', 'ERROR', '2'),
2190 ])
2191
2192
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002193class QueueHandlerTest(BaseTest):
2194 # Do not bother with a logger name group.
2195 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2196
2197 def setUp(self):
2198 BaseTest.setUp(self)
2199 self.queue = queue.Queue(-1)
2200 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2201 self.que_logger = logging.getLogger('que')
2202 self.que_logger.propagate = False
2203 self.que_logger.setLevel(logging.WARNING)
2204 self.que_logger.addHandler(self.que_hdlr)
2205
2206 def tearDown(self):
2207 self.que_hdlr.close()
2208 BaseTest.tearDown(self)
2209
2210 def test_queue_handler(self):
2211 self.que_logger.debug(self.next_message())
2212 self.assertRaises(queue.Empty, self.queue.get_nowait)
2213 self.que_logger.info(self.next_message())
2214 self.assertRaises(queue.Empty, self.queue.get_nowait)
2215 msg = self.next_message()
2216 self.que_logger.warning(msg)
2217 data = self.queue.get_nowait()
2218 self.assertTrue(isinstance(data, logging.LogRecord))
2219 self.assertEqual(data.name, self.que_logger.name)
2220 self.assertEqual((data.msg, data.args), (msg, None))
2221
Vinay Sajipe723e962011-04-15 22:27:17 +01002222 def test_queue_listener(self):
2223 handler = TestHandler(Matcher())
2224 listener = logging.handlers.QueueListener(self.queue, handler)
2225 listener.start()
2226 try:
2227 self.que_logger.warning(self.next_message())
2228 self.que_logger.error(self.next_message())
2229 self.que_logger.critical(self.next_message())
2230 finally:
2231 listener.stop()
2232 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2233 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2234 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2235
Vinay Sajip37eb3382011-04-26 20:26:41 +01002236ZERO = datetime.timedelta(0)
2237
2238class UTC(datetime.tzinfo):
2239 def utcoffset(self, dt):
2240 return ZERO
2241
2242 dst = utcoffset
2243
2244 def tzname(self, dt):
2245 return 'UTC'
2246
2247utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002248
Vinay Sajipa39c5712010-10-25 13:57:39 +00002249class FormatterTest(unittest.TestCase):
2250 def setUp(self):
2251 self.common = {
2252 'name': 'formatter.test',
2253 'level': logging.DEBUG,
2254 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2255 'lineno': 42,
2256 'exc_info': None,
2257 'func': None,
2258 'msg': 'Message with %d %s',
2259 'args': (2, 'placeholders'),
2260 }
2261 self.variants = {
2262 }
2263
2264 def get_record(self, name=None):
2265 result = dict(self.common)
2266 if name is not None:
2267 result.update(self.variants[name])
2268 return logging.makeLogRecord(result)
2269
2270 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002271 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002272 r = self.get_record()
2273 f = logging.Formatter('${%(message)s}')
2274 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2275 f = logging.Formatter('%(random)s')
2276 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002277 self.assertFalse(f.usesTime())
2278 f = logging.Formatter('%(asctime)s')
2279 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002280 f = logging.Formatter('%(asctime)-15s')
2281 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002282 f = logging.Formatter('asctime')
2283 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002284
2285 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002286 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002287 r = self.get_record()
2288 f = logging.Formatter('$%{message}%$', style='{')
2289 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2290 f = logging.Formatter('{random}', style='{')
2291 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002292 self.assertFalse(f.usesTime())
2293 f = logging.Formatter('{asctime}', style='{')
2294 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002295 f = logging.Formatter('{asctime!s:15}', style='{')
2296 self.assertTrue(f.usesTime())
2297 f = logging.Formatter('{asctime:15}', style='{')
2298 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002299 f = logging.Formatter('asctime', style='{')
2300 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002301
2302 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002303 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002304 r = self.get_record()
2305 f = logging.Formatter('$message', style='$')
2306 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2307 f = logging.Formatter('$$%${message}%$$', style='$')
2308 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2309 f = logging.Formatter('${random}', style='$')
2310 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002311 self.assertFalse(f.usesTime())
2312 f = logging.Formatter('${asctime}', style='$')
2313 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002314 f = logging.Formatter('${asctime', style='$')
2315 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002316 f = logging.Formatter('$asctime', style='$')
2317 self.assertTrue(f.usesTime())
2318 f = logging.Formatter('asctime', style='$')
2319 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002320
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002321 def test_invalid_style(self):
2322 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2323
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002324 def test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002325 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002326 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
Vinay Sajip86a96ce2011-04-27 08:30:30 +01002327 r.created = time.mktime(dt.timetuple()) - time.timezone
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002328 r.msecs = 123
2329 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002330 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002331 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2332 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002333 f.format(r)
2334 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2335
2336class TestBufferingFormatter(logging.BufferingFormatter):
2337 def formatHeader(self, records):
2338 return '[(%d)' % len(records)
2339
2340 def formatFooter(self, records):
2341 return '(%d)]' % len(records)
2342
2343class BufferingFormatterTest(unittest.TestCase):
2344 def setUp(self):
2345 self.records = [
2346 logging.makeLogRecord({'msg': 'one'}),
2347 logging.makeLogRecord({'msg': 'two'}),
2348 ]
2349
2350 def test_default(self):
2351 f = logging.BufferingFormatter()
2352 self.assertEqual('', f.format([]))
2353 self.assertEqual('onetwo', f.format(self.records))
2354
2355 def test_custom(self):
2356 f = TestBufferingFormatter()
2357 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2358 lf = logging.Formatter('<%(message)s>')
2359 f = TestBufferingFormatter(lf)
2360 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002361
2362class ExceptionTest(BaseTest):
2363 def test_formatting(self):
2364 r = self.root_logger
2365 h = RecordingHandler()
2366 r.addHandler(h)
2367 try:
2368 raise RuntimeError('deliberate mistake')
2369 except:
2370 logging.exception('failed', stack_info=True)
2371 r.removeHandler(h)
2372 h.close()
2373 r = h.records[0]
2374 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2375 'call last):\n'))
2376 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2377 'deliberate mistake'))
2378 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2379 'call last):\n'))
2380 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2381 'stack_info=True)'))
2382
2383
Vinay Sajip5a27d402010-12-10 11:42:57 +00002384class LastResortTest(BaseTest):
2385 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002386 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002387 root = self.root_logger
2388 root.removeHandler(self.root_hdlr)
2389 old_stderr = sys.stderr
2390 old_lastresort = logging.lastResort
2391 old_raise_exceptions = logging.raiseExceptions
2392 try:
2393 sys.stderr = sio = io.StringIO()
2394 root.warning('This is your final chance!')
2395 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2396 #No handlers and no last resort, so 'No handlers' message
2397 logging.lastResort = None
2398 sys.stderr = sio = io.StringIO()
2399 root.warning('This is your final chance!')
2400 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2401 # 'No handlers' message only printed once
2402 sys.stderr = sio = io.StringIO()
2403 root.warning('This is your final chance!')
2404 self.assertEqual(sio.getvalue(), '')
2405 root.manager.emittedNoHandlerWarning = False
2406 #If raiseExceptions is False, no message is printed
2407 logging.raiseExceptions = False
2408 sys.stderr = sio = io.StringIO()
2409 root.warning('This is your final chance!')
2410 self.assertEqual(sio.getvalue(), '')
2411 finally:
2412 sys.stderr = old_stderr
2413 root.addHandler(self.root_hdlr)
2414 logging.lastResort = old_lastresort
2415 logging.raiseExceptions = old_raise_exceptions
2416
2417
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002418class FakeHandler:
2419
2420 def __init__(self, identifier, called):
2421 for method in ('acquire', 'flush', 'close', 'release'):
2422 setattr(self, method, self.record_call(identifier, method, called))
2423
2424 def record_call(self, identifier, method_name, called):
2425 def inner():
2426 called.append('{} - {}'.format(identifier, method_name))
2427 return inner
2428
2429
2430class RecordingHandler(logging.NullHandler):
2431
2432 def __init__(self, *args, **kwargs):
2433 super(RecordingHandler, self).__init__(*args, **kwargs)
2434 self.records = []
2435
2436 def handle(self, record):
2437 """Keep track of all the emitted records."""
2438 self.records.append(record)
2439
2440
2441class ShutdownTest(BaseTest):
2442
Vinay Sajip5e66b162011-04-20 15:41:14 +01002443 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002444
2445 def setUp(self):
2446 super(ShutdownTest, self).setUp()
2447 self.called = []
2448
2449 raise_exceptions = logging.raiseExceptions
2450 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2451
2452 def raise_error(self, error):
2453 def inner():
2454 raise error()
2455 return inner
2456
2457 def test_no_failure(self):
2458 # create some fake handlers
2459 handler0 = FakeHandler(0, self.called)
2460 handler1 = FakeHandler(1, self.called)
2461 handler2 = FakeHandler(2, self.called)
2462
2463 # create live weakref to those handlers
2464 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2465
2466 logging.shutdown(handlerList=list(handlers))
2467
2468 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2469 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2470 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2471 self.assertEqual(expected, self.called)
2472
2473 def _test_with_failure_in_method(self, method, error):
2474 handler = FakeHandler(0, self.called)
2475 setattr(handler, method, self.raise_error(error))
2476 handlers = [logging.weakref.ref(handler)]
2477
2478 logging.shutdown(handlerList=list(handlers))
2479
2480 self.assertEqual('0 - release', self.called[-1])
2481
2482 def test_with_ioerror_in_acquire(self):
2483 self._test_with_failure_in_method('acquire', IOError)
2484
2485 def test_with_ioerror_in_flush(self):
2486 self._test_with_failure_in_method('flush', IOError)
2487
2488 def test_with_ioerror_in_close(self):
2489 self._test_with_failure_in_method('close', IOError)
2490
2491 def test_with_valueerror_in_acquire(self):
2492 self._test_with_failure_in_method('acquire', ValueError)
2493
2494 def test_with_valueerror_in_flush(self):
2495 self._test_with_failure_in_method('flush', ValueError)
2496
2497 def test_with_valueerror_in_close(self):
2498 self._test_with_failure_in_method('close', ValueError)
2499
2500 def test_with_other_error_in_acquire_without_raise(self):
2501 logging.raiseExceptions = False
2502 self._test_with_failure_in_method('acquire', IndexError)
2503
2504 def test_with_other_error_in_flush_without_raise(self):
2505 logging.raiseExceptions = False
2506 self._test_with_failure_in_method('flush', IndexError)
2507
2508 def test_with_other_error_in_close_without_raise(self):
2509 logging.raiseExceptions = False
2510 self._test_with_failure_in_method('close', IndexError)
2511
2512 def test_with_other_error_in_acquire_with_raise(self):
2513 logging.raiseExceptions = True
2514 self.assertRaises(IndexError, self._test_with_failure_in_method,
2515 'acquire', IndexError)
2516
2517 def test_with_other_error_in_flush_with_raise(self):
2518 logging.raiseExceptions = True
2519 self.assertRaises(IndexError, self._test_with_failure_in_method,
2520 'flush', IndexError)
2521
2522 def test_with_other_error_in_close_with_raise(self):
2523 logging.raiseExceptions = True
2524 self.assertRaises(IndexError, self._test_with_failure_in_method,
2525 'close', IndexError)
2526
2527
2528class ModuleLevelMiscTest(BaseTest):
2529
Vinay Sajip5e66b162011-04-20 15:41:14 +01002530 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002531
2532 def test_disable(self):
2533 old_disable = logging.root.manager.disable
2534 # confirm our assumptions are correct
2535 assert old_disable == 0
2536 self.addCleanup(lambda: logging.disable(old_disable))
2537
2538 logging.disable(83)
2539 self.assertEqual(logging.root.manager.disable, 83)
2540
2541 def _test_log(self, method, level=None):
2542 called = []
2543 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002544 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002545
2546 recording = RecordingHandler()
2547 logging.root.addHandler(recording)
2548
2549 log_method = getattr(logging, method)
2550 if level is not None:
2551 log_method(level, "test me: %r", recording)
2552 else:
2553 log_method("test me: %r", recording)
2554
2555 self.assertEqual(len(recording.records), 1)
2556 record = recording.records[0]
2557 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2558
2559 expected_level = level if level is not None else getattr(logging, method.upper())
2560 self.assertEqual(record.levelno, expected_level)
2561
2562 # basicConfig was not called!
2563 self.assertEqual(called, [])
2564
2565 def test_log(self):
2566 self._test_log('log', logging.ERROR)
2567
2568 def test_debug(self):
2569 self._test_log('debug')
2570
2571 def test_info(self):
2572 self._test_log('info')
2573
2574 def test_warning(self):
2575 self._test_log('warning')
2576
2577 def test_error(self):
2578 self._test_log('error')
2579
2580 def test_critical(self):
2581 self._test_log('critical')
2582
2583 def test_set_logger_class(self):
2584 self.assertRaises(TypeError, logging.setLoggerClass, object)
2585
2586 class MyLogger(logging.Logger):
2587 pass
2588
2589 logging.setLoggerClass(MyLogger)
2590 self.assertEqual(logging.getLoggerClass(), MyLogger)
2591
2592 logging.setLoggerClass(logging.Logger)
2593 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2594
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002595class LogRecordTest(BaseTest):
2596 def test_str_rep(self):
2597 r = logging.makeLogRecord({})
2598 s = str(r)
2599 self.assertTrue(s.startswith('<LogRecord: '))
2600 self.assertTrue(s.endswith('>'))
2601
2602 def test_dict_arg(self):
2603 h = RecordingHandler()
2604 r = logging.getLogger()
2605 r.addHandler(h)
2606 d = {'less' : 'more' }
2607 logging.warning('less is %(less)s', d)
2608 self.assertIs(h.records[0].args, d)
2609 self.assertEqual(h.records[0].message, 'less is more')
2610 r.removeHandler(h)
2611 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002612
2613class BasicConfigTest(unittest.TestCase):
2614
Vinay Sajip95bf5042011-04-20 11:50:56 +01002615 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002616
2617 def setUp(self):
2618 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01002619 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01002620 self.saved_handlers = logging._handlers.copy()
2621 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002622 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002623 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002624 logging.root.handlers = []
2625
2626 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01002627 for h in logging.root.handlers[:]:
2628 logging.root.removeHandler(h)
2629 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002630 super(BasicConfigTest, self).tearDown()
2631
Vinay Sajip3def7e02011-04-20 10:58:06 +01002632 def cleanup(self):
2633 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01002634 logging._handlers.clear()
2635 logging._handlers.update(self.saved_handlers)
2636 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002637 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002638
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002639 def test_no_kwargs(self):
2640 logging.basicConfig()
2641
2642 # handler defaults to a StreamHandler to sys.stderr
2643 self.assertEqual(len(logging.root.handlers), 1)
2644 handler = logging.root.handlers[0]
2645 self.assertIsInstance(handler, logging.StreamHandler)
2646 self.assertEqual(handler.stream, sys.stderr)
2647
2648 formatter = handler.formatter
2649 # format defaults to logging.BASIC_FORMAT
2650 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2651 # datefmt defaults to None
2652 self.assertIsNone(formatter.datefmt)
2653 # style defaults to %
2654 self.assertIsInstance(formatter._style, logging.PercentStyle)
2655
2656 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01002657 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002658
2659 def test_filename(self):
2660 logging.basicConfig(filename='test.log')
2661
2662 self.assertEqual(len(logging.root.handlers), 1)
2663 handler = logging.root.handlers[0]
2664 self.assertIsInstance(handler, logging.FileHandler)
2665
2666 expected = logging.FileHandler('test.log', 'a')
2667 self.addCleanup(expected.close)
2668 self.assertEqual(handler.stream.mode, expected.stream.mode)
2669 self.assertEqual(handler.stream.name, expected.stream.name)
2670
2671 def test_filemode(self):
2672 logging.basicConfig(filename='test.log', filemode='wb')
2673
2674 handler = logging.root.handlers[0]
2675 expected = logging.FileHandler('test.log', 'wb')
2676 self.addCleanup(expected.close)
2677 self.assertEqual(handler.stream.mode, expected.stream.mode)
2678
2679 def test_stream(self):
2680 stream = io.StringIO()
2681 self.addCleanup(stream.close)
2682 logging.basicConfig(stream=stream)
2683
2684 self.assertEqual(len(logging.root.handlers), 1)
2685 handler = logging.root.handlers[0]
2686 self.assertIsInstance(handler, logging.StreamHandler)
2687 self.assertEqual(handler.stream, stream)
2688
2689 def test_format(self):
2690 logging.basicConfig(format='foo')
2691
2692 formatter = logging.root.handlers[0].formatter
2693 self.assertEqual(formatter._style._fmt, 'foo')
2694
2695 def test_datefmt(self):
2696 logging.basicConfig(datefmt='bar')
2697
2698 formatter = logging.root.handlers[0].formatter
2699 self.assertEqual(formatter.datefmt, 'bar')
2700
2701 def test_style(self):
2702 logging.basicConfig(style='$')
2703
2704 formatter = logging.root.handlers[0].formatter
2705 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2706
2707 def test_level(self):
2708 old_level = logging.root.level
2709 self.addCleanup(lambda: logging.root.setLevel(old_level))
2710
2711 logging.basicConfig(level=57)
2712 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002713 # Test that second call has no effect
2714 logging.basicConfig(level=58)
2715 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002716
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002717 def test_incompatible(self):
2718 assertRaises = self.assertRaises
2719 handlers = [logging.StreamHandler()]
2720 stream = sys.stderr
2721 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2722 stream=stream)
2723 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2724 handlers=handlers)
2725 assertRaises(ValueError, logging.basicConfig, stream=stream,
2726 handlers=handlers)
2727
2728 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002729 handlers = [
2730 logging.StreamHandler(),
2731 logging.StreamHandler(sys.stdout),
2732 logging.StreamHandler(),
2733 ]
2734 f = logging.Formatter()
2735 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002736 logging.basicConfig(handlers=handlers)
2737 self.assertIs(handlers[0], logging.root.handlers[0])
2738 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002739 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002740 self.assertIsNotNone(handlers[0].formatter)
2741 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002742 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002743 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2744
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002745 def _test_log(self, method, level=None):
2746 # logging.root has no handlers so basicConfig should be called
2747 called = []
2748
2749 old_basic_config = logging.basicConfig
2750 def my_basic_config(*a, **kw):
2751 old_basic_config()
2752 old_level = logging.root.level
2753 logging.root.setLevel(100) # avoid having messages in stderr
2754 self.addCleanup(lambda: logging.root.setLevel(old_level))
2755 called.append((a, kw))
2756
2757 patch(self, logging, 'basicConfig', my_basic_config)
2758
2759 log_method = getattr(logging, method)
2760 if level is not None:
2761 log_method(level, "test me")
2762 else:
2763 log_method("test me")
2764
2765 # basicConfig was called with no arguments
2766 self.assertEqual(called, [((), {})])
2767
2768 def test_log(self):
2769 self._test_log('log', logging.WARNING)
2770
2771 def test_debug(self):
2772 self._test_log('debug')
2773
2774 def test_info(self):
2775 self._test_log('info')
2776
2777 def test_warning(self):
2778 self._test_log('warning')
2779
2780 def test_error(self):
2781 self._test_log('error')
2782
2783 def test_critical(self):
2784 self._test_log('critical')
2785
2786
2787class LoggerAdapterTest(unittest.TestCase):
2788
2789 def setUp(self):
2790 super(LoggerAdapterTest, self).setUp()
2791 old_handler_list = logging._handlerList[:]
2792
2793 self.recording = RecordingHandler()
2794 self.logger = logging.root
2795 self.logger.addHandler(self.recording)
2796 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2797 self.addCleanup(self.recording.close)
2798
2799 def cleanup():
2800 logging._handlerList[:] = old_handler_list
2801
2802 self.addCleanup(cleanup)
2803 self.addCleanup(logging.shutdown)
2804 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2805
2806 def test_exception(self):
2807 msg = 'testing exception: %r'
2808 exc = None
2809 try:
2810 assert False
2811 except AssertionError as e:
2812 exc = e
2813 self.adapter.exception(msg, self.recording)
2814
2815 self.assertEqual(len(self.recording.records), 1)
2816 record = self.recording.records[0]
2817 self.assertEqual(record.levelno, logging.ERROR)
2818 self.assertEqual(record.msg, msg)
2819 self.assertEqual(record.args, (self.recording,))
2820 self.assertEqual(record.exc_info,
2821 (exc.__class__, exc, exc.__traceback__))
2822
2823 def test_critical(self):
2824 msg = 'critical test! %r'
2825 self.adapter.critical(msg, self.recording)
2826
2827 self.assertEqual(len(self.recording.records), 1)
2828 record = self.recording.records[0]
2829 self.assertEqual(record.levelno, logging.CRITICAL)
2830 self.assertEqual(record.msg, msg)
2831 self.assertEqual(record.args, (self.recording,))
2832
2833 def test_is_enabled_for(self):
2834 old_disable = self.adapter.logger.manager.disable
2835 self.adapter.logger.manager.disable = 33
2836 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2837 'disable', old_disable))
2838 self.assertFalse(self.adapter.isEnabledFor(32))
2839
2840 def test_has_handlers(self):
2841 self.assertTrue(self.adapter.hasHandlers())
2842
2843 for handler in self.logger.handlers:
2844 self.logger.removeHandler(handler)
2845 assert not self.logger.hasHandlers()
2846
2847 self.assertFalse(self.adapter.hasHandlers())
2848
2849
2850class LoggerTest(BaseTest):
2851
2852 def setUp(self):
2853 super(LoggerTest, self).setUp()
2854 self.recording = RecordingHandler()
2855 self.logger = logging.Logger(name='blah')
2856 self.logger.addHandler(self.recording)
2857 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2858 self.addCleanup(self.recording.close)
2859 self.addCleanup(logging.shutdown)
2860
2861 def test_set_invalid_level(self):
2862 self.assertRaises(TypeError, self.logger.setLevel, object())
2863
2864 def test_exception(self):
2865 msg = 'testing exception: %r'
2866 exc = None
2867 try:
2868 assert False
2869 except AssertionError as e:
2870 exc = e
2871 self.logger.exception(msg, self.recording)
2872
2873 self.assertEqual(len(self.recording.records), 1)
2874 record = self.recording.records[0]
2875 self.assertEqual(record.levelno, logging.ERROR)
2876 self.assertEqual(record.msg, msg)
2877 self.assertEqual(record.args, (self.recording,))
2878 self.assertEqual(record.exc_info,
2879 (exc.__class__, exc, exc.__traceback__))
2880
2881 def test_log_invalid_level_with_raise(self):
2882 old_raise = logging.raiseExceptions
2883 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2884
2885 logging.raiseExceptions = True
2886 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2887
2888 def test_log_invalid_level_no_raise(self):
2889 old_raise = logging.raiseExceptions
2890 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2891
2892 logging.raiseExceptions = False
2893 self.logger.log('10', 'test message') # no exception happens
2894
2895 def test_find_caller_with_stack_info(self):
2896 called = []
2897 patch(self, logging.traceback, 'print_stack',
2898 lambda f, file: called.append(file.getvalue()))
2899
2900 self.logger.findCaller(stack_info=True)
2901
2902 self.assertEqual(len(called), 1)
2903 self.assertEqual('Stack (most recent call last):\n', called[0])
2904
2905 def test_make_record_with_extra_overwrite(self):
2906 name = 'my record'
2907 level = 13
2908 fn = lno = msg = args = exc_info = func = sinfo = None
2909 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2910 exc_info, func, sinfo)
2911
2912 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2913 extra = {key: 'some value'}
2914 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2915 fn, lno, msg, args, exc_info,
2916 extra=extra, sinfo=sinfo)
2917
2918 def test_make_record_with_extra_no_overwrite(self):
2919 name = 'my record'
2920 level = 13
2921 fn = lno = msg = args = exc_info = func = sinfo = None
2922 extra = {'valid_key': 'some value'}
2923 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2924 exc_info, extra=extra, sinfo=sinfo)
2925 self.assertIn('valid_key', result.__dict__)
2926
2927 def test_has_handlers(self):
2928 self.assertTrue(self.logger.hasHandlers())
2929
2930 for handler in self.logger.handlers:
2931 self.logger.removeHandler(handler)
2932 assert not self.logger.hasHandlers()
2933
2934 self.assertFalse(self.logger.hasHandlers())
2935
2936 def test_has_handlers_no_propagate(self):
2937 child_logger = logging.getLogger('blah.child')
2938 child_logger.propagate = False
2939 assert child_logger.handlers == []
2940
2941 self.assertFalse(child_logger.hasHandlers())
2942
2943 def test_is_enabled_for(self):
2944 old_disable = self.logger.manager.disable
2945 self.logger.manager.disable = 23
2946 self.addCleanup(lambda: setattr(self.logger.manager,
2947 'disable', old_disable))
2948 self.assertFalse(self.logger.isEnabledFor(22))
2949
2950
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002951class BaseFileTest(BaseTest):
2952 "Base class for handler tests that write log files"
2953
2954 def setUp(self):
2955 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002956 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2957 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002958 self.rmfiles = []
2959
2960 def tearDown(self):
2961 for fn in self.rmfiles:
2962 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002963 if os.path.exists(self.fn):
2964 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002965 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002966
2967 def assertLogFile(self, filename):
2968 "Assert a log file is there and register it for deletion"
2969 self.assertTrue(os.path.exists(filename),
2970 msg="Log file %r does not exist")
2971 self.rmfiles.append(filename)
2972
2973
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002974class FileHandlerTest(BaseFileTest):
2975 def test_delay(self):
2976 os.unlink(self.fn)
2977 fh = logging.FileHandler(self.fn, delay=True)
2978 self.assertIsNone(fh.stream)
2979 self.assertFalse(os.path.exists(self.fn))
2980 fh.handle(logging.makeLogRecord({}))
2981 self.assertIsNotNone(fh.stream)
2982 self.assertTrue(os.path.exists(self.fn))
2983 fh.close()
2984
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002985class RotatingFileHandlerTest(BaseFileTest):
2986 def next_rec(self):
2987 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2988 self.next_message(), None, None, None)
2989
2990 def test_should_not_rollover(self):
2991 # If maxbytes is zero rollover never occurs
2992 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2993 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002994 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002995
2996 def test_should_rollover(self):
2997 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2998 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002999 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003000
3001 def test_file_created(self):
3002 # checks that the file is created and assumes it was created
3003 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003004 rh = logging.handlers.RotatingFileHandler(self.fn)
3005 rh.emit(self.next_rec())
3006 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00003007 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003008
3009 def test_rollover_filenames(self):
3010 rh = logging.handlers.RotatingFileHandler(
3011 self.fn, backupCount=2, maxBytes=1)
3012 rh.emit(self.next_rec())
3013 self.assertLogFile(self.fn)
3014 rh.emit(self.next_rec())
3015 self.assertLogFile(self.fn + ".1")
3016 rh.emit(self.next_rec())
3017 self.assertLogFile(self.fn + ".2")
3018 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00003019 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003020
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003021class TimedRotatingFileHandlerTest(BaseFileTest):
3022 # test methods added below
3023 pass
3024
3025def secs(**kw):
3026 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
3027
3028for when, exp in (('S', 1),
3029 ('M', 60),
3030 ('H', 60 * 60),
3031 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00003032 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003033 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00003034 ('W0', secs(days=4, hours=24)),
3035 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003036 def test_compute_rollover(self, when=when, exp=exp):
3037 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00003038 self.fn, when=when, interval=1, backupCount=0, utc=True)
3039 currentTime = 0.0
3040 actual = rh.computeRollover(currentTime)
3041 if exp != actual:
3042 # Failures occur on some systems for MIDNIGHT and W0.
3043 # Print detailed calculation for MIDNIGHT so we can try to see
3044 # what's going on
3045 import time
3046 if when == 'MIDNIGHT':
3047 try:
3048 if rh.utc:
3049 t = time.gmtime(currentTime)
3050 else:
3051 t = time.localtime(currentTime)
3052 currentHour = t[3]
3053 currentMinute = t[4]
3054 currentSecond = t[5]
3055 # r is the number of seconds left between now and midnight
3056 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3057 currentMinute) * 60 +
3058 currentSecond)
3059 result = currentTime + r
3060 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3061 print('currentHour: %s' % currentHour, file=sys.stderr)
3062 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3063 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3064 print('r: %s' % r, file=sys.stderr)
3065 print('result: %s' % result, file=sys.stderr)
3066 except Exception:
3067 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3068 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003069 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003070 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3071
Christian Heimes180510d2008-03-03 19:15:45 +00003072# Set the locale to the platform-dependent default. I have no idea
3073# why the test does this, but in any case we save the current locale
3074# first and restore it at the end.
3075@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003076def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003077 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003078 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003079 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003080 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip985ef872011-04-26 19:34:04 +01003081 FormatterTest, BufferingFormatterTest, StreamHandlerTest,
Vinay Sajip61561522010-12-03 11:50:38 +00003082 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003083 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
3084 LoggerAdapterTest, LoggerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003085 FileHandlerTest, RotatingFileHandlerTest,
3086 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003087 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003088 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003089
Christian Heimes180510d2008-03-03 19:15:45 +00003090if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003091 test_main()