blob: 306eea70c994b18523fa219b86df4246046cbaee [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
511 def test_abc(self):
512 pass
513
514class BadStream(object):
515 def write(self, data):
516 raise RuntimeError('deliberate mistake')
517
518class TestStreamHandler(logging.StreamHandler):
519 def handleError(self, record):
520 self.error_record = record
521
522class StreamHandlerTest(BaseTest):
523 def test_error_handling(self):
524 h = TestStreamHandler(BadStream())
525 r = logging.makeLogRecord({})
526 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100527 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100528 try:
529 h.handle(r)
530 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100531 h = logging.StreamHandler(BadStream())
532 sys.stderr = sio = io.StringIO()
533 h.handle(r)
534 self.assertTrue('\nRuntimeError: '
535 'deliberate mistake\n' in sio.getvalue())
536 logging.raiseExceptions = False
537 sys.stderr = sio = io.StringIO()
538 h.handle(r)
539 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100540 finally:
541 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100542 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100543
Christian Heimes180510d2008-03-03 19:15:45 +0000544class MemoryHandlerTest(BaseTest):
545
546 """Tests for the MemoryHandler."""
547
548 # Do not bother with a logger name group.
549 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
550
551 def setUp(self):
552 BaseTest.setUp(self)
553 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
554 self.root_hdlr)
555 self.mem_logger = logging.getLogger('mem')
556 self.mem_logger.propagate = 0
557 self.mem_logger.addHandler(self.mem_hdlr)
558
559 def tearDown(self):
560 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000561 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000562
563 def test_flush(self):
564 # The memory handler flushes to its target handler based on specific
565 # criteria (message count and message level).
566 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000567 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000568 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000569 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000570 # This will flush because the level is >= logging.WARNING
571 self.mem_logger.warn(self.next_message())
572 lines = [
573 ('DEBUG', '1'),
574 ('INFO', '2'),
575 ('WARNING', '3'),
576 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000577 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000578 for n in (4, 14):
579 for i in range(9):
580 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000581 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000582 # This will flush because it's the 10th message since the last
583 # flush.
584 self.mem_logger.debug(self.next_message())
585 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000586 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000587
588 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000589 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000590
591
592class ExceptionFormatter(logging.Formatter):
593 """A special exception formatter."""
594 def formatException(self, ei):
595 return "Got a [%s]" % ei[0].__name__
596
597
598class ConfigFileTest(BaseTest):
599
600 """Reading logging config from a .ini-style config file."""
601
602 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
603
604 # config0 is a standard configuration.
605 config0 = """
606 [loggers]
607 keys=root
608
609 [handlers]
610 keys=hand1
611
612 [formatters]
613 keys=form1
614
615 [logger_root]
616 level=WARNING
617 handlers=hand1
618
619 [handler_hand1]
620 class=StreamHandler
621 level=NOTSET
622 formatter=form1
623 args=(sys.stdout,)
624
625 [formatter_form1]
626 format=%(levelname)s ++ %(message)s
627 datefmt=
628 """
629
630 # config1 adds a little to the standard configuration.
631 config1 = """
632 [loggers]
633 keys=root,parser
634
635 [handlers]
636 keys=hand1
637
638 [formatters]
639 keys=form1
640
641 [logger_root]
642 level=WARNING
643 handlers=
644
645 [logger_parser]
646 level=DEBUG
647 handlers=hand1
648 propagate=1
649 qualname=compiler.parser
650
651 [handler_hand1]
652 class=StreamHandler
653 level=NOTSET
654 formatter=form1
655 args=(sys.stdout,)
656
657 [formatter_form1]
658 format=%(levelname)s ++ %(message)s
659 datefmt=
660 """
661
Vinay Sajip3f84b072011-03-07 17:49:33 +0000662 # config1a moves the handler to the root.
663 config1a = """
664 [loggers]
665 keys=root,parser
666
667 [handlers]
668 keys=hand1
669
670 [formatters]
671 keys=form1
672
673 [logger_root]
674 level=WARNING
675 handlers=hand1
676
677 [logger_parser]
678 level=DEBUG
679 handlers=
680 propagate=1
681 qualname=compiler.parser
682
683 [handler_hand1]
684 class=StreamHandler
685 level=NOTSET
686 formatter=form1
687 args=(sys.stdout,)
688
689 [formatter_form1]
690 format=%(levelname)s ++ %(message)s
691 datefmt=
692 """
693
Christian Heimes180510d2008-03-03 19:15:45 +0000694 # config2 has a subtle configuration error that should be reported
695 config2 = config1.replace("sys.stdout", "sys.stbout")
696
697 # config3 has a less subtle configuration error
698 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
699
700 # config4 specifies a custom formatter class to be loaded
701 config4 = """
702 [loggers]
703 keys=root
704
705 [handlers]
706 keys=hand1
707
708 [formatters]
709 keys=form1
710
711 [logger_root]
712 level=NOTSET
713 handlers=hand1
714
715 [handler_hand1]
716 class=StreamHandler
717 level=NOTSET
718 formatter=form1
719 args=(sys.stdout,)
720
721 [formatter_form1]
722 class=""" + __name__ + """.ExceptionFormatter
723 format=%(levelname)s:%(name)s:%(message)s
724 datefmt=
725 """
726
Georg Brandl3dbca812008-07-23 16:10:53 +0000727 # config5 specifies a custom handler class to be loaded
728 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
729
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000730 # config6 uses ', ' delimiters in the handlers and formatters sections
731 config6 = """
732 [loggers]
733 keys=root,parser
734
735 [handlers]
736 keys=hand1, hand2
737
738 [formatters]
739 keys=form1, form2
740
741 [logger_root]
742 level=WARNING
743 handlers=
744
745 [logger_parser]
746 level=DEBUG
747 handlers=hand1
748 propagate=1
749 qualname=compiler.parser
750
751 [handler_hand1]
752 class=StreamHandler
753 level=NOTSET
754 formatter=form1
755 args=(sys.stdout,)
756
757 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000758 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000759 level=NOTSET
760 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000761 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000762
763 [formatter_form1]
764 format=%(levelname)s ++ %(message)s
765 datefmt=
766
767 [formatter_form2]
768 format=%(message)s
769 datefmt=
770 """
771
Vinay Sajip3f84b072011-03-07 17:49:33 +0000772 # config7 adds a compiler logger.
773 config7 = """
774 [loggers]
775 keys=root,parser,compiler
776
777 [handlers]
778 keys=hand1
779
780 [formatters]
781 keys=form1
782
783 [logger_root]
784 level=WARNING
785 handlers=hand1
786
787 [logger_compiler]
788 level=DEBUG
789 handlers=
790 propagate=1
791 qualname=compiler
792
793 [logger_parser]
794 level=DEBUG
795 handlers=
796 propagate=1
797 qualname=compiler.parser
798
799 [handler_hand1]
800 class=StreamHandler
801 level=NOTSET
802 formatter=form1
803 args=(sys.stdout,)
804
805 [formatter_form1]
806 format=%(levelname)s ++ %(message)s
807 datefmt=
808 """
809
Christian Heimes180510d2008-03-03 19:15:45 +0000810 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000811 file = io.StringIO(textwrap.dedent(conf))
812 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000813
814 def test_config0_ok(self):
815 # A simple config file which overrides the default settings.
816 with captured_stdout() as output:
817 self.apply_config(self.config0)
818 logger = logging.getLogger()
819 # Won't output anything
820 logger.info(self.next_message())
821 # Outputs a message
822 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000823 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000824 ('ERROR', '2'),
825 ], stream=output)
826 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000827 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000828
Georg Brandl3dbca812008-07-23 16:10:53 +0000829 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000830 # A config file defining a sub-parser as well.
831 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000832 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000833 logger = logging.getLogger("compiler.parser")
834 # Both will output a message
835 logger.info(self.next_message())
836 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000837 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000838 ('INFO', '1'),
839 ('ERROR', '2'),
840 ], stream=output)
841 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000842 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000843
844 def test_config2_failure(self):
845 # A simple config file which overrides the default settings.
846 self.assertRaises(Exception, self.apply_config, self.config2)
847
848 def test_config3_failure(self):
849 # A simple config file which overrides the default settings.
850 self.assertRaises(Exception, self.apply_config, self.config3)
851
852 def test_config4_ok(self):
853 # A config file specifying a custom formatter class.
854 with captured_stdout() as output:
855 self.apply_config(self.config4)
856 logger = logging.getLogger()
857 try:
858 raise RuntimeError()
859 except RuntimeError:
860 logging.exception("just testing")
861 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000862 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000863 "ERROR:root:just testing\nGot a [RuntimeError]\n")
864 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000865 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000866
Georg Brandl3dbca812008-07-23 16:10:53 +0000867 def test_config5_ok(self):
868 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000869
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000870 def test_config6_ok(self):
871 self.test_config1_ok(config=self.config6)
872
Vinay Sajip3f84b072011-03-07 17:49:33 +0000873 def test_config7_ok(self):
874 with captured_stdout() as output:
875 self.apply_config(self.config1a)
876 logger = logging.getLogger("compiler.parser")
877 # See issue #11424. compiler-hyphenated sorts
878 # between compiler and compiler.xyz and this
879 # was preventing compiler.xyz from being included
880 # in the child loggers of compiler because of an
881 # overzealous loop termination condition.
882 hyphenated = logging.getLogger('compiler-hyphenated')
883 # All will output a message
884 logger.info(self.next_message())
885 logger.error(self.next_message())
886 hyphenated.critical(self.next_message())
887 self.assert_log_lines([
888 ('INFO', '1'),
889 ('ERROR', '2'),
890 ('CRITICAL', '3'),
891 ], stream=output)
892 # Original logger output is empty.
893 self.assert_log_lines([])
894 with captured_stdout() as output:
895 self.apply_config(self.config7)
896 logger = logging.getLogger("compiler.parser")
897 self.assertFalse(logger.disabled)
898 # Both will output a message
899 logger.info(self.next_message())
900 logger.error(self.next_message())
901 logger = logging.getLogger("compiler.lexer")
902 # Both will output a message
903 logger.info(self.next_message())
904 logger.error(self.next_message())
905 # Will not appear
906 hyphenated.critical(self.next_message())
907 self.assert_log_lines([
908 ('INFO', '4'),
909 ('ERROR', '5'),
910 ('INFO', '6'),
911 ('ERROR', '7'),
912 ], stream=output)
913 # Original logger output is empty.
914 self.assert_log_lines([])
915
Christian Heimes180510d2008-03-03 19:15:45 +0000916class LogRecordStreamHandler(StreamRequestHandler):
917
918 """Handler for a streaming logging request. It saves the log message in the
919 TCP server's 'log_output' attribute."""
920
921 TCP_LOG_END = "!!!END!!!"
922
923 def handle(self):
924 """Handle multiple requests - each expected to be of 4-byte length,
925 followed by the LogRecord in pickle format. Logs the record
926 according to whatever policy is configured locally."""
927 while True:
928 chunk = self.connection.recv(4)
929 if len(chunk) < 4:
930 break
931 slen = struct.unpack(">L", chunk)[0]
932 chunk = self.connection.recv(slen)
933 while len(chunk) < slen:
934 chunk = chunk + self.connection.recv(slen - len(chunk))
935 obj = self.unpickle(chunk)
936 record = logging.makeLogRecord(obj)
937 self.handle_log_record(record)
938
939 def unpickle(self, data):
940 return pickle.loads(data)
941
942 def handle_log_record(self, record):
943 # If the end-of-messages sentinel is seen, tell the server to
944 # terminate.
945 if self.TCP_LOG_END in record.msg:
946 self.server.abort = 1
947 return
948 self.server.log_output += record.msg + "\n"
949
Guido van Rossum376e6362003-04-25 14:22:00 +0000950
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000951class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000952
953 """A simple-minded TCP socket-based logging receiver suitable for test
954 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000955
956 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000957 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000958
959 def __init__(self, host='localhost',
960 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
961 handler=LogRecordStreamHandler):
962 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +0000963 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +0000964 self.timeout = 0.1
965 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000966
967 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000968 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000969 rd, wr, ex = select.select([self.socket.fileno()], [], [],
970 self.timeout)
971 if rd:
972 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +0000973 # Notify the main thread that we're about to exit
974 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000975 # close the listen socket
976 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000977
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000978
Victor Stinner45df8202010-04-28 22:31:17 +0000979@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +0000980class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000981
Christian Heimes180510d2008-03-03 19:15:45 +0000982 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000983
Christian Heimes180510d2008-03-03 19:15:45 +0000984 def setUp(self):
985 """Set up a TCP server to receive log messages, and a SocketHandler
986 pointing to that server's address and port."""
987 BaseTest.setUp(self)
988 self.tcpserver = LogRecordSocketReceiver(port=0)
989 self.port = self.tcpserver.socket.getsockname()[1]
990 self.threads = [
991 threading.Thread(target=self.tcpserver.serve_until_stopped)]
992 for thread in self.threads:
993 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000994
Christian Heimes180510d2008-03-03 19:15:45 +0000995 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
996 self.sock_hdlr.setFormatter(self.root_formatter)
997 self.root_logger.removeHandler(self.root_logger.handlers[0])
998 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000999
Christian Heimes180510d2008-03-03 19:15:45 +00001000 def tearDown(self):
1001 """Shutdown the TCP server."""
1002 try:
1003 self.tcpserver.abort = True
1004 del self.tcpserver
1005 self.root_logger.removeHandler(self.sock_hdlr)
1006 self.sock_hdlr.close()
1007 for thread in self.threads:
1008 thread.join(2.0)
1009 finally:
1010 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001011
Christian Heimes180510d2008-03-03 19:15:45 +00001012 def get_output(self):
1013 """Get the log output as received by the TCP server."""
1014 # Signal the TCP receiver and wait for it to terminate.
1015 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
1016 self.tcpserver.finished.wait(2.0)
1017 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001018
Christian Heimes180510d2008-03-03 19:15:45 +00001019 def test_output(self):
1020 # The log message sent to the SocketHandler is properly received.
1021 logger = logging.getLogger("tcp")
1022 logger.error("spam")
1023 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +00001024 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001025
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001026
Christian Heimes180510d2008-03-03 19:15:45 +00001027class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001028
Christian Heimes180510d2008-03-03 19:15:45 +00001029 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001030
Christian Heimes180510d2008-03-03 19:15:45 +00001031 def setUp(self):
1032 """Create a dict to remember potentially destroyed objects."""
1033 BaseTest.setUp(self)
1034 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001035
Christian Heimes180510d2008-03-03 19:15:45 +00001036 def _watch_for_survival(self, *args):
1037 """Watch the given objects for survival, by creating weakrefs to
1038 them."""
1039 for obj in args:
1040 key = id(obj), repr(obj)
1041 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001042
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001043 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001044 """Assert that all objects watched for survival have survived."""
1045 # Trigger cycle breaking.
1046 gc.collect()
1047 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001048 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001049 if ref() is None:
1050 dead.append(repr_)
1051 if dead:
1052 self.fail("%d objects should have survived "
1053 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001054
Christian Heimes180510d2008-03-03 19:15:45 +00001055 def test_persistent_loggers(self):
1056 # Logger objects are persistent and retain their configuration, even
1057 # if visible references are destroyed.
1058 self.root_logger.setLevel(logging.INFO)
1059 foo = logging.getLogger("foo")
1060 self._watch_for_survival(foo)
1061 foo.setLevel(logging.DEBUG)
1062 self.root_logger.debug(self.next_message())
1063 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001064 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001065 ('foo', 'DEBUG', '2'),
1066 ])
1067 del foo
1068 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001069 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001070 # foo has retained its settings.
1071 bar = logging.getLogger("foo")
1072 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001073 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001074 ('foo', 'DEBUG', '2'),
1075 ('foo', 'DEBUG', '3'),
1076 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001077
Benjamin Petersonf91df042009-02-13 02:50:59 +00001078
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001079class EncodingTest(BaseTest):
1080 def test_encoding_plain_file(self):
1081 # In Python 2.x, a plain file object is treated as having no encoding.
1082 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001083 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1084 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001085 # the non-ascii data we write to the log.
1086 data = "foo\x80"
1087 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001088 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001089 log.addHandler(handler)
1090 try:
1091 # write non-ascii data to the log.
1092 log.warning(data)
1093 finally:
1094 log.removeHandler(handler)
1095 handler.close()
1096 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001097 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001098 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001099 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001100 finally:
1101 f.close()
1102 finally:
1103 if os.path.isfile(fn):
1104 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001105
Benjamin Petersonf91df042009-02-13 02:50:59 +00001106 def test_encoding_cyrillic_unicode(self):
1107 log = logging.getLogger("test")
1108 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1109 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1110 #Ensure it's written in a Cyrillic encoding
1111 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001112 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001113 stream = io.BytesIO()
1114 writer = writer_class(stream, 'strict')
1115 handler = logging.StreamHandler(writer)
1116 log.addHandler(handler)
1117 try:
1118 log.warning(message)
1119 finally:
1120 log.removeHandler(handler)
1121 handler.close()
1122 # check we wrote exactly those bytes, ignoring trailing \n etc
1123 s = stream.getvalue()
1124 #Compare against what the data should be when encoded in CP-1251
1125 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1126
1127
Georg Brandlf9734072008-12-07 15:30:06 +00001128class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001129
Georg Brandlf9734072008-12-07 15:30:06 +00001130 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001131 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001132 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001133 self.addCleanup(lambda: logging.captureWarnings(False))
1134 warnings.filterwarnings("always", category=UserWarning)
1135 stream = io.StringIO()
1136 h = logging.StreamHandler(stream)
1137 logger = logging.getLogger("py.warnings")
1138 logger.addHandler(h)
1139 warnings.warn("I'm warning you...")
1140 logger.removeHandler(h)
1141 s = stream.getvalue()
1142 h.close()
1143 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001144
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001145 #See if an explicit file uses the original implementation
1146 a_file = io.StringIO()
1147 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1148 a_file, "Dummy line")
1149 s = a_file.getvalue()
1150 a_file.close()
1151 self.assertEqual(s,
1152 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1153
1154 def test_warnings_no_handlers(self):
1155 with warnings.catch_warnings():
1156 logging.captureWarnings(True)
1157 self.addCleanup(lambda: logging.captureWarnings(False))
1158
1159 # confirm our assumption: no loggers are set
1160 logger = logging.getLogger("py.warnings")
1161 assert logger.handlers == []
1162
1163 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1164 self.assertTrue(len(logger.handlers) == 1)
1165 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001166
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001167
1168def formatFunc(format, datefmt=None):
1169 return logging.Formatter(format, datefmt)
1170
1171def handlerFunc():
1172 return logging.StreamHandler()
1173
1174class CustomHandler(logging.StreamHandler):
1175 pass
1176
1177class ConfigDictTest(BaseTest):
1178
1179 """Reading logging config from a dictionary."""
1180
1181 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1182
1183 # config0 is a standard configuration.
1184 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001185 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001186 'formatters': {
1187 'form1' : {
1188 'format' : '%(levelname)s ++ %(message)s',
1189 },
1190 },
1191 'handlers' : {
1192 'hand1' : {
1193 'class' : 'logging.StreamHandler',
1194 'formatter' : 'form1',
1195 'level' : 'NOTSET',
1196 'stream' : 'ext://sys.stdout',
1197 },
1198 },
1199 'root' : {
1200 'level' : 'WARNING',
1201 'handlers' : ['hand1'],
1202 },
1203 }
1204
1205 # config1 adds a little to the standard configuration.
1206 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001207 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001208 'formatters': {
1209 'form1' : {
1210 'format' : '%(levelname)s ++ %(message)s',
1211 },
1212 },
1213 'handlers' : {
1214 'hand1' : {
1215 'class' : 'logging.StreamHandler',
1216 'formatter' : 'form1',
1217 'level' : 'NOTSET',
1218 'stream' : 'ext://sys.stdout',
1219 },
1220 },
1221 'loggers' : {
1222 'compiler.parser' : {
1223 'level' : 'DEBUG',
1224 'handlers' : ['hand1'],
1225 },
1226 },
1227 'root' : {
1228 'level' : 'WARNING',
1229 },
1230 }
1231
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001232 # config1a moves the handler to the root. Used with config8a
1233 config1a = {
1234 'version': 1,
1235 'formatters': {
1236 'form1' : {
1237 'format' : '%(levelname)s ++ %(message)s',
1238 },
1239 },
1240 'handlers' : {
1241 'hand1' : {
1242 'class' : 'logging.StreamHandler',
1243 'formatter' : 'form1',
1244 'level' : 'NOTSET',
1245 'stream' : 'ext://sys.stdout',
1246 },
1247 },
1248 'loggers' : {
1249 'compiler.parser' : {
1250 'level' : 'DEBUG',
1251 },
1252 },
1253 'root' : {
1254 'level' : 'WARNING',
1255 'handlers' : ['hand1'],
1256 },
1257 }
1258
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001259 # config2 has a subtle configuration error that should be reported
1260 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001261 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001262 'formatters': {
1263 'form1' : {
1264 'format' : '%(levelname)s ++ %(message)s',
1265 },
1266 },
1267 'handlers' : {
1268 'hand1' : {
1269 'class' : 'logging.StreamHandler',
1270 'formatter' : 'form1',
1271 'level' : 'NOTSET',
1272 'stream' : 'ext://sys.stdbout',
1273 },
1274 },
1275 'loggers' : {
1276 'compiler.parser' : {
1277 'level' : 'DEBUG',
1278 'handlers' : ['hand1'],
1279 },
1280 },
1281 'root' : {
1282 'level' : 'WARNING',
1283 },
1284 }
1285
1286 #As config1 but with a misspelt level on a handler
1287 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001288 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001289 'formatters': {
1290 'form1' : {
1291 'format' : '%(levelname)s ++ %(message)s',
1292 },
1293 },
1294 'handlers' : {
1295 'hand1' : {
1296 'class' : 'logging.StreamHandler',
1297 'formatter' : 'form1',
1298 'level' : 'NTOSET',
1299 'stream' : 'ext://sys.stdout',
1300 },
1301 },
1302 'loggers' : {
1303 'compiler.parser' : {
1304 'level' : 'DEBUG',
1305 'handlers' : ['hand1'],
1306 },
1307 },
1308 'root' : {
1309 'level' : 'WARNING',
1310 },
1311 }
1312
1313
1314 #As config1 but with a misspelt level on a logger
1315 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001316 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001317 'formatters': {
1318 'form1' : {
1319 'format' : '%(levelname)s ++ %(message)s',
1320 },
1321 },
1322 'handlers' : {
1323 'hand1' : {
1324 'class' : 'logging.StreamHandler',
1325 'formatter' : 'form1',
1326 'level' : 'NOTSET',
1327 'stream' : 'ext://sys.stdout',
1328 },
1329 },
1330 'loggers' : {
1331 'compiler.parser' : {
1332 'level' : 'DEBUG',
1333 'handlers' : ['hand1'],
1334 },
1335 },
1336 'root' : {
1337 'level' : 'WRANING',
1338 },
1339 }
1340
1341 # config3 has a less subtle configuration error
1342 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001343 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001344 'formatters': {
1345 'form1' : {
1346 'format' : '%(levelname)s ++ %(message)s',
1347 },
1348 },
1349 'handlers' : {
1350 'hand1' : {
1351 'class' : 'logging.StreamHandler',
1352 'formatter' : 'misspelled_name',
1353 'level' : 'NOTSET',
1354 'stream' : 'ext://sys.stdout',
1355 },
1356 },
1357 'loggers' : {
1358 'compiler.parser' : {
1359 'level' : 'DEBUG',
1360 'handlers' : ['hand1'],
1361 },
1362 },
1363 'root' : {
1364 'level' : 'WARNING',
1365 },
1366 }
1367
1368 # config4 specifies a custom formatter class to be loaded
1369 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001370 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001371 'formatters': {
1372 'form1' : {
1373 '()' : __name__ + '.ExceptionFormatter',
1374 'format' : '%(levelname)s:%(name)s:%(message)s',
1375 },
1376 },
1377 'handlers' : {
1378 'hand1' : {
1379 'class' : 'logging.StreamHandler',
1380 'formatter' : 'form1',
1381 'level' : 'NOTSET',
1382 'stream' : 'ext://sys.stdout',
1383 },
1384 },
1385 'root' : {
1386 'level' : 'NOTSET',
1387 'handlers' : ['hand1'],
1388 },
1389 }
1390
1391 # As config4 but using an actual callable rather than a string
1392 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001393 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001394 'formatters': {
1395 'form1' : {
1396 '()' : ExceptionFormatter,
1397 'format' : '%(levelname)s:%(name)s:%(message)s',
1398 },
1399 'form2' : {
1400 '()' : __name__ + '.formatFunc',
1401 'format' : '%(levelname)s:%(name)s:%(message)s',
1402 },
1403 'form3' : {
1404 '()' : formatFunc,
1405 'format' : '%(levelname)s:%(name)s:%(message)s',
1406 },
1407 },
1408 'handlers' : {
1409 'hand1' : {
1410 'class' : 'logging.StreamHandler',
1411 'formatter' : 'form1',
1412 'level' : 'NOTSET',
1413 'stream' : 'ext://sys.stdout',
1414 },
1415 'hand2' : {
1416 '()' : handlerFunc,
1417 },
1418 },
1419 'root' : {
1420 'level' : 'NOTSET',
1421 'handlers' : ['hand1'],
1422 },
1423 }
1424
1425 # config5 specifies a custom handler class to be loaded
1426 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001427 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001428 'formatters': {
1429 'form1' : {
1430 'format' : '%(levelname)s ++ %(message)s',
1431 },
1432 },
1433 'handlers' : {
1434 'hand1' : {
1435 'class' : __name__ + '.CustomHandler',
1436 'formatter' : 'form1',
1437 'level' : 'NOTSET',
1438 'stream' : 'ext://sys.stdout',
1439 },
1440 },
1441 'loggers' : {
1442 'compiler.parser' : {
1443 'level' : 'DEBUG',
1444 'handlers' : ['hand1'],
1445 },
1446 },
1447 'root' : {
1448 'level' : 'WARNING',
1449 },
1450 }
1451
1452 # config6 specifies a custom handler class to be loaded
1453 # but has bad arguments
1454 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001455 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001456 'formatters': {
1457 'form1' : {
1458 'format' : '%(levelname)s ++ %(message)s',
1459 },
1460 },
1461 'handlers' : {
1462 'hand1' : {
1463 'class' : __name__ + '.CustomHandler',
1464 'formatter' : 'form1',
1465 'level' : 'NOTSET',
1466 'stream' : 'ext://sys.stdout',
1467 '9' : 'invalid parameter name',
1468 },
1469 },
1470 'loggers' : {
1471 'compiler.parser' : {
1472 'level' : 'DEBUG',
1473 'handlers' : ['hand1'],
1474 },
1475 },
1476 'root' : {
1477 'level' : 'WARNING',
1478 },
1479 }
1480
1481 #config 7 does not define compiler.parser but defines compiler.lexer
1482 #so compiler.parser should be disabled after applying it
1483 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001484 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001485 'formatters': {
1486 'form1' : {
1487 'format' : '%(levelname)s ++ %(message)s',
1488 },
1489 },
1490 'handlers' : {
1491 'hand1' : {
1492 'class' : 'logging.StreamHandler',
1493 'formatter' : 'form1',
1494 'level' : 'NOTSET',
1495 'stream' : 'ext://sys.stdout',
1496 },
1497 },
1498 'loggers' : {
1499 'compiler.lexer' : {
1500 'level' : 'DEBUG',
1501 'handlers' : ['hand1'],
1502 },
1503 },
1504 'root' : {
1505 'level' : 'WARNING',
1506 },
1507 }
1508
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001509 # config8 defines both compiler and compiler.lexer
1510 # so compiler.parser should not be disabled (since
1511 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001512 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001513 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001514 'disable_existing_loggers' : False,
1515 'formatters': {
1516 'form1' : {
1517 'format' : '%(levelname)s ++ %(message)s',
1518 },
1519 },
1520 'handlers' : {
1521 'hand1' : {
1522 'class' : 'logging.StreamHandler',
1523 'formatter' : 'form1',
1524 'level' : 'NOTSET',
1525 'stream' : 'ext://sys.stdout',
1526 },
1527 },
1528 'loggers' : {
1529 'compiler' : {
1530 'level' : 'DEBUG',
1531 'handlers' : ['hand1'],
1532 },
1533 'compiler.lexer' : {
1534 },
1535 },
1536 'root' : {
1537 'level' : 'WARNING',
1538 },
1539 }
1540
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001541 # config8a disables existing loggers
1542 config8a = {
1543 'version': 1,
1544 'disable_existing_loggers' : True,
1545 'formatters': {
1546 'form1' : {
1547 'format' : '%(levelname)s ++ %(message)s',
1548 },
1549 },
1550 'handlers' : {
1551 'hand1' : {
1552 'class' : 'logging.StreamHandler',
1553 'formatter' : 'form1',
1554 'level' : 'NOTSET',
1555 'stream' : 'ext://sys.stdout',
1556 },
1557 },
1558 'loggers' : {
1559 'compiler' : {
1560 'level' : 'DEBUG',
1561 'handlers' : ['hand1'],
1562 },
1563 'compiler.lexer' : {
1564 },
1565 },
1566 'root' : {
1567 'level' : 'WARNING',
1568 },
1569 }
1570
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001571 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001572 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001573 'formatters': {
1574 'form1' : {
1575 'format' : '%(levelname)s ++ %(message)s',
1576 },
1577 },
1578 'handlers' : {
1579 'hand1' : {
1580 'class' : 'logging.StreamHandler',
1581 'formatter' : 'form1',
1582 'level' : 'WARNING',
1583 'stream' : 'ext://sys.stdout',
1584 },
1585 },
1586 'loggers' : {
1587 'compiler.parser' : {
1588 'level' : 'WARNING',
1589 'handlers' : ['hand1'],
1590 },
1591 },
1592 'root' : {
1593 'level' : 'NOTSET',
1594 },
1595 }
1596
1597 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001598 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001599 'incremental' : True,
1600 'handlers' : {
1601 'hand1' : {
1602 'level' : 'WARNING',
1603 },
1604 },
1605 'loggers' : {
1606 'compiler.parser' : {
1607 'level' : 'INFO',
1608 },
1609 },
1610 }
1611
1612 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001613 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001614 'incremental' : True,
1615 'handlers' : {
1616 'hand1' : {
1617 'level' : 'INFO',
1618 },
1619 },
1620 'loggers' : {
1621 'compiler.parser' : {
1622 'level' : 'INFO',
1623 },
1624 },
1625 }
1626
1627 #As config1 but with a filter added
1628 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001629 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001630 'formatters': {
1631 'form1' : {
1632 'format' : '%(levelname)s ++ %(message)s',
1633 },
1634 },
1635 'filters' : {
1636 'filt1' : {
1637 'name' : 'compiler.parser',
1638 },
1639 },
1640 'handlers' : {
1641 'hand1' : {
1642 'class' : 'logging.StreamHandler',
1643 'formatter' : 'form1',
1644 'level' : 'NOTSET',
1645 'stream' : 'ext://sys.stdout',
1646 'filters' : ['filt1'],
1647 },
1648 },
1649 'loggers' : {
1650 'compiler.parser' : {
1651 'level' : 'DEBUG',
1652 'filters' : ['filt1'],
1653 },
1654 },
1655 'root' : {
1656 'level' : 'WARNING',
1657 'handlers' : ['hand1'],
1658 },
1659 }
1660
1661 #As config1 but using cfg:// references
1662 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001663 'version': 1,
1664 'true_formatters': {
1665 'form1' : {
1666 'format' : '%(levelname)s ++ %(message)s',
1667 },
1668 },
1669 'handler_configs': {
1670 'hand1' : {
1671 'class' : 'logging.StreamHandler',
1672 'formatter' : 'form1',
1673 'level' : 'NOTSET',
1674 'stream' : 'ext://sys.stdout',
1675 },
1676 },
1677 'formatters' : 'cfg://true_formatters',
1678 'handlers' : {
1679 'hand1' : 'cfg://handler_configs[hand1]',
1680 },
1681 'loggers' : {
1682 'compiler.parser' : {
1683 'level' : 'DEBUG',
1684 'handlers' : ['hand1'],
1685 },
1686 },
1687 'root' : {
1688 'level' : 'WARNING',
1689 },
1690 }
1691
1692 #As config11 but missing the version key
1693 config12 = {
1694 'true_formatters': {
1695 'form1' : {
1696 'format' : '%(levelname)s ++ %(message)s',
1697 },
1698 },
1699 'handler_configs': {
1700 'hand1' : {
1701 'class' : 'logging.StreamHandler',
1702 'formatter' : 'form1',
1703 'level' : 'NOTSET',
1704 'stream' : 'ext://sys.stdout',
1705 },
1706 },
1707 'formatters' : 'cfg://true_formatters',
1708 'handlers' : {
1709 'hand1' : 'cfg://handler_configs[hand1]',
1710 },
1711 'loggers' : {
1712 'compiler.parser' : {
1713 'level' : 'DEBUG',
1714 'handlers' : ['hand1'],
1715 },
1716 },
1717 'root' : {
1718 'level' : 'WARNING',
1719 },
1720 }
1721
1722 #As config11 but using an unsupported version
1723 config13 = {
1724 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001725 'true_formatters': {
1726 'form1' : {
1727 'format' : '%(levelname)s ++ %(message)s',
1728 },
1729 },
1730 'handler_configs': {
1731 'hand1' : {
1732 'class' : 'logging.StreamHandler',
1733 'formatter' : 'form1',
1734 'level' : 'NOTSET',
1735 'stream' : 'ext://sys.stdout',
1736 },
1737 },
1738 'formatters' : 'cfg://true_formatters',
1739 'handlers' : {
1740 'hand1' : 'cfg://handler_configs[hand1]',
1741 },
1742 'loggers' : {
1743 'compiler.parser' : {
1744 'level' : 'DEBUG',
1745 'handlers' : ['hand1'],
1746 },
1747 },
1748 'root' : {
1749 'level' : 'WARNING',
1750 },
1751 }
1752
1753 def apply_config(self, conf):
1754 logging.config.dictConfig(conf)
1755
1756 def test_config0_ok(self):
1757 # A simple config which overrides the default settings.
1758 with captured_stdout() as output:
1759 self.apply_config(self.config0)
1760 logger = logging.getLogger()
1761 # Won't output anything
1762 logger.info(self.next_message())
1763 # Outputs a message
1764 logger.error(self.next_message())
1765 self.assert_log_lines([
1766 ('ERROR', '2'),
1767 ], stream=output)
1768 # Original logger output is empty.
1769 self.assert_log_lines([])
1770
1771 def test_config1_ok(self, config=config1):
1772 # A config defining a sub-parser as well.
1773 with captured_stdout() as output:
1774 self.apply_config(config)
1775 logger = logging.getLogger("compiler.parser")
1776 # Both will output a message
1777 logger.info(self.next_message())
1778 logger.error(self.next_message())
1779 self.assert_log_lines([
1780 ('INFO', '1'),
1781 ('ERROR', '2'),
1782 ], stream=output)
1783 # Original logger output is empty.
1784 self.assert_log_lines([])
1785
1786 def test_config2_failure(self):
1787 # A simple config which overrides the default settings.
1788 self.assertRaises(Exception, self.apply_config, self.config2)
1789
1790 def test_config2a_failure(self):
1791 # A simple config which overrides the default settings.
1792 self.assertRaises(Exception, self.apply_config, self.config2a)
1793
1794 def test_config2b_failure(self):
1795 # A simple config which overrides the default settings.
1796 self.assertRaises(Exception, self.apply_config, self.config2b)
1797
1798 def test_config3_failure(self):
1799 # A simple config which overrides the default settings.
1800 self.assertRaises(Exception, self.apply_config, self.config3)
1801
1802 def test_config4_ok(self):
1803 # A config specifying a custom formatter class.
1804 with captured_stdout() as output:
1805 self.apply_config(self.config4)
1806 #logger = logging.getLogger()
1807 try:
1808 raise RuntimeError()
1809 except RuntimeError:
1810 logging.exception("just testing")
1811 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001812 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001813 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1814 # Original logger output is empty
1815 self.assert_log_lines([])
1816
1817 def test_config4a_ok(self):
1818 # A config specifying a custom formatter class.
1819 with captured_stdout() as output:
1820 self.apply_config(self.config4a)
1821 #logger = logging.getLogger()
1822 try:
1823 raise RuntimeError()
1824 except RuntimeError:
1825 logging.exception("just testing")
1826 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001827 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001828 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1829 # Original logger output is empty
1830 self.assert_log_lines([])
1831
1832 def test_config5_ok(self):
1833 self.test_config1_ok(config=self.config5)
1834
1835 def test_config6_failure(self):
1836 self.assertRaises(Exception, self.apply_config, self.config6)
1837
1838 def test_config7_ok(self):
1839 with captured_stdout() as output:
1840 self.apply_config(self.config1)
1841 logger = logging.getLogger("compiler.parser")
1842 # Both will output a message
1843 logger.info(self.next_message())
1844 logger.error(self.next_message())
1845 self.assert_log_lines([
1846 ('INFO', '1'),
1847 ('ERROR', '2'),
1848 ], stream=output)
1849 # Original logger output is empty.
1850 self.assert_log_lines([])
1851 with captured_stdout() as output:
1852 self.apply_config(self.config7)
1853 logger = logging.getLogger("compiler.parser")
1854 self.assertTrue(logger.disabled)
1855 logger = logging.getLogger("compiler.lexer")
1856 # Both will output a message
1857 logger.info(self.next_message())
1858 logger.error(self.next_message())
1859 self.assert_log_lines([
1860 ('INFO', '3'),
1861 ('ERROR', '4'),
1862 ], stream=output)
1863 # Original logger output is empty.
1864 self.assert_log_lines([])
1865
1866 #Same as test_config_7_ok but don't disable old loggers.
1867 def test_config_8_ok(self):
1868 with captured_stdout() as output:
1869 self.apply_config(self.config1)
1870 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001871 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001872 logger.info(self.next_message())
1873 logger.error(self.next_message())
1874 self.assert_log_lines([
1875 ('INFO', '1'),
1876 ('ERROR', '2'),
1877 ], stream=output)
1878 # Original logger output is empty.
1879 self.assert_log_lines([])
1880 with captured_stdout() as output:
1881 self.apply_config(self.config8)
1882 logger = logging.getLogger("compiler.parser")
1883 self.assertFalse(logger.disabled)
1884 # Both will output a message
1885 logger.info(self.next_message())
1886 logger.error(self.next_message())
1887 logger = logging.getLogger("compiler.lexer")
1888 # Both will output a message
1889 logger.info(self.next_message())
1890 logger.error(self.next_message())
1891 self.assert_log_lines([
1892 ('INFO', '3'),
1893 ('ERROR', '4'),
1894 ('INFO', '5'),
1895 ('ERROR', '6'),
1896 ], stream=output)
1897 # Original logger output is empty.
1898 self.assert_log_lines([])
1899
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001900 def test_config_8a_ok(self):
1901 with captured_stdout() as output:
1902 self.apply_config(self.config1a)
1903 logger = logging.getLogger("compiler.parser")
1904 # See issue #11424. compiler-hyphenated sorts
1905 # between compiler and compiler.xyz and this
1906 # was preventing compiler.xyz from being included
1907 # in the child loggers of compiler because of an
1908 # overzealous loop termination condition.
1909 hyphenated = logging.getLogger('compiler-hyphenated')
1910 # All will output a message
1911 logger.info(self.next_message())
1912 logger.error(self.next_message())
1913 hyphenated.critical(self.next_message())
1914 self.assert_log_lines([
1915 ('INFO', '1'),
1916 ('ERROR', '2'),
1917 ('CRITICAL', '3'),
1918 ], stream=output)
1919 # Original logger output is empty.
1920 self.assert_log_lines([])
1921 with captured_stdout() as output:
1922 self.apply_config(self.config8a)
1923 logger = logging.getLogger("compiler.parser")
1924 self.assertFalse(logger.disabled)
1925 # Both will output a message
1926 logger.info(self.next_message())
1927 logger.error(self.next_message())
1928 logger = logging.getLogger("compiler.lexer")
1929 # Both will output a message
1930 logger.info(self.next_message())
1931 logger.error(self.next_message())
1932 # Will not appear
1933 hyphenated.critical(self.next_message())
1934 self.assert_log_lines([
1935 ('INFO', '4'),
1936 ('ERROR', '5'),
1937 ('INFO', '6'),
1938 ('ERROR', '7'),
1939 ], stream=output)
1940 # Original logger output is empty.
1941 self.assert_log_lines([])
1942
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001943 def test_config_9_ok(self):
1944 with captured_stdout() as output:
1945 self.apply_config(self.config9)
1946 logger = logging.getLogger("compiler.parser")
1947 #Nothing will be output since both handler and logger are set to WARNING
1948 logger.info(self.next_message())
1949 self.assert_log_lines([], stream=output)
1950 self.apply_config(self.config9a)
1951 #Nothing will be output since both handler is still set to WARNING
1952 logger.info(self.next_message())
1953 self.assert_log_lines([], stream=output)
1954 self.apply_config(self.config9b)
1955 #Message should now be output
1956 logger.info(self.next_message())
1957 self.assert_log_lines([
1958 ('INFO', '3'),
1959 ], stream=output)
1960
1961 def test_config_10_ok(self):
1962 with captured_stdout() as output:
1963 self.apply_config(self.config10)
1964 logger = logging.getLogger("compiler.parser")
1965 logger.warning(self.next_message())
1966 logger = logging.getLogger('compiler')
1967 #Not output, because filtered
1968 logger.warning(self.next_message())
1969 logger = logging.getLogger('compiler.lexer')
1970 #Not output, because filtered
1971 logger.warning(self.next_message())
1972 logger = logging.getLogger("compiler.parser.codegen")
1973 #Output, as not filtered
1974 logger.error(self.next_message())
1975 self.assert_log_lines([
1976 ('WARNING', '1'),
1977 ('ERROR', '4'),
1978 ], stream=output)
1979
1980 def test_config11_ok(self):
1981 self.test_config1_ok(self.config11)
1982
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001983 def test_config12_failure(self):
1984 self.assertRaises(Exception, self.apply_config, self.config12)
1985
1986 def test_config13_failure(self):
1987 self.assertRaises(Exception, self.apply_config, self.config13)
1988
Victor Stinner45df8202010-04-28 22:31:17 +00001989 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001990 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001991 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001992 # Ask for a randomly assigned port (by using port 0)
1993 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001994 t.start()
1995 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001996 # Now get the port allocated
1997 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001998 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001999 try:
2000 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2001 sock.settimeout(2.0)
2002 sock.connect(('localhost', port))
2003
2004 slen = struct.pack('>L', len(text))
2005 s = slen + text
2006 sentsofar = 0
2007 left = len(s)
2008 while left > 0:
2009 sent = sock.send(s[sentsofar:])
2010 sentsofar += sent
2011 left -= sent
2012 sock.close()
2013 finally:
2014 t.ready.wait(2.0)
2015 logging.config.stopListening()
2016 t.join(2.0)
2017
2018 def test_listen_config_10_ok(self):
2019 with captured_stdout() as output:
2020 self.setup_via_listener(json.dumps(self.config10))
2021 logger = logging.getLogger("compiler.parser")
2022 logger.warning(self.next_message())
2023 logger = logging.getLogger('compiler')
2024 #Not output, because filtered
2025 logger.warning(self.next_message())
2026 logger = logging.getLogger('compiler.lexer')
2027 #Not output, because filtered
2028 logger.warning(self.next_message())
2029 logger = logging.getLogger("compiler.parser.codegen")
2030 #Output, as not filtered
2031 logger.error(self.next_message())
2032 self.assert_log_lines([
2033 ('WARNING', '1'),
2034 ('ERROR', '4'),
2035 ], stream=output)
2036
2037 def test_listen_config_1_ok(self):
2038 with captured_stdout() as output:
2039 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2040 logger = logging.getLogger("compiler.parser")
2041 # Both will output a message
2042 logger.info(self.next_message())
2043 logger.error(self.next_message())
2044 self.assert_log_lines([
2045 ('INFO', '1'),
2046 ('ERROR', '2'),
2047 ], stream=output)
2048 # Original logger output is empty.
2049 self.assert_log_lines([])
2050
Vinay Sajip373baef2011-04-26 20:05:24 +01002051 def test_baseconfig(self):
2052 d = {
2053 'atuple': (1, 2, 3),
2054 'alist': ['a', 'b', 'c'],
2055 'adict': {'d': 'e', 'f': 3 },
2056 'nest1': ('g', ('h', 'i'), 'j'),
2057 'nest2': ['k', ['l', 'm'], 'n'],
2058 'nest3': ['o', 'cfg://alist', 'p'],
2059 }
2060 bc = logging.config.BaseConfigurator(d)
2061 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2062 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2063 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2064 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2065 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2066 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2067 v = bc.convert('cfg://nest3')
2068 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2069 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2070 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2071 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002072
2073class ManagerTest(BaseTest):
2074 def test_manager_loggerclass(self):
2075 logged = []
2076
2077 class MyLogger(logging.Logger):
2078 def _log(self, level, msg, args, exc_info=None, extra=None):
2079 logged.append(msg)
2080
2081 man = logging.Manager(None)
2082 self.assertRaises(TypeError, man.setLoggerClass, int)
2083 man.setLoggerClass(MyLogger)
2084 logger = man.getLogger('test')
2085 logger.warning('should appear in logged')
2086 logging.warning('should not appear in logged')
2087
2088 self.assertEqual(logged, ['should appear in logged'])
2089
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002090 def test_set_log_record_factory(self):
2091 man = logging.Manager(None)
2092 expected = object()
2093 man.setLogRecordFactory(expected)
2094 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002095
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002096class ChildLoggerTest(BaseTest):
2097 def test_child_loggers(self):
2098 r = logging.getLogger()
2099 l1 = logging.getLogger('abc')
2100 l2 = logging.getLogger('def.ghi')
2101 c1 = r.getChild('xyz')
2102 c2 = r.getChild('uvw.xyz')
2103 self.assertTrue(c1 is logging.getLogger('xyz'))
2104 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2105 c1 = l1.getChild('def')
2106 c2 = c1.getChild('ghi')
2107 c3 = l1.getChild('def.ghi')
2108 self.assertTrue(c1 is logging.getLogger('abc.def'))
2109 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2110 self.assertTrue(c2 is c3)
2111
2112
Vinay Sajip6fac8172010-10-19 20:44:14 +00002113class DerivedLogRecord(logging.LogRecord):
2114 pass
2115
Vinay Sajip61561522010-12-03 11:50:38 +00002116class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002117
2118 def setUp(self):
2119 class CheckingFilter(logging.Filter):
2120 def __init__(self, cls):
2121 self.cls = cls
2122
2123 def filter(self, record):
2124 t = type(record)
2125 if t is not self.cls:
2126 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2127 self.cls)
2128 raise TypeError(msg)
2129 return True
2130
2131 BaseTest.setUp(self)
2132 self.filter = CheckingFilter(DerivedLogRecord)
2133 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002134 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002135
2136 def tearDown(self):
2137 self.root_logger.removeFilter(self.filter)
2138 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002139 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002140
2141 def test_logrecord_class(self):
2142 self.assertRaises(TypeError, self.root_logger.warning,
2143 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002144 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002145 self.root_logger.error(self.next_message())
2146 self.assert_log_lines([
2147 ('root', 'ERROR', '2'),
2148 ])
2149
2150
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002151class QueueHandlerTest(BaseTest):
2152 # Do not bother with a logger name group.
2153 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2154
2155 def setUp(self):
2156 BaseTest.setUp(self)
2157 self.queue = queue.Queue(-1)
2158 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2159 self.que_logger = logging.getLogger('que')
2160 self.que_logger.propagate = False
2161 self.que_logger.setLevel(logging.WARNING)
2162 self.que_logger.addHandler(self.que_hdlr)
2163
2164 def tearDown(self):
2165 self.que_hdlr.close()
2166 BaseTest.tearDown(self)
2167
2168 def test_queue_handler(self):
2169 self.que_logger.debug(self.next_message())
2170 self.assertRaises(queue.Empty, self.queue.get_nowait)
2171 self.que_logger.info(self.next_message())
2172 self.assertRaises(queue.Empty, self.queue.get_nowait)
2173 msg = self.next_message()
2174 self.que_logger.warning(msg)
2175 data = self.queue.get_nowait()
2176 self.assertTrue(isinstance(data, logging.LogRecord))
2177 self.assertEqual(data.name, self.que_logger.name)
2178 self.assertEqual((data.msg, data.args), (msg, None))
2179
Vinay Sajipe723e962011-04-15 22:27:17 +01002180 def test_queue_listener(self):
2181 handler = TestHandler(Matcher())
2182 listener = logging.handlers.QueueListener(self.queue, handler)
2183 listener.start()
2184 try:
2185 self.que_logger.warning(self.next_message())
2186 self.que_logger.error(self.next_message())
2187 self.que_logger.critical(self.next_message())
2188 finally:
2189 listener.stop()
2190 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2191 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2192 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2193
Vinay Sajip37eb3382011-04-26 20:26:41 +01002194ZERO = datetime.timedelta(0)
2195
2196class UTC(datetime.tzinfo):
2197 def utcoffset(self, dt):
2198 return ZERO
2199
2200 dst = utcoffset
2201
2202 def tzname(self, dt):
2203 return 'UTC'
2204
2205utc = UTC()
Vinay Sajipe723e962011-04-15 22:27:17 +01002206
Vinay Sajipa39c5712010-10-25 13:57:39 +00002207class FormatterTest(unittest.TestCase):
2208 def setUp(self):
2209 self.common = {
2210 'name': 'formatter.test',
2211 'level': logging.DEBUG,
2212 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2213 'lineno': 42,
2214 'exc_info': None,
2215 'func': None,
2216 'msg': 'Message with %d %s',
2217 'args': (2, 'placeholders'),
2218 }
2219 self.variants = {
2220 }
2221
2222 def get_record(self, name=None):
2223 result = dict(self.common)
2224 if name is not None:
2225 result.update(self.variants[name])
2226 return logging.makeLogRecord(result)
2227
2228 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002229 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002230 r = self.get_record()
2231 f = logging.Formatter('${%(message)s}')
2232 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2233 f = logging.Formatter('%(random)s')
2234 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002235 self.assertFalse(f.usesTime())
2236 f = logging.Formatter('%(asctime)s')
2237 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002238 f = logging.Formatter('%(asctime)-15s')
2239 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002240 f = logging.Formatter('asctime')
2241 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002242
2243 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002244 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002245 r = self.get_record()
2246 f = logging.Formatter('$%{message}%$', style='{')
2247 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2248 f = logging.Formatter('{random}', style='{')
2249 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002250 self.assertFalse(f.usesTime())
2251 f = logging.Formatter('{asctime}', style='{')
2252 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002253 f = logging.Formatter('{asctime!s:15}', style='{')
2254 self.assertTrue(f.usesTime())
2255 f = logging.Formatter('{asctime:15}', style='{')
2256 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002257 f = logging.Formatter('asctime', style='{')
2258 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002259
2260 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002261 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002262 r = self.get_record()
2263 f = logging.Formatter('$message', style='$')
2264 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2265 f = logging.Formatter('$$%${message}%$$', style='$')
2266 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2267 f = logging.Formatter('${random}', style='$')
2268 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002269 self.assertFalse(f.usesTime())
2270 f = logging.Formatter('${asctime}', style='$')
2271 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002272 f = logging.Formatter('${asctime', style='$')
2273 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002274 f = logging.Formatter('$asctime', style='$')
2275 self.assertTrue(f.usesTime())
2276 f = logging.Formatter('asctime', style='$')
2277 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002278
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002279 def test_invalid_style(self):
2280 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2281
Vinay Sajipd972d8f2011-04-26 20:51:07 +01002282 def disabled_test_time(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002283 r = self.get_record()
Vinay Sajip37eb3382011-04-26 20:26:41 +01002284 dt = datetime.datetime(1993,4,21,8,3,0,0,utc)
2285 r.created = time.mktime(dt.utctimetuple())
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002286 r.msecs = 123
2287 f = logging.Formatter('%(asctime)s %(message)s')
Vinay Sajip37eb3382011-04-26 20:26:41 +01002288 f.converter = time.gmtime
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002289 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2290 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002291 f.format(r)
2292 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2293
2294class TestBufferingFormatter(logging.BufferingFormatter):
2295 def formatHeader(self, records):
2296 return '[(%d)' % len(records)
2297
2298 def formatFooter(self, records):
2299 return '(%d)]' % len(records)
2300
2301class BufferingFormatterTest(unittest.TestCase):
2302 def setUp(self):
2303 self.records = [
2304 logging.makeLogRecord({'msg': 'one'}),
2305 logging.makeLogRecord({'msg': 'two'}),
2306 ]
2307
2308 def test_default(self):
2309 f = logging.BufferingFormatter()
2310 self.assertEqual('', f.format([]))
2311 self.assertEqual('onetwo', f.format(self.records))
2312
2313 def test_custom(self):
2314 f = TestBufferingFormatter()
2315 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2316 lf = logging.Formatter('<%(message)s>')
2317 f = TestBufferingFormatter(lf)
2318 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002319
2320class ExceptionTest(BaseTest):
2321 def test_formatting(self):
2322 r = self.root_logger
2323 h = RecordingHandler()
2324 r.addHandler(h)
2325 try:
2326 raise RuntimeError('deliberate mistake')
2327 except:
2328 logging.exception('failed', stack_info=True)
2329 r.removeHandler(h)
2330 h.close()
2331 r = h.records[0]
2332 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2333 'call last):\n'))
2334 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2335 'deliberate mistake'))
2336 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2337 'call last):\n'))
2338 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2339 'stack_info=True)'))
2340
2341
Vinay Sajip5a27d402010-12-10 11:42:57 +00002342class LastResortTest(BaseTest):
2343 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002344 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002345 root = self.root_logger
2346 root.removeHandler(self.root_hdlr)
2347 old_stderr = sys.stderr
2348 old_lastresort = logging.lastResort
2349 old_raise_exceptions = logging.raiseExceptions
2350 try:
2351 sys.stderr = sio = io.StringIO()
2352 root.warning('This is your final chance!')
2353 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2354 #No handlers and no last resort, so 'No handlers' message
2355 logging.lastResort = None
2356 sys.stderr = sio = io.StringIO()
2357 root.warning('This is your final chance!')
2358 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2359 # 'No handlers' message only printed once
2360 sys.stderr = sio = io.StringIO()
2361 root.warning('This is your final chance!')
2362 self.assertEqual(sio.getvalue(), '')
2363 root.manager.emittedNoHandlerWarning = False
2364 #If raiseExceptions is False, no message is printed
2365 logging.raiseExceptions = False
2366 sys.stderr = sio = io.StringIO()
2367 root.warning('This is your final chance!')
2368 self.assertEqual(sio.getvalue(), '')
2369 finally:
2370 sys.stderr = old_stderr
2371 root.addHandler(self.root_hdlr)
2372 logging.lastResort = old_lastresort
2373 logging.raiseExceptions = old_raise_exceptions
2374
2375
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002376class FakeHandler:
2377
2378 def __init__(self, identifier, called):
2379 for method in ('acquire', 'flush', 'close', 'release'):
2380 setattr(self, method, self.record_call(identifier, method, called))
2381
2382 def record_call(self, identifier, method_name, called):
2383 def inner():
2384 called.append('{} - {}'.format(identifier, method_name))
2385 return inner
2386
2387
2388class RecordingHandler(logging.NullHandler):
2389
2390 def __init__(self, *args, **kwargs):
2391 super(RecordingHandler, self).__init__(*args, **kwargs)
2392 self.records = []
2393
2394 def handle(self, record):
2395 """Keep track of all the emitted records."""
2396 self.records.append(record)
2397
2398
2399class ShutdownTest(BaseTest):
2400
Vinay Sajip5e66b162011-04-20 15:41:14 +01002401 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002402
2403 def setUp(self):
2404 super(ShutdownTest, self).setUp()
2405 self.called = []
2406
2407 raise_exceptions = logging.raiseExceptions
2408 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2409
2410 def raise_error(self, error):
2411 def inner():
2412 raise error()
2413 return inner
2414
2415 def test_no_failure(self):
2416 # create some fake handlers
2417 handler0 = FakeHandler(0, self.called)
2418 handler1 = FakeHandler(1, self.called)
2419 handler2 = FakeHandler(2, self.called)
2420
2421 # create live weakref to those handlers
2422 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2423
2424 logging.shutdown(handlerList=list(handlers))
2425
2426 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2427 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2428 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2429 self.assertEqual(expected, self.called)
2430
2431 def _test_with_failure_in_method(self, method, error):
2432 handler = FakeHandler(0, self.called)
2433 setattr(handler, method, self.raise_error(error))
2434 handlers = [logging.weakref.ref(handler)]
2435
2436 logging.shutdown(handlerList=list(handlers))
2437
2438 self.assertEqual('0 - release', self.called[-1])
2439
2440 def test_with_ioerror_in_acquire(self):
2441 self._test_with_failure_in_method('acquire', IOError)
2442
2443 def test_with_ioerror_in_flush(self):
2444 self._test_with_failure_in_method('flush', IOError)
2445
2446 def test_with_ioerror_in_close(self):
2447 self._test_with_failure_in_method('close', IOError)
2448
2449 def test_with_valueerror_in_acquire(self):
2450 self._test_with_failure_in_method('acquire', ValueError)
2451
2452 def test_with_valueerror_in_flush(self):
2453 self._test_with_failure_in_method('flush', ValueError)
2454
2455 def test_with_valueerror_in_close(self):
2456 self._test_with_failure_in_method('close', ValueError)
2457
2458 def test_with_other_error_in_acquire_without_raise(self):
2459 logging.raiseExceptions = False
2460 self._test_with_failure_in_method('acquire', IndexError)
2461
2462 def test_with_other_error_in_flush_without_raise(self):
2463 logging.raiseExceptions = False
2464 self._test_with_failure_in_method('flush', IndexError)
2465
2466 def test_with_other_error_in_close_without_raise(self):
2467 logging.raiseExceptions = False
2468 self._test_with_failure_in_method('close', IndexError)
2469
2470 def test_with_other_error_in_acquire_with_raise(self):
2471 logging.raiseExceptions = True
2472 self.assertRaises(IndexError, self._test_with_failure_in_method,
2473 'acquire', IndexError)
2474
2475 def test_with_other_error_in_flush_with_raise(self):
2476 logging.raiseExceptions = True
2477 self.assertRaises(IndexError, self._test_with_failure_in_method,
2478 'flush', IndexError)
2479
2480 def test_with_other_error_in_close_with_raise(self):
2481 logging.raiseExceptions = True
2482 self.assertRaises(IndexError, self._test_with_failure_in_method,
2483 'close', IndexError)
2484
2485
2486class ModuleLevelMiscTest(BaseTest):
2487
Vinay Sajip5e66b162011-04-20 15:41:14 +01002488 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002489
2490 def test_disable(self):
2491 old_disable = logging.root.manager.disable
2492 # confirm our assumptions are correct
2493 assert old_disable == 0
2494 self.addCleanup(lambda: logging.disable(old_disable))
2495
2496 logging.disable(83)
2497 self.assertEqual(logging.root.manager.disable, 83)
2498
2499 def _test_log(self, method, level=None):
2500 called = []
2501 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002502 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002503
2504 recording = RecordingHandler()
2505 logging.root.addHandler(recording)
2506
2507 log_method = getattr(logging, method)
2508 if level is not None:
2509 log_method(level, "test me: %r", recording)
2510 else:
2511 log_method("test me: %r", recording)
2512
2513 self.assertEqual(len(recording.records), 1)
2514 record = recording.records[0]
2515 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2516
2517 expected_level = level if level is not None else getattr(logging, method.upper())
2518 self.assertEqual(record.levelno, expected_level)
2519
2520 # basicConfig was not called!
2521 self.assertEqual(called, [])
2522
2523 def test_log(self):
2524 self._test_log('log', logging.ERROR)
2525
2526 def test_debug(self):
2527 self._test_log('debug')
2528
2529 def test_info(self):
2530 self._test_log('info')
2531
2532 def test_warning(self):
2533 self._test_log('warning')
2534
2535 def test_error(self):
2536 self._test_log('error')
2537
2538 def test_critical(self):
2539 self._test_log('critical')
2540
2541 def test_set_logger_class(self):
2542 self.assertRaises(TypeError, logging.setLoggerClass, object)
2543
2544 class MyLogger(logging.Logger):
2545 pass
2546
2547 logging.setLoggerClass(MyLogger)
2548 self.assertEqual(logging.getLoggerClass(), MyLogger)
2549
2550 logging.setLoggerClass(logging.Logger)
2551 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2552
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002553class LogRecordTest(BaseTest):
2554 def test_str_rep(self):
2555 r = logging.makeLogRecord({})
2556 s = str(r)
2557 self.assertTrue(s.startswith('<LogRecord: '))
2558 self.assertTrue(s.endswith('>'))
2559
2560 def test_dict_arg(self):
2561 h = RecordingHandler()
2562 r = logging.getLogger()
2563 r.addHandler(h)
2564 d = {'less' : 'more' }
2565 logging.warning('less is %(less)s', d)
2566 self.assertIs(h.records[0].args, d)
2567 self.assertEqual(h.records[0].message, 'less is more')
2568 r.removeHandler(h)
2569 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002570
2571class BasicConfigTest(unittest.TestCase):
2572
Vinay Sajip95bf5042011-04-20 11:50:56 +01002573 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002574
2575 def setUp(self):
2576 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01002577 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01002578 self.saved_handlers = logging._handlers.copy()
2579 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002580 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002581 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002582 logging.root.handlers = []
2583
2584 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01002585 for h in logging.root.handlers[:]:
2586 logging.root.removeHandler(h)
2587 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002588 super(BasicConfigTest, self).tearDown()
2589
Vinay Sajip3def7e02011-04-20 10:58:06 +01002590 def cleanup(self):
2591 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01002592 logging._handlers.clear()
2593 logging._handlers.update(self.saved_handlers)
2594 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002595 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002596
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002597 def test_no_kwargs(self):
2598 logging.basicConfig()
2599
2600 # handler defaults to a StreamHandler to sys.stderr
2601 self.assertEqual(len(logging.root.handlers), 1)
2602 handler = logging.root.handlers[0]
2603 self.assertIsInstance(handler, logging.StreamHandler)
2604 self.assertEqual(handler.stream, sys.stderr)
2605
2606 formatter = handler.formatter
2607 # format defaults to logging.BASIC_FORMAT
2608 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2609 # datefmt defaults to None
2610 self.assertIsNone(formatter.datefmt)
2611 # style defaults to %
2612 self.assertIsInstance(formatter._style, logging.PercentStyle)
2613
2614 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01002615 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002616
2617 def test_filename(self):
2618 logging.basicConfig(filename='test.log')
2619
2620 self.assertEqual(len(logging.root.handlers), 1)
2621 handler = logging.root.handlers[0]
2622 self.assertIsInstance(handler, logging.FileHandler)
2623
2624 expected = logging.FileHandler('test.log', 'a')
2625 self.addCleanup(expected.close)
2626 self.assertEqual(handler.stream.mode, expected.stream.mode)
2627 self.assertEqual(handler.stream.name, expected.stream.name)
2628
2629 def test_filemode(self):
2630 logging.basicConfig(filename='test.log', filemode='wb')
2631
2632 handler = logging.root.handlers[0]
2633 expected = logging.FileHandler('test.log', 'wb')
2634 self.addCleanup(expected.close)
2635 self.assertEqual(handler.stream.mode, expected.stream.mode)
2636
2637 def test_stream(self):
2638 stream = io.StringIO()
2639 self.addCleanup(stream.close)
2640 logging.basicConfig(stream=stream)
2641
2642 self.assertEqual(len(logging.root.handlers), 1)
2643 handler = logging.root.handlers[0]
2644 self.assertIsInstance(handler, logging.StreamHandler)
2645 self.assertEqual(handler.stream, stream)
2646
2647 def test_format(self):
2648 logging.basicConfig(format='foo')
2649
2650 formatter = logging.root.handlers[0].formatter
2651 self.assertEqual(formatter._style._fmt, 'foo')
2652
2653 def test_datefmt(self):
2654 logging.basicConfig(datefmt='bar')
2655
2656 formatter = logging.root.handlers[0].formatter
2657 self.assertEqual(formatter.datefmt, 'bar')
2658
2659 def test_style(self):
2660 logging.basicConfig(style='$')
2661
2662 formatter = logging.root.handlers[0].formatter
2663 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2664
2665 def test_level(self):
2666 old_level = logging.root.level
2667 self.addCleanup(lambda: logging.root.setLevel(old_level))
2668
2669 logging.basicConfig(level=57)
2670 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002671 # Test that second call has no effect
2672 logging.basicConfig(level=58)
2673 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002674
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002675 def test_incompatible(self):
2676 assertRaises = self.assertRaises
2677 handlers = [logging.StreamHandler()]
2678 stream = sys.stderr
2679 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2680 stream=stream)
2681 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2682 handlers=handlers)
2683 assertRaises(ValueError, logging.basicConfig, stream=stream,
2684 handlers=handlers)
2685
2686 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002687 handlers = [
2688 logging.StreamHandler(),
2689 logging.StreamHandler(sys.stdout),
2690 logging.StreamHandler(),
2691 ]
2692 f = logging.Formatter()
2693 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002694 logging.basicConfig(handlers=handlers)
2695 self.assertIs(handlers[0], logging.root.handlers[0])
2696 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002697 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002698 self.assertIsNotNone(handlers[0].formatter)
2699 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002700 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002701 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2702
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002703 def _test_log(self, method, level=None):
2704 # logging.root has no handlers so basicConfig should be called
2705 called = []
2706
2707 old_basic_config = logging.basicConfig
2708 def my_basic_config(*a, **kw):
2709 old_basic_config()
2710 old_level = logging.root.level
2711 logging.root.setLevel(100) # avoid having messages in stderr
2712 self.addCleanup(lambda: logging.root.setLevel(old_level))
2713 called.append((a, kw))
2714
2715 patch(self, logging, 'basicConfig', my_basic_config)
2716
2717 log_method = getattr(logging, method)
2718 if level is not None:
2719 log_method(level, "test me")
2720 else:
2721 log_method("test me")
2722
2723 # basicConfig was called with no arguments
2724 self.assertEqual(called, [((), {})])
2725
2726 def test_log(self):
2727 self._test_log('log', logging.WARNING)
2728
2729 def test_debug(self):
2730 self._test_log('debug')
2731
2732 def test_info(self):
2733 self._test_log('info')
2734
2735 def test_warning(self):
2736 self._test_log('warning')
2737
2738 def test_error(self):
2739 self._test_log('error')
2740
2741 def test_critical(self):
2742 self._test_log('critical')
2743
2744
2745class LoggerAdapterTest(unittest.TestCase):
2746
2747 def setUp(self):
2748 super(LoggerAdapterTest, self).setUp()
2749 old_handler_list = logging._handlerList[:]
2750
2751 self.recording = RecordingHandler()
2752 self.logger = logging.root
2753 self.logger.addHandler(self.recording)
2754 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2755 self.addCleanup(self.recording.close)
2756
2757 def cleanup():
2758 logging._handlerList[:] = old_handler_list
2759
2760 self.addCleanup(cleanup)
2761 self.addCleanup(logging.shutdown)
2762 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2763
2764 def test_exception(self):
2765 msg = 'testing exception: %r'
2766 exc = None
2767 try:
2768 assert False
2769 except AssertionError as e:
2770 exc = e
2771 self.adapter.exception(msg, self.recording)
2772
2773 self.assertEqual(len(self.recording.records), 1)
2774 record = self.recording.records[0]
2775 self.assertEqual(record.levelno, logging.ERROR)
2776 self.assertEqual(record.msg, msg)
2777 self.assertEqual(record.args, (self.recording,))
2778 self.assertEqual(record.exc_info,
2779 (exc.__class__, exc, exc.__traceback__))
2780
2781 def test_critical(self):
2782 msg = 'critical test! %r'
2783 self.adapter.critical(msg, self.recording)
2784
2785 self.assertEqual(len(self.recording.records), 1)
2786 record = self.recording.records[0]
2787 self.assertEqual(record.levelno, logging.CRITICAL)
2788 self.assertEqual(record.msg, msg)
2789 self.assertEqual(record.args, (self.recording,))
2790
2791 def test_is_enabled_for(self):
2792 old_disable = self.adapter.logger.manager.disable
2793 self.adapter.logger.manager.disable = 33
2794 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2795 'disable', old_disable))
2796 self.assertFalse(self.adapter.isEnabledFor(32))
2797
2798 def test_has_handlers(self):
2799 self.assertTrue(self.adapter.hasHandlers())
2800
2801 for handler in self.logger.handlers:
2802 self.logger.removeHandler(handler)
2803 assert not self.logger.hasHandlers()
2804
2805 self.assertFalse(self.adapter.hasHandlers())
2806
2807
2808class LoggerTest(BaseTest):
2809
2810 def setUp(self):
2811 super(LoggerTest, self).setUp()
2812 self.recording = RecordingHandler()
2813 self.logger = logging.Logger(name='blah')
2814 self.logger.addHandler(self.recording)
2815 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2816 self.addCleanup(self.recording.close)
2817 self.addCleanup(logging.shutdown)
2818
2819 def test_set_invalid_level(self):
2820 self.assertRaises(TypeError, self.logger.setLevel, object())
2821
2822 def test_exception(self):
2823 msg = 'testing exception: %r'
2824 exc = None
2825 try:
2826 assert False
2827 except AssertionError as e:
2828 exc = e
2829 self.logger.exception(msg, self.recording)
2830
2831 self.assertEqual(len(self.recording.records), 1)
2832 record = self.recording.records[0]
2833 self.assertEqual(record.levelno, logging.ERROR)
2834 self.assertEqual(record.msg, msg)
2835 self.assertEqual(record.args, (self.recording,))
2836 self.assertEqual(record.exc_info,
2837 (exc.__class__, exc, exc.__traceback__))
2838
2839 def test_log_invalid_level_with_raise(self):
2840 old_raise = logging.raiseExceptions
2841 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2842
2843 logging.raiseExceptions = True
2844 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2845
2846 def test_log_invalid_level_no_raise(self):
2847 old_raise = logging.raiseExceptions
2848 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2849
2850 logging.raiseExceptions = False
2851 self.logger.log('10', 'test message') # no exception happens
2852
2853 def test_find_caller_with_stack_info(self):
2854 called = []
2855 patch(self, logging.traceback, 'print_stack',
2856 lambda f, file: called.append(file.getvalue()))
2857
2858 self.logger.findCaller(stack_info=True)
2859
2860 self.assertEqual(len(called), 1)
2861 self.assertEqual('Stack (most recent call last):\n', called[0])
2862
2863 def test_make_record_with_extra_overwrite(self):
2864 name = 'my record'
2865 level = 13
2866 fn = lno = msg = args = exc_info = func = sinfo = None
2867 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2868 exc_info, func, sinfo)
2869
2870 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2871 extra = {key: 'some value'}
2872 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2873 fn, lno, msg, args, exc_info,
2874 extra=extra, sinfo=sinfo)
2875
2876 def test_make_record_with_extra_no_overwrite(self):
2877 name = 'my record'
2878 level = 13
2879 fn = lno = msg = args = exc_info = func = sinfo = None
2880 extra = {'valid_key': 'some value'}
2881 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2882 exc_info, extra=extra, sinfo=sinfo)
2883 self.assertIn('valid_key', result.__dict__)
2884
2885 def test_has_handlers(self):
2886 self.assertTrue(self.logger.hasHandlers())
2887
2888 for handler in self.logger.handlers:
2889 self.logger.removeHandler(handler)
2890 assert not self.logger.hasHandlers()
2891
2892 self.assertFalse(self.logger.hasHandlers())
2893
2894 def test_has_handlers_no_propagate(self):
2895 child_logger = logging.getLogger('blah.child')
2896 child_logger.propagate = False
2897 assert child_logger.handlers == []
2898
2899 self.assertFalse(child_logger.hasHandlers())
2900
2901 def test_is_enabled_for(self):
2902 old_disable = self.logger.manager.disable
2903 self.logger.manager.disable = 23
2904 self.addCleanup(lambda: setattr(self.logger.manager,
2905 'disable', old_disable))
2906 self.assertFalse(self.logger.isEnabledFor(22))
2907
2908
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002909class BaseFileTest(BaseTest):
2910 "Base class for handler tests that write log files"
2911
2912 def setUp(self):
2913 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002914 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2915 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002916 self.rmfiles = []
2917
2918 def tearDown(self):
2919 for fn in self.rmfiles:
2920 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002921 if os.path.exists(self.fn):
2922 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002923 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002924
2925 def assertLogFile(self, filename):
2926 "Assert a log file is there and register it for deletion"
2927 self.assertTrue(os.path.exists(filename),
2928 msg="Log file %r does not exist")
2929 self.rmfiles.append(filename)
2930
2931
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002932class FileHandlerTest(BaseFileTest):
2933 def test_delay(self):
2934 os.unlink(self.fn)
2935 fh = logging.FileHandler(self.fn, delay=True)
2936 self.assertIsNone(fh.stream)
2937 self.assertFalse(os.path.exists(self.fn))
2938 fh.handle(logging.makeLogRecord({}))
2939 self.assertIsNotNone(fh.stream)
2940 self.assertTrue(os.path.exists(self.fn))
2941 fh.close()
2942
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002943class RotatingFileHandlerTest(BaseFileTest):
2944 def next_rec(self):
2945 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2946 self.next_message(), None, None, None)
2947
2948 def test_should_not_rollover(self):
2949 # If maxbytes is zero rollover never occurs
2950 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2951 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002952 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002953
2954 def test_should_rollover(self):
2955 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2956 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002957 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002958
2959 def test_file_created(self):
2960 # checks that the file is created and assumes it was created
2961 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002962 rh = logging.handlers.RotatingFileHandler(self.fn)
2963 rh.emit(self.next_rec())
2964 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002965 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002966
2967 def test_rollover_filenames(self):
2968 rh = logging.handlers.RotatingFileHandler(
2969 self.fn, backupCount=2, maxBytes=1)
2970 rh.emit(self.next_rec())
2971 self.assertLogFile(self.fn)
2972 rh.emit(self.next_rec())
2973 self.assertLogFile(self.fn + ".1")
2974 rh.emit(self.next_rec())
2975 self.assertLogFile(self.fn + ".2")
2976 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002977 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002978
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002979class TimedRotatingFileHandlerTest(BaseFileTest):
2980 # test methods added below
2981 pass
2982
2983def secs(**kw):
2984 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2985
2986for when, exp in (('S', 1),
2987 ('M', 60),
2988 ('H', 60 * 60),
2989 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002990 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002991 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002992 ('W0', secs(days=4, hours=24)),
2993 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002994 def test_compute_rollover(self, when=when, exp=exp):
2995 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002996 self.fn, when=when, interval=1, backupCount=0, utc=True)
2997 currentTime = 0.0
2998 actual = rh.computeRollover(currentTime)
2999 if exp != actual:
3000 # Failures occur on some systems for MIDNIGHT and W0.
3001 # Print detailed calculation for MIDNIGHT so we can try to see
3002 # what's going on
3003 import time
3004 if when == 'MIDNIGHT':
3005 try:
3006 if rh.utc:
3007 t = time.gmtime(currentTime)
3008 else:
3009 t = time.localtime(currentTime)
3010 currentHour = t[3]
3011 currentMinute = t[4]
3012 currentSecond = t[5]
3013 # r is the number of seconds left between now and midnight
3014 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3015 currentMinute) * 60 +
3016 currentSecond)
3017 result = currentTime + r
3018 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3019 print('currentHour: %s' % currentHour, file=sys.stderr)
3020 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3021 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3022 print('r: %s' % r, file=sys.stderr)
3023 print('result: %s' % result, file=sys.stderr)
3024 except Exception:
3025 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3026 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003027 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003028 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3029
Christian Heimes180510d2008-03-03 19:15:45 +00003030# Set the locale to the platform-dependent default. I have no idea
3031# why the test does this, but in any case we save the current locale
3032# first and restore it at the end.
3033@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003034def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003035 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003036 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003037 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003038 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip985ef872011-04-26 19:34:04 +01003039 FormatterTest, BufferingFormatterTest, StreamHandlerTest,
Vinay Sajip61561522010-12-03 11:50:38 +00003040 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003041 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
3042 LoggerAdapterTest, LoggerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003043 FileHandlerTest, RotatingFileHandlerTest,
3044 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003045 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003046 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003047
Christian Heimes180510d2008-03-03 19:15:45 +00003048if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003049 test_main()