blob: 813e62a01425e3d611401aafafa44fcfef734c61 [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
Christian Heimes180510d2008-03-03 19:15:45 +000046import unittest
Georg Brandlf9734072008-12-07 15:30:06 +000047import warnings
Christian Heimes180510d2008-03-03 19:15:45 +000048import weakref
Victor Stinner45df8202010-04-28 22:31:17 +000049try:
50 import threading
51except ImportError:
52 threading = None
Christian Heimes18c66892008-02-17 13:31:39 +000053
54
Christian Heimes180510d2008-03-03 19:15:45 +000055class BaseTest(unittest.TestCase):
56
57 """Base class for logging tests."""
58
59 log_format = "%(name)s -> %(levelname)s: %(message)s"
60 expected_log_pat = r"^([\w.]+) -> ([\w]+): ([\d]+)$"
61 message_num = 0
62
63 def setUp(self):
64 """Setup the default logging stream to an internal StringIO instance,
65 so that we can examine log output as we want."""
66 logger_dict = logging.getLogger().manager.loggerDict
Christian Heimes18c66892008-02-17 13:31:39 +000067 logging._acquireLock()
68 try:
Christian Heimes180510d2008-03-03 19:15:45 +000069 self.saved_handlers = logging._handlers.copy()
70 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000071 self.saved_loggers = saved_loggers = logger_dict.copy()
Christian Heimes180510d2008-03-03 19:15:45 +000072 self.saved_level_names = logging._levelNames.copy()
Vinay Sajip7b60f4e2010-12-27 14:31:52 +000073 self.logger_states = logger_states = {}
74 for name in saved_loggers:
75 logger_states[name] = getattr(saved_loggers[name],
76 'disabled', None)
Christian Heimes18c66892008-02-17 13:31:39 +000077 finally:
78 logging._releaseLock()
79
Benjamin Peterson22005fc2010-04-11 16:25:06 +000080 # Set two unused loggers: one non-ASCII and one Unicode.
81 # This is to test correct operation when sorting existing
82 # loggers in the configuration code. See issue 8201.
Vinay Sajipb4a08092010-09-20 09:55:00 +000083 self.logger1 = logging.getLogger("\xab\xd7\xbb")
84 self.logger2 = logging.getLogger("\u013f\u00d6\u0047")
Benjamin Peterson22005fc2010-04-11 16:25:06 +000085
Christian Heimes180510d2008-03-03 19:15:45 +000086 self.root_logger = logging.getLogger("")
87 self.original_logging_level = self.root_logger.getEffectiveLevel()
88
89 self.stream = io.StringIO()
90 self.root_logger.setLevel(logging.DEBUG)
91 self.root_hdlr = logging.StreamHandler(self.stream)
92 self.root_formatter = logging.Formatter(self.log_format)
93 self.root_hdlr.setFormatter(self.root_formatter)
Vinay Sajip7b0e86e2010-12-30 23:26:50 +000094 if self.logger1.hasHandlers():
95 hlist = self.logger1.handlers + self.root_logger.handlers
96 raise AssertionError('Unexpected handlers: %s' % hlist)
97 if self.logger2.hasHandlers():
98 hlist = self.logger2.handlers + self.root_logger.handlers
99 raise AssertionError('Unexpected handlers: %s' % hlist)
Christian Heimes180510d2008-03-03 19:15:45 +0000100 self.root_logger.addHandler(self.root_hdlr)
Vinay Sajipb4a08092010-09-20 09:55:00 +0000101 self.assertTrue(self.logger1.hasHandlers())
102 self.assertTrue(self.logger2.hasHandlers())
Christian Heimes180510d2008-03-03 19:15:45 +0000103
104 def tearDown(self):
105 """Remove our logging stream, and restore the original logging
106 level."""
107 self.stream.close()
108 self.root_logger.removeHandler(self.root_hdlr)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000109 while self.root_logger.handlers:
110 h = self.root_logger.handlers[0]
111 self.root_logger.removeHandler(h)
112 h.close()
Christian Heimes180510d2008-03-03 19:15:45 +0000113 self.root_logger.setLevel(self.original_logging_level)
114 logging._acquireLock()
115 try:
116 logging._levelNames.clear()
117 logging._levelNames.update(self.saved_level_names)
118 logging._handlers.clear()
119 logging._handlers.update(self.saved_handlers)
120 logging._handlerList[:] = self.saved_handler_list
121 loggerDict = logging.getLogger().manager.loggerDict
122 loggerDict.clear()
123 loggerDict.update(self.saved_loggers)
Vinay Sajip7b60f4e2010-12-27 14:31:52 +0000124 logger_states = self.logger_states
125 for name in self.logger_states:
126 if logger_states[name] is not None:
127 self.saved_loggers[name].disabled = logger_states[name]
Christian Heimes180510d2008-03-03 19:15:45 +0000128 finally:
129 logging._releaseLock()
130
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000131 def assert_log_lines(self, expected_values, stream=None):
Christian Heimes180510d2008-03-03 19:15:45 +0000132 """Match the collected log lines against the regular expression
133 self.expected_log_pat, and compare the extracted group values to
134 the expected_values list of tuples."""
135 stream = stream or self.stream
136 pat = re.compile(self.expected_log_pat)
137 try:
138 stream.reset()
139 actual_lines = stream.readlines()
140 except AttributeError:
141 # StringIO.StringIO lacks a reset() method.
142 actual_lines = stream.getvalue().splitlines()
Ezio Melottib3aedd42010-11-20 19:04:17 +0000143 self.assertEqual(len(actual_lines), len(expected_values),
Vinay Sajip6fac8172010-10-19 20:44:14 +0000144 '%s vs. %s' % (actual_lines, expected_values))
Christian Heimes180510d2008-03-03 19:15:45 +0000145 for actual, expected in zip(actual_lines, expected_values):
146 match = pat.search(actual)
147 if not match:
148 self.fail("Log line does not match expected pattern:\n" +
149 actual)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000150 self.assertEqual(tuple(match.groups()), expected)
Christian Heimes180510d2008-03-03 19:15:45 +0000151 s = stream.read()
152 if s:
153 self.fail("Remaining output at end of log stream:\n" + s)
154
155 def next_message(self):
156 """Generate a message consisting solely of an auto-incrementing
157 integer."""
158 self.message_num += 1
159 return "%d" % self.message_num
160
161
162class BuiltinLevelsTest(BaseTest):
163 """Test builtin levels and their inheritance."""
164
165 def test_flat(self):
166 #Logging levels in a flat logger namespace.
167 m = self.next_message
168
169 ERR = logging.getLogger("ERR")
170 ERR.setLevel(logging.ERROR)
Vinay Sajipc84f0162010-09-21 11:25:39 +0000171 INF = logging.LoggerAdapter(logging.getLogger("INF"), {})
Christian Heimes180510d2008-03-03 19:15:45 +0000172 INF.setLevel(logging.INFO)
173 DEB = logging.getLogger("DEB")
174 DEB.setLevel(logging.DEBUG)
175
176 # These should log.
177 ERR.log(logging.CRITICAL, m())
178 ERR.error(m())
179
180 INF.log(logging.CRITICAL, m())
181 INF.error(m())
182 INF.warn(m())
183 INF.info(m())
184
185 DEB.log(logging.CRITICAL, m())
186 DEB.error(m())
187 DEB.warn (m())
188 DEB.info (m())
189 DEB.debug(m())
190
191 # These should not log.
192 ERR.warn(m())
193 ERR.info(m())
194 ERR.debug(m())
195
196 INF.debug(m())
197
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000198 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000199 ('ERR', 'CRITICAL', '1'),
200 ('ERR', 'ERROR', '2'),
201 ('INF', 'CRITICAL', '3'),
202 ('INF', 'ERROR', '4'),
203 ('INF', 'WARNING', '5'),
204 ('INF', 'INFO', '6'),
205 ('DEB', 'CRITICAL', '7'),
206 ('DEB', 'ERROR', '8'),
207 ('DEB', 'WARNING', '9'),
208 ('DEB', 'INFO', '10'),
209 ('DEB', 'DEBUG', '11'),
210 ])
211
212 def test_nested_explicit(self):
213 # Logging levels in a nested namespace, all explicitly set.
214 m = self.next_message
215
216 INF = logging.getLogger("INF")
217 INF.setLevel(logging.INFO)
218 INF_ERR = logging.getLogger("INF.ERR")
219 INF_ERR.setLevel(logging.ERROR)
220
221 # These should log.
222 INF_ERR.log(logging.CRITICAL, m())
223 INF_ERR.error(m())
224
225 # These should not log.
226 INF_ERR.warn(m())
227 INF_ERR.info(m())
228 INF_ERR.debug(m())
229
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000230 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000231 ('INF.ERR', 'CRITICAL', '1'),
232 ('INF.ERR', 'ERROR', '2'),
233 ])
234
235 def test_nested_inherited(self):
236 #Logging levels in a nested namespace, inherited from parent loggers.
237 m = self.next_message
238
239 INF = logging.getLogger("INF")
240 INF.setLevel(logging.INFO)
241 INF_ERR = logging.getLogger("INF.ERR")
242 INF_ERR.setLevel(logging.ERROR)
243 INF_UNDEF = logging.getLogger("INF.UNDEF")
244 INF_ERR_UNDEF = logging.getLogger("INF.ERR.UNDEF")
245 UNDEF = logging.getLogger("UNDEF")
246
247 # These should log.
248 INF_UNDEF.log(logging.CRITICAL, m())
249 INF_UNDEF.error(m())
250 INF_UNDEF.warn(m())
251 INF_UNDEF.info(m())
252 INF_ERR_UNDEF.log(logging.CRITICAL, m())
253 INF_ERR_UNDEF.error(m())
254
255 # These should not log.
256 INF_UNDEF.debug(m())
257 INF_ERR_UNDEF.warn(m())
258 INF_ERR_UNDEF.info(m())
259 INF_ERR_UNDEF.debug(m())
260
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000261 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000262 ('INF.UNDEF', 'CRITICAL', '1'),
263 ('INF.UNDEF', 'ERROR', '2'),
264 ('INF.UNDEF', 'WARNING', '3'),
265 ('INF.UNDEF', 'INFO', '4'),
266 ('INF.ERR.UNDEF', 'CRITICAL', '5'),
267 ('INF.ERR.UNDEF', 'ERROR', '6'),
268 ])
269
270 def test_nested_with_virtual_parent(self):
271 # Logging levels when some parent does not exist yet.
272 m = self.next_message
273
274 INF = logging.getLogger("INF")
275 GRANDCHILD = logging.getLogger("INF.BADPARENT.UNDEF")
276 CHILD = logging.getLogger("INF.BADPARENT")
277 INF.setLevel(logging.INFO)
278
279 # These should log.
280 GRANDCHILD.log(logging.FATAL, m())
281 GRANDCHILD.info(m())
282 CHILD.log(logging.FATAL, m())
283 CHILD.info(m())
284
285 # These should not log.
286 GRANDCHILD.debug(m())
287 CHILD.debug(m())
288
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000289 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000290 ('INF.BADPARENT.UNDEF', 'CRITICAL', '1'),
291 ('INF.BADPARENT.UNDEF', 'INFO', '2'),
292 ('INF.BADPARENT', 'CRITICAL', '3'),
293 ('INF.BADPARENT', 'INFO', '4'),
294 ])
295
296
297class BasicFilterTest(BaseTest):
298
299 """Test the bundled Filter class."""
300
301 def test_filter(self):
302 # Only messages satisfying the specified criteria pass through the
303 # filter.
304 filter_ = logging.Filter("spam.eggs")
305 handler = self.root_logger.handlers[0]
306 try:
307 handler.addFilter(filter_)
308 spam = logging.getLogger("spam")
309 spam_eggs = logging.getLogger("spam.eggs")
310 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
311 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
312
313 spam.info(self.next_message())
314 spam_eggs.info(self.next_message()) # Good.
315 spam_eggs_fish.info(self.next_message()) # Good.
316 spam_bakedbeans.info(self.next_message())
317
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000318 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000319 ('spam.eggs', 'INFO', '2'),
320 ('spam.eggs.fish', 'INFO', '3'),
321 ])
322 finally:
323 handler.removeFilter(filter_)
324
Vinay Sajip6dbed2e2010-10-19 20:53:01 +0000325 def test_callable_filter(self):
326 # Only messages satisfying the specified criteria pass through the
327 # filter.
328
329 def filterfunc(record):
330 parts = record.name.split('.')
331 prefix = '.'.join(parts[:2])
332 return prefix == 'spam.eggs'
333
334 handler = self.root_logger.handlers[0]
335 try:
336 handler.addFilter(filterfunc)
337 spam = logging.getLogger("spam")
338 spam_eggs = logging.getLogger("spam.eggs")
339 spam_eggs_fish = logging.getLogger("spam.eggs.fish")
340 spam_bakedbeans = logging.getLogger("spam.bakedbeans")
341
342 spam.info(self.next_message())
343 spam_eggs.info(self.next_message()) # Good.
344 spam_eggs_fish.info(self.next_message()) # Good.
345 spam_bakedbeans.info(self.next_message())
346
347 self.assert_log_lines([
348 ('spam.eggs', 'INFO', '2'),
349 ('spam.eggs.fish', 'INFO', '3'),
350 ])
351 finally:
352 handler.removeFilter(filterfunc)
353
Vinay Sajip985ef872011-04-26 19:34:04 +0100354 def test_empty_filter(self):
355 f = logging.Filter()
356 r = logging.makeLogRecord({'name': 'spam.eggs'})
357 self.assertTrue(f.filter(r))
Christian Heimes180510d2008-03-03 19:15:45 +0000358
359#
360# First, we define our levels. There can be as many as you want - the only
361# limitations are that they should be integers, the lowest should be > 0 and
362# larger values mean less information being logged. If you need specific
363# level values which do not fit into these limitations, you can use a
364# mapping dictionary to convert between your application levels and the
365# logging system.
366#
367SILENT = 120
368TACITURN = 119
369TERSE = 118
370EFFUSIVE = 117
371SOCIABLE = 116
372VERBOSE = 115
373TALKATIVE = 114
374GARRULOUS = 113
375CHATTERBOX = 112
376BORING = 111
377
378LEVEL_RANGE = range(BORING, SILENT + 1)
379
380#
381# Next, we define names for our levels. You don't need to do this - in which
382# case the system will use "Level n" to denote the text for the level.
383#
384my_logging_levels = {
385 SILENT : 'Silent',
386 TACITURN : 'Taciturn',
387 TERSE : 'Terse',
388 EFFUSIVE : 'Effusive',
389 SOCIABLE : 'Sociable',
390 VERBOSE : 'Verbose',
391 TALKATIVE : 'Talkative',
392 GARRULOUS : 'Garrulous',
393 CHATTERBOX : 'Chatterbox',
394 BORING : 'Boring',
395}
396
397class GarrulousFilter(logging.Filter):
398
399 """A filter which blocks garrulous messages."""
400
401 def filter(self, record):
402 return record.levelno != GARRULOUS
403
404class VerySpecificFilter(logging.Filter):
405
406 """A filter which blocks sociable and taciturn messages."""
407
408 def filter(self, record):
409 return record.levelno not in [SOCIABLE, TACITURN]
410
411
412class CustomLevelsAndFiltersTest(BaseTest):
413
414 """Test various filtering possibilities with custom logging levels."""
415
416 # Skip the logger name group.
417 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
418
419 def setUp(self):
420 BaseTest.setUp(self)
Benjamin Peterson9451a1c2010-03-13 22:30:34 +0000421 for k, v in my_logging_levels.items():
Christian Heimes180510d2008-03-03 19:15:45 +0000422 logging.addLevelName(k, v)
423
424 def log_at_all_levels(self, logger):
425 for lvl in LEVEL_RANGE:
426 logger.log(lvl, self.next_message())
427
428 def test_logger_filter(self):
429 # Filter at logger level.
430 self.root_logger.setLevel(VERBOSE)
431 # Levels >= 'Verbose' are good.
432 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000433 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000434 ('Verbose', '5'),
435 ('Sociable', '6'),
436 ('Effusive', '7'),
437 ('Terse', '8'),
438 ('Taciturn', '9'),
439 ('Silent', '10'),
440 ])
441
442 def test_handler_filter(self):
443 # Filter at handler level.
444 self.root_logger.handlers[0].setLevel(SOCIABLE)
445 try:
446 # Levels >= 'Sociable' are good.
447 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000448 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000449 ('Sociable', '6'),
450 ('Effusive', '7'),
451 ('Terse', '8'),
452 ('Taciturn', '9'),
453 ('Silent', '10'),
454 ])
455 finally:
456 self.root_logger.handlers[0].setLevel(logging.NOTSET)
457
458 def test_specific_filters(self):
459 # Set a specific filter object on the handler, and then add another
460 # filter object on the logger itself.
461 handler = self.root_logger.handlers[0]
462 specific_filter = None
463 garr = GarrulousFilter()
464 handler.addFilter(garr)
465 try:
466 self.log_at_all_levels(self.root_logger)
467 first_lines = [
468 # Notice how 'Garrulous' is missing
469 ('Boring', '1'),
470 ('Chatterbox', '2'),
471 ('Talkative', '4'),
472 ('Verbose', '5'),
473 ('Sociable', '6'),
474 ('Effusive', '7'),
475 ('Terse', '8'),
476 ('Taciturn', '9'),
477 ('Silent', '10'),
478 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000479 self.assert_log_lines(first_lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000480
481 specific_filter = VerySpecificFilter()
482 self.root_logger.addFilter(specific_filter)
483 self.log_at_all_levels(self.root_logger)
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000484 self.assert_log_lines(first_lines + [
Christian Heimes180510d2008-03-03 19:15:45 +0000485 # Not only 'Garrulous' is still missing, but also 'Sociable'
486 # and 'Taciturn'
487 ('Boring', '11'),
488 ('Chatterbox', '12'),
489 ('Talkative', '14'),
490 ('Verbose', '15'),
491 ('Effusive', '17'),
492 ('Terse', '18'),
493 ('Silent', '20'),
494 ])
495 finally:
496 if specific_filter:
497 self.root_logger.removeFilter(specific_filter)
498 handler.removeFilter(garr)
499
500
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100501class HandlerTest(BaseTest):
502 def test_name(self):
503 h = logging.Handler()
504 h.name = 'generic'
505 self.assertEqual(h.name, 'generic')
506 h.name = 'anothergeneric'
507 self.assertEqual(h.name, 'anothergeneric')
508 self.assertRaises(NotImplementedError, h.emit, None)
509
510 def test_abc(self):
511 pass
512
513class BadStream(object):
514 def write(self, data):
515 raise RuntimeError('deliberate mistake')
516
517class TestStreamHandler(logging.StreamHandler):
518 def handleError(self, record):
519 self.error_record = record
520
521class StreamHandlerTest(BaseTest):
522 def test_error_handling(self):
523 h = TestStreamHandler(BadStream())
524 r = logging.makeLogRecord({})
525 old_raise = logging.raiseExceptions
Vinay Sajip985ef872011-04-26 19:34:04 +0100526 old_stderr = sys.stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100527 try:
528 h.handle(r)
529 self.assertIs(h.error_record, r)
Vinay Sajip985ef872011-04-26 19:34:04 +0100530 h = logging.StreamHandler(BadStream())
531 sys.stderr = sio = io.StringIO()
532 h.handle(r)
533 self.assertTrue('\nRuntimeError: '
534 'deliberate mistake\n' in sio.getvalue())
535 logging.raiseExceptions = False
536 sys.stderr = sio = io.StringIO()
537 h.handle(r)
538 self.assertEqual('', sio.getvalue())
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100539 finally:
540 logging.raiseExceptions = old_raise
Vinay Sajip985ef872011-04-26 19:34:04 +0100541 sys.stderr = old_stderr
Vinay Sajip26fe4b72011-04-26 18:43:05 +0100542
Christian Heimes180510d2008-03-03 19:15:45 +0000543class MemoryHandlerTest(BaseTest):
544
545 """Tests for the MemoryHandler."""
546
547 # Do not bother with a logger name group.
548 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
549
550 def setUp(self):
551 BaseTest.setUp(self)
552 self.mem_hdlr = logging.handlers.MemoryHandler(10, logging.WARNING,
553 self.root_hdlr)
554 self.mem_logger = logging.getLogger('mem')
555 self.mem_logger.propagate = 0
556 self.mem_logger.addHandler(self.mem_hdlr)
557
558 def tearDown(self):
559 self.mem_hdlr.close()
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000560 BaseTest.tearDown(self)
Christian Heimes180510d2008-03-03 19:15:45 +0000561
562 def test_flush(self):
563 # The memory handler flushes to its target handler based on specific
564 # criteria (message count and message level).
565 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000566 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000567 self.mem_logger.info(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000568 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000569 # This will flush because the level is >= logging.WARNING
570 self.mem_logger.warn(self.next_message())
571 lines = [
572 ('DEBUG', '1'),
573 ('INFO', '2'),
574 ('WARNING', '3'),
575 ]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000576 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000577 for n in (4, 14):
578 for i in range(9):
579 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000580 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000581 # This will flush because it's the 10th message since the last
582 # flush.
583 self.mem_logger.debug(self.next_message())
584 lines = lines + [('DEBUG', str(i)) for i in range(n, n + 10)]
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000585 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000586
587 self.mem_logger.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000588 self.assert_log_lines(lines)
Christian Heimes180510d2008-03-03 19:15:45 +0000589
590
591class ExceptionFormatter(logging.Formatter):
592 """A special exception formatter."""
593 def formatException(self, ei):
594 return "Got a [%s]" % ei[0].__name__
595
596
597class ConfigFileTest(BaseTest):
598
599 """Reading logging config from a .ini-style config file."""
600
601 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
602
603 # config0 is a standard configuration.
604 config0 = """
605 [loggers]
606 keys=root
607
608 [handlers]
609 keys=hand1
610
611 [formatters]
612 keys=form1
613
614 [logger_root]
615 level=WARNING
616 handlers=hand1
617
618 [handler_hand1]
619 class=StreamHandler
620 level=NOTSET
621 formatter=form1
622 args=(sys.stdout,)
623
624 [formatter_form1]
625 format=%(levelname)s ++ %(message)s
626 datefmt=
627 """
628
629 # config1 adds a little to the standard configuration.
630 config1 = """
631 [loggers]
632 keys=root,parser
633
634 [handlers]
635 keys=hand1
636
637 [formatters]
638 keys=form1
639
640 [logger_root]
641 level=WARNING
642 handlers=
643
644 [logger_parser]
645 level=DEBUG
646 handlers=hand1
647 propagate=1
648 qualname=compiler.parser
649
650 [handler_hand1]
651 class=StreamHandler
652 level=NOTSET
653 formatter=form1
654 args=(sys.stdout,)
655
656 [formatter_form1]
657 format=%(levelname)s ++ %(message)s
658 datefmt=
659 """
660
Vinay Sajip3f84b072011-03-07 17:49:33 +0000661 # config1a moves the handler to the root.
662 config1a = """
663 [loggers]
664 keys=root,parser
665
666 [handlers]
667 keys=hand1
668
669 [formatters]
670 keys=form1
671
672 [logger_root]
673 level=WARNING
674 handlers=hand1
675
676 [logger_parser]
677 level=DEBUG
678 handlers=
679 propagate=1
680 qualname=compiler.parser
681
682 [handler_hand1]
683 class=StreamHandler
684 level=NOTSET
685 formatter=form1
686 args=(sys.stdout,)
687
688 [formatter_form1]
689 format=%(levelname)s ++ %(message)s
690 datefmt=
691 """
692
Christian Heimes180510d2008-03-03 19:15:45 +0000693 # config2 has a subtle configuration error that should be reported
694 config2 = config1.replace("sys.stdout", "sys.stbout")
695
696 # config3 has a less subtle configuration error
697 config3 = config1.replace("formatter=form1", "formatter=misspelled_name")
698
699 # config4 specifies a custom formatter class to be loaded
700 config4 = """
701 [loggers]
702 keys=root
703
704 [handlers]
705 keys=hand1
706
707 [formatters]
708 keys=form1
709
710 [logger_root]
711 level=NOTSET
712 handlers=hand1
713
714 [handler_hand1]
715 class=StreamHandler
716 level=NOTSET
717 formatter=form1
718 args=(sys.stdout,)
719
720 [formatter_form1]
721 class=""" + __name__ + """.ExceptionFormatter
722 format=%(levelname)s:%(name)s:%(message)s
723 datefmt=
724 """
725
Georg Brandl3dbca812008-07-23 16:10:53 +0000726 # config5 specifies a custom handler class to be loaded
727 config5 = config1.replace('class=StreamHandler', 'class=logging.StreamHandler')
728
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000729 # config6 uses ', ' delimiters in the handlers and formatters sections
730 config6 = """
731 [loggers]
732 keys=root,parser
733
734 [handlers]
735 keys=hand1, hand2
736
737 [formatters]
738 keys=form1, form2
739
740 [logger_root]
741 level=WARNING
742 handlers=
743
744 [logger_parser]
745 level=DEBUG
746 handlers=hand1
747 propagate=1
748 qualname=compiler.parser
749
750 [handler_hand1]
751 class=StreamHandler
752 level=NOTSET
753 formatter=form1
754 args=(sys.stdout,)
755
756 [handler_hand2]
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000757 class=StreamHandler
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000758 level=NOTSET
759 formatter=form1
Benjamin Peterson9aa42992008-09-10 21:57:34 +0000760 args=(sys.stderr,)
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000761
762 [formatter_form1]
763 format=%(levelname)s ++ %(message)s
764 datefmt=
765
766 [formatter_form2]
767 format=%(message)s
768 datefmt=
769 """
770
Vinay Sajip3f84b072011-03-07 17:49:33 +0000771 # config7 adds a compiler logger.
772 config7 = """
773 [loggers]
774 keys=root,parser,compiler
775
776 [handlers]
777 keys=hand1
778
779 [formatters]
780 keys=form1
781
782 [logger_root]
783 level=WARNING
784 handlers=hand1
785
786 [logger_compiler]
787 level=DEBUG
788 handlers=
789 propagate=1
790 qualname=compiler
791
792 [logger_parser]
793 level=DEBUG
794 handlers=
795 propagate=1
796 qualname=compiler.parser
797
798 [handler_hand1]
799 class=StreamHandler
800 level=NOTSET
801 formatter=form1
802 args=(sys.stdout,)
803
804 [formatter_form1]
805 format=%(levelname)s ++ %(message)s
806 datefmt=
807 """
808
Christian Heimes180510d2008-03-03 19:15:45 +0000809 def apply_config(self, conf):
Vinay Sajipdb81c4c2010-02-25 23:13:06 +0000810 file = io.StringIO(textwrap.dedent(conf))
811 logging.config.fileConfig(file)
Christian Heimes180510d2008-03-03 19:15:45 +0000812
813 def test_config0_ok(self):
814 # A simple config file which overrides the default settings.
815 with captured_stdout() as output:
816 self.apply_config(self.config0)
817 logger = logging.getLogger()
818 # Won't output anything
819 logger.info(self.next_message())
820 # Outputs a message
821 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000822 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000823 ('ERROR', '2'),
824 ], stream=output)
825 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000826 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000827
Georg Brandl3dbca812008-07-23 16:10:53 +0000828 def test_config1_ok(self, config=config1):
Christian Heimes180510d2008-03-03 19:15:45 +0000829 # A config file defining a sub-parser as well.
830 with captured_stdout() as output:
Georg Brandl3dbca812008-07-23 16:10:53 +0000831 self.apply_config(config)
Christian Heimes180510d2008-03-03 19:15:45 +0000832 logger = logging.getLogger("compiler.parser")
833 # Both will output a message
834 logger.info(self.next_message())
835 logger.error(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000836 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +0000837 ('INFO', '1'),
838 ('ERROR', '2'),
839 ], stream=output)
840 # Original logger output is empty.
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000841 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000842
843 def test_config2_failure(self):
844 # A simple config file which overrides the default settings.
845 self.assertRaises(Exception, self.apply_config, self.config2)
846
847 def test_config3_failure(self):
848 # A simple config file which overrides the default settings.
849 self.assertRaises(Exception, self.apply_config, self.config3)
850
851 def test_config4_ok(self):
852 # A config file specifying a custom formatter class.
853 with captured_stdout() as output:
854 self.apply_config(self.config4)
855 logger = logging.getLogger()
856 try:
857 raise RuntimeError()
858 except RuntimeError:
859 logging.exception("just testing")
860 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000861 self.assertEqual(output.getvalue(),
Christian Heimes180510d2008-03-03 19:15:45 +0000862 "ERROR:root:just testing\nGot a [RuntimeError]\n")
863 # Original logger output is empty
Benjamin Peterson77108eb2009-07-01 00:43:10 +0000864 self.assert_log_lines([])
Christian Heimes180510d2008-03-03 19:15:45 +0000865
Georg Brandl3dbca812008-07-23 16:10:53 +0000866 def test_config5_ok(self):
867 self.test_config1_ok(config=self.config5)
Christian Heimes180510d2008-03-03 19:15:45 +0000868
Benjamin Petersonae5360b2008-09-08 23:05:23 +0000869 def test_config6_ok(self):
870 self.test_config1_ok(config=self.config6)
871
Vinay Sajip3f84b072011-03-07 17:49:33 +0000872 def test_config7_ok(self):
873 with captured_stdout() as output:
874 self.apply_config(self.config1a)
875 logger = logging.getLogger("compiler.parser")
876 # See issue #11424. compiler-hyphenated sorts
877 # between compiler and compiler.xyz and this
878 # was preventing compiler.xyz from being included
879 # in the child loggers of compiler because of an
880 # overzealous loop termination condition.
881 hyphenated = logging.getLogger('compiler-hyphenated')
882 # All will output a message
883 logger.info(self.next_message())
884 logger.error(self.next_message())
885 hyphenated.critical(self.next_message())
886 self.assert_log_lines([
887 ('INFO', '1'),
888 ('ERROR', '2'),
889 ('CRITICAL', '3'),
890 ], stream=output)
891 # Original logger output is empty.
892 self.assert_log_lines([])
893 with captured_stdout() as output:
894 self.apply_config(self.config7)
895 logger = logging.getLogger("compiler.parser")
896 self.assertFalse(logger.disabled)
897 # Both will output a message
898 logger.info(self.next_message())
899 logger.error(self.next_message())
900 logger = logging.getLogger("compiler.lexer")
901 # Both will output a message
902 logger.info(self.next_message())
903 logger.error(self.next_message())
904 # Will not appear
905 hyphenated.critical(self.next_message())
906 self.assert_log_lines([
907 ('INFO', '4'),
908 ('ERROR', '5'),
909 ('INFO', '6'),
910 ('ERROR', '7'),
911 ], stream=output)
912 # Original logger output is empty.
913 self.assert_log_lines([])
914
Christian Heimes180510d2008-03-03 19:15:45 +0000915class LogRecordStreamHandler(StreamRequestHandler):
916
917 """Handler for a streaming logging request. It saves the log message in the
918 TCP server's 'log_output' attribute."""
919
920 TCP_LOG_END = "!!!END!!!"
921
922 def handle(self):
923 """Handle multiple requests - each expected to be of 4-byte length,
924 followed by the LogRecord in pickle format. Logs the record
925 according to whatever policy is configured locally."""
926 while True:
927 chunk = self.connection.recv(4)
928 if len(chunk) < 4:
929 break
930 slen = struct.unpack(">L", chunk)[0]
931 chunk = self.connection.recv(slen)
932 while len(chunk) < slen:
933 chunk = chunk + self.connection.recv(slen - len(chunk))
934 obj = self.unpickle(chunk)
935 record = logging.makeLogRecord(obj)
936 self.handle_log_record(record)
937
938 def unpickle(self, data):
939 return pickle.loads(data)
940
941 def handle_log_record(self, record):
942 # If the end-of-messages sentinel is seen, tell the server to
943 # terminate.
944 if self.TCP_LOG_END in record.msg:
945 self.server.abort = 1
946 return
947 self.server.log_output += record.msg + "\n"
948
Guido van Rossum376e6362003-04-25 14:22:00 +0000949
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000950class LogRecordSocketReceiver(ThreadingTCPServer):
Christian Heimes180510d2008-03-03 19:15:45 +0000951
952 """A simple-minded TCP socket-based logging receiver suitable for test
953 purposes."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000954
955 allow_reuse_address = 1
Christian Heimes180510d2008-03-03 19:15:45 +0000956 log_output = ""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000957
958 def __init__(self, host='localhost',
959 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
960 handler=LogRecordStreamHandler):
961 ThreadingTCPServer.__init__(self, (host, port), handler)
Christian Heimes8640e742008-02-23 16:23:06 +0000962 self.abort = False
Christian Heimes180510d2008-03-03 19:15:45 +0000963 self.timeout = 0.1
964 self.finished = threading.Event()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000965
966 def serve_until_stopped(self):
Neal Norwitz55cd82f2006-02-05 08:21:08 +0000967 while not self.abort:
Neal Norwitz5bab0f82006-03-05 02:16:12 +0000968 rd, wr, ex = select.select([self.socket.fileno()], [], [],
969 self.timeout)
970 if rd:
971 self.handle_request()
Christian Heimes180510d2008-03-03 19:15:45 +0000972 # Notify the main thread that we're about to exit
973 self.finished.set()
Martin v. Löwisf6848882006-01-29 19:55:18 +0000974 # close the listen socket
975 self.server_close()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000976
Guido van Rossum2a1d5162003-01-21 21:05:22 +0000977
Victor Stinner45df8202010-04-28 22:31:17 +0000978@unittest.skipUnless(threading, 'Threading required for this test.')
Christian Heimes180510d2008-03-03 19:15:45 +0000979class SocketHandlerTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000980
Christian Heimes180510d2008-03-03 19:15:45 +0000981 """Test for SocketHandler objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000982
Christian Heimes180510d2008-03-03 19:15:45 +0000983 def setUp(self):
984 """Set up a TCP server to receive log messages, and a SocketHandler
985 pointing to that server's address and port."""
986 BaseTest.setUp(self)
987 self.tcpserver = LogRecordSocketReceiver(port=0)
988 self.port = self.tcpserver.socket.getsockname()[1]
989 self.threads = [
990 threading.Thread(target=self.tcpserver.serve_until_stopped)]
991 for thread in self.threads:
992 thread.start()
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000993
Christian Heimes180510d2008-03-03 19:15:45 +0000994 self.sock_hdlr = logging.handlers.SocketHandler('localhost', self.port)
995 self.sock_hdlr.setFormatter(self.root_formatter)
996 self.root_logger.removeHandler(self.root_logger.handlers[0])
997 self.root_logger.addHandler(self.sock_hdlr)
Neal Norwitzb4a2df02003-01-02 14:56:39 +0000998
Christian Heimes180510d2008-03-03 19:15:45 +0000999 def tearDown(self):
1000 """Shutdown the TCP server."""
1001 try:
1002 self.tcpserver.abort = True
1003 del self.tcpserver
1004 self.root_logger.removeHandler(self.sock_hdlr)
1005 self.sock_hdlr.close()
1006 for thread in self.threads:
1007 thread.join(2.0)
1008 finally:
1009 BaseTest.tearDown(self)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001010
Christian Heimes180510d2008-03-03 19:15:45 +00001011 def get_output(self):
1012 """Get the log output as received by the TCP server."""
1013 # Signal the TCP receiver and wait for it to terminate.
1014 self.root_logger.critical(LogRecordStreamHandler.TCP_LOG_END)
1015 self.tcpserver.finished.wait(2.0)
1016 return self.tcpserver.log_output
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001017
Christian Heimes180510d2008-03-03 19:15:45 +00001018 def test_output(self):
1019 # The log message sent to the SocketHandler is properly received.
1020 logger = logging.getLogger("tcp")
1021 logger.error("spam")
1022 logger.debug("eggs")
Ezio Melottib3aedd42010-11-20 19:04:17 +00001023 self.assertEqual(self.get_output(), "spam\neggs\n")
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001024
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001025
Christian Heimes180510d2008-03-03 19:15:45 +00001026class MemoryTest(BaseTest):
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001027
Christian Heimes180510d2008-03-03 19:15:45 +00001028 """Test memory persistence of logger objects."""
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001029
Christian Heimes180510d2008-03-03 19:15:45 +00001030 def setUp(self):
1031 """Create a dict to remember potentially destroyed objects."""
1032 BaseTest.setUp(self)
1033 self._survivors = {}
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001034
Christian Heimes180510d2008-03-03 19:15:45 +00001035 def _watch_for_survival(self, *args):
1036 """Watch the given objects for survival, by creating weakrefs to
1037 them."""
1038 for obj in args:
1039 key = id(obj), repr(obj)
1040 self._survivors[key] = weakref.ref(obj)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001041
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001042 def _assertTruesurvival(self):
Christian Heimes180510d2008-03-03 19:15:45 +00001043 """Assert that all objects watched for survival have survived."""
1044 # Trigger cycle breaking.
1045 gc.collect()
1046 dead = []
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001047 for (id_, repr_), ref in self._survivors.items():
Christian Heimes180510d2008-03-03 19:15:45 +00001048 if ref() is None:
1049 dead.append(repr_)
1050 if dead:
1051 self.fail("%d objects should have survived "
1052 "but have been destroyed: %s" % (len(dead), ", ".join(dead)))
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001053
Christian Heimes180510d2008-03-03 19:15:45 +00001054 def test_persistent_loggers(self):
1055 # Logger objects are persistent and retain their configuration, even
1056 # if visible references are destroyed.
1057 self.root_logger.setLevel(logging.INFO)
1058 foo = logging.getLogger("foo")
1059 self._watch_for_survival(foo)
1060 foo.setLevel(logging.DEBUG)
1061 self.root_logger.debug(self.next_message())
1062 foo.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001063 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001064 ('foo', 'DEBUG', '2'),
1065 ])
1066 del foo
1067 # foo has survived.
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001068 self._assertTruesurvival()
Christian Heimes180510d2008-03-03 19:15:45 +00001069 # foo has retained its settings.
1070 bar = logging.getLogger("foo")
1071 bar.debug(self.next_message())
Benjamin Peterson77108eb2009-07-01 00:43:10 +00001072 self.assert_log_lines([
Christian Heimes180510d2008-03-03 19:15:45 +00001073 ('foo', 'DEBUG', '2'),
1074 ('foo', 'DEBUG', '3'),
1075 ])
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001076
Benjamin Petersonf91df042009-02-13 02:50:59 +00001077
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001078class EncodingTest(BaseTest):
1079 def test_encoding_plain_file(self):
1080 # In Python 2.x, a plain file object is treated as having no encoding.
1081 log = logging.getLogger("test")
Vinay Sajip60b4df12010-12-27 11:18:52 +00001082 fd, fn = tempfile.mkstemp(".log", "test_logging-1-")
1083 os.close(fd)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001084 # the non-ascii data we write to the log.
1085 data = "foo\x80"
1086 try:
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001087 handler = logging.FileHandler(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001088 log.addHandler(handler)
1089 try:
1090 # write non-ascii data to the log.
1091 log.warning(data)
1092 finally:
1093 log.removeHandler(handler)
1094 handler.close()
1095 # check we wrote exactly those bytes, ignoring trailing \n etc
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001096 f = open(fn, encoding="utf-8")
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001097 try:
Benjamin Petersonc9c0f202009-06-30 23:06:06 +00001098 self.assertEqual(f.read().rstrip(), data)
Benjamin Petersonae5360b2008-09-08 23:05:23 +00001099 finally:
1100 f.close()
1101 finally:
1102 if os.path.isfile(fn):
1103 os.remove(fn)
Neal Norwitzb4a2df02003-01-02 14:56:39 +00001104
Benjamin Petersonf91df042009-02-13 02:50:59 +00001105 def test_encoding_cyrillic_unicode(self):
1106 log = logging.getLogger("test")
1107 #Get a message in Unicode: Do svidanya in Cyrillic (meaning goodbye)
1108 message = '\u0434\u043e \u0441\u0432\u0438\u0434\u0430\u043d\u0438\u044f'
1109 #Ensure it's written in a Cyrillic encoding
1110 writer_class = codecs.getwriter('cp1251')
Benjamin Peterson25c95f12009-05-08 20:42:26 +00001111 writer_class.encoding = 'cp1251'
Benjamin Petersonf91df042009-02-13 02:50:59 +00001112 stream = io.BytesIO()
1113 writer = writer_class(stream, 'strict')
1114 handler = logging.StreamHandler(writer)
1115 log.addHandler(handler)
1116 try:
1117 log.warning(message)
1118 finally:
1119 log.removeHandler(handler)
1120 handler.close()
1121 # check we wrote exactly those bytes, ignoring trailing \n etc
1122 s = stream.getvalue()
1123 #Compare against what the data should be when encoded in CP-1251
1124 self.assertEqual(s, b'\xe4\xee \xf1\xe2\xe8\xe4\xe0\xed\xe8\xff\n')
1125
1126
Georg Brandlf9734072008-12-07 15:30:06 +00001127class WarningsTest(BaseTest):
Brett Cannondf8709d2009-04-01 20:01:47 +00001128
Georg Brandlf9734072008-12-07 15:30:06 +00001129 def test_warnings(self):
Brett Cannondf8709d2009-04-01 20:01:47 +00001130 with warnings.catch_warnings():
Brett Cannon5b9082a2009-04-05 18:57:32 +00001131 logging.captureWarnings(True)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001132 self.addCleanup(lambda: logging.captureWarnings(False))
1133 warnings.filterwarnings("always", category=UserWarning)
1134 stream = io.StringIO()
1135 h = logging.StreamHandler(stream)
1136 logger = logging.getLogger("py.warnings")
1137 logger.addHandler(h)
1138 warnings.warn("I'm warning you...")
1139 logger.removeHandler(h)
1140 s = stream.getvalue()
1141 h.close()
1142 self.assertTrue(s.find("UserWarning: I'm warning you...\n") > 0)
Georg Brandlf9734072008-12-07 15:30:06 +00001143
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01001144 #See if an explicit file uses the original implementation
1145 a_file = io.StringIO()
1146 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42,
1147 a_file, "Dummy line")
1148 s = a_file.getvalue()
1149 a_file.close()
1150 self.assertEqual(s,
1151 "dummy.py:42: UserWarning: Explicit\n Dummy line\n")
1152
1153 def test_warnings_no_handlers(self):
1154 with warnings.catch_warnings():
1155 logging.captureWarnings(True)
1156 self.addCleanup(lambda: logging.captureWarnings(False))
1157
1158 # confirm our assumption: no loggers are set
1159 logger = logging.getLogger("py.warnings")
1160 assert logger.handlers == []
1161
1162 warnings.showwarning("Explicit", UserWarning, "dummy.py", 42)
1163 self.assertTrue(len(logger.handlers) == 1)
1164 self.assertIsInstance(logger.handlers[0], logging.NullHandler)
Georg Brandlf9734072008-12-07 15:30:06 +00001165
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001166
1167def formatFunc(format, datefmt=None):
1168 return logging.Formatter(format, datefmt)
1169
1170def handlerFunc():
1171 return logging.StreamHandler()
1172
1173class CustomHandler(logging.StreamHandler):
1174 pass
1175
1176class ConfigDictTest(BaseTest):
1177
1178 """Reading logging config from a dictionary."""
1179
1180 expected_log_pat = r"^([\w]+) \+\+ ([\w]+)$"
1181
1182 # config0 is a standard configuration.
1183 config0 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001184 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001185 'formatters': {
1186 'form1' : {
1187 'format' : '%(levelname)s ++ %(message)s',
1188 },
1189 },
1190 'handlers' : {
1191 'hand1' : {
1192 'class' : 'logging.StreamHandler',
1193 'formatter' : 'form1',
1194 'level' : 'NOTSET',
1195 'stream' : 'ext://sys.stdout',
1196 },
1197 },
1198 'root' : {
1199 'level' : 'WARNING',
1200 'handlers' : ['hand1'],
1201 },
1202 }
1203
1204 # config1 adds a little to the standard configuration.
1205 config1 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001206 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001207 'formatters': {
1208 'form1' : {
1209 'format' : '%(levelname)s ++ %(message)s',
1210 },
1211 },
1212 'handlers' : {
1213 'hand1' : {
1214 'class' : 'logging.StreamHandler',
1215 'formatter' : 'form1',
1216 'level' : 'NOTSET',
1217 'stream' : 'ext://sys.stdout',
1218 },
1219 },
1220 'loggers' : {
1221 'compiler.parser' : {
1222 'level' : 'DEBUG',
1223 'handlers' : ['hand1'],
1224 },
1225 },
1226 'root' : {
1227 'level' : 'WARNING',
1228 },
1229 }
1230
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001231 # config1a moves the handler to the root. Used with config8a
1232 config1a = {
1233 'version': 1,
1234 'formatters': {
1235 'form1' : {
1236 'format' : '%(levelname)s ++ %(message)s',
1237 },
1238 },
1239 'handlers' : {
1240 'hand1' : {
1241 'class' : 'logging.StreamHandler',
1242 'formatter' : 'form1',
1243 'level' : 'NOTSET',
1244 'stream' : 'ext://sys.stdout',
1245 },
1246 },
1247 'loggers' : {
1248 'compiler.parser' : {
1249 'level' : 'DEBUG',
1250 },
1251 },
1252 'root' : {
1253 'level' : 'WARNING',
1254 'handlers' : ['hand1'],
1255 },
1256 }
1257
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001258 # config2 has a subtle configuration error that should be reported
1259 config2 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001260 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001261 'formatters': {
1262 'form1' : {
1263 'format' : '%(levelname)s ++ %(message)s',
1264 },
1265 },
1266 'handlers' : {
1267 'hand1' : {
1268 'class' : 'logging.StreamHandler',
1269 'formatter' : 'form1',
1270 'level' : 'NOTSET',
1271 'stream' : 'ext://sys.stdbout',
1272 },
1273 },
1274 'loggers' : {
1275 'compiler.parser' : {
1276 'level' : 'DEBUG',
1277 'handlers' : ['hand1'],
1278 },
1279 },
1280 'root' : {
1281 'level' : 'WARNING',
1282 },
1283 }
1284
1285 #As config1 but with a misspelt level on a handler
1286 config2a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001287 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001288 'formatters': {
1289 'form1' : {
1290 'format' : '%(levelname)s ++ %(message)s',
1291 },
1292 },
1293 'handlers' : {
1294 'hand1' : {
1295 'class' : 'logging.StreamHandler',
1296 'formatter' : 'form1',
1297 'level' : 'NTOSET',
1298 'stream' : 'ext://sys.stdout',
1299 },
1300 },
1301 'loggers' : {
1302 'compiler.parser' : {
1303 'level' : 'DEBUG',
1304 'handlers' : ['hand1'],
1305 },
1306 },
1307 'root' : {
1308 'level' : 'WARNING',
1309 },
1310 }
1311
1312
1313 #As config1 but with a misspelt level on a logger
1314 config2b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001315 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001316 'formatters': {
1317 'form1' : {
1318 'format' : '%(levelname)s ++ %(message)s',
1319 },
1320 },
1321 'handlers' : {
1322 'hand1' : {
1323 'class' : 'logging.StreamHandler',
1324 'formatter' : 'form1',
1325 'level' : 'NOTSET',
1326 'stream' : 'ext://sys.stdout',
1327 },
1328 },
1329 'loggers' : {
1330 'compiler.parser' : {
1331 'level' : 'DEBUG',
1332 'handlers' : ['hand1'],
1333 },
1334 },
1335 'root' : {
1336 'level' : 'WRANING',
1337 },
1338 }
1339
1340 # config3 has a less subtle configuration error
1341 config3 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001342 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001343 'formatters': {
1344 'form1' : {
1345 'format' : '%(levelname)s ++ %(message)s',
1346 },
1347 },
1348 'handlers' : {
1349 'hand1' : {
1350 'class' : 'logging.StreamHandler',
1351 'formatter' : 'misspelled_name',
1352 'level' : 'NOTSET',
1353 'stream' : 'ext://sys.stdout',
1354 },
1355 },
1356 'loggers' : {
1357 'compiler.parser' : {
1358 'level' : 'DEBUG',
1359 'handlers' : ['hand1'],
1360 },
1361 },
1362 'root' : {
1363 'level' : 'WARNING',
1364 },
1365 }
1366
1367 # config4 specifies a custom formatter class to be loaded
1368 config4 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001369 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001370 'formatters': {
1371 'form1' : {
1372 '()' : __name__ + '.ExceptionFormatter',
1373 'format' : '%(levelname)s:%(name)s:%(message)s',
1374 },
1375 },
1376 'handlers' : {
1377 'hand1' : {
1378 'class' : 'logging.StreamHandler',
1379 'formatter' : 'form1',
1380 'level' : 'NOTSET',
1381 'stream' : 'ext://sys.stdout',
1382 },
1383 },
1384 'root' : {
1385 'level' : 'NOTSET',
1386 'handlers' : ['hand1'],
1387 },
1388 }
1389
1390 # As config4 but using an actual callable rather than a string
1391 config4a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001392 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001393 'formatters': {
1394 'form1' : {
1395 '()' : ExceptionFormatter,
1396 'format' : '%(levelname)s:%(name)s:%(message)s',
1397 },
1398 'form2' : {
1399 '()' : __name__ + '.formatFunc',
1400 'format' : '%(levelname)s:%(name)s:%(message)s',
1401 },
1402 'form3' : {
1403 '()' : formatFunc,
1404 'format' : '%(levelname)s:%(name)s:%(message)s',
1405 },
1406 },
1407 'handlers' : {
1408 'hand1' : {
1409 'class' : 'logging.StreamHandler',
1410 'formatter' : 'form1',
1411 'level' : 'NOTSET',
1412 'stream' : 'ext://sys.stdout',
1413 },
1414 'hand2' : {
1415 '()' : handlerFunc,
1416 },
1417 },
1418 'root' : {
1419 'level' : 'NOTSET',
1420 'handlers' : ['hand1'],
1421 },
1422 }
1423
1424 # config5 specifies a custom handler class to be loaded
1425 config5 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001426 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001427 'formatters': {
1428 'form1' : {
1429 'format' : '%(levelname)s ++ %(message)s',
1430 },
1431 },
1432 'handlers' : {
1433 'hand1' : {
1434 'class' : __name__ + '.CustomHandler',
1435 'formatter' : 'form1',
1436 'level' : 'NOTSET',
1437 'stream' : 'ext://sys.stdout',
1438 },
1439 },
1440 'loggers' : {
1441 'compiler.parser' : {
1442 'level' : 'DEBUG',
1443 'handlers' : ['hand1'],
1444 },
1445 },
1446 'root' : {
1447 'level' : 'WARNING',
1448 },
1449 }
1450
1451 # config6 specifies a custom handler class to be loaded
1452 # but has bad arguments
1453 config6 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001454 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001455 'formatters': {
1456 'form1' : {
1457 'format' : '%(levelname)s ++ %(message)s',
1458 },
1459 },
1460 'handlers' : {
1461 'hand1' : {
1462 'class' : __name__ + '.CustomHandler',
1463 'formatter' : 'form1',
1464 'level' : 'NOTSET',
1465 'stream' : 'ext://sys.stdout',
1466 '9' : 'invalid parameter name',
1467 },
1468 },
1469 'loggers' : {
1470 'compiler.parser' : {
1471 'level' : 'DEBUG',
1472 'handlers' : ['hand1'],
1473 },
1474 },
1475 'root' : {
1476 'level' : 'WARNING',
1477 },
1478 }
1479
1480 #config 7 does not define compiler.parser but defines compiler.lexer
1481 #so compiler.parser should be disabled after applying it
1482 config7 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001483 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001484 'formatters': {
1485 'form1' : {
1486 'format' : '%(levelname)s ++ %(message)s',
1487 },
1488 },
1489 'handlers' : {
1490 'hand1' : {
1491 'class' : 'logging.StreamHandler',
1492 'formatter' : 'form1',
1493 'level' : 'NOTSET',
1494 'stream' : 'ext://sys.stdout',
1495 },
1496 },
1497 'loggers' : {
1498 'compiler.lexer' : {
1499 'level' : 'DEBUG',
1500 'handlers' : ['hand1'],
1501 },
1502 },
1503 'root' : {
1504 'level' : 'WARNING',
1505 },
1506 }
1507
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001508 # config8 defines both compiler and compiler.lexer
1509 # so compiler.parser should not be disabled (since
1510 # compiler is defined)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001511 config8 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001512 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001513 'disable_existing_loggers' : False,
1514 'formatters': {
1515 'form1' : {
1516 'format' : '%(levelname)s ++ %(message)s',
1517 },
1518 },
1519 'handlers' : {
1520 'hand1' : {
1521 'class' : 'logging.StreamHandler',
1522 'formatter' : 'form1',
1523 'level' : 'NOTSET',
1524 'stream' : 'ext://sys.stdout',
1525 },
1526 },
1527 'loggers' : {
1528 'compiler' : {
1529 'level' : 'DEBUG',
1530 'handlers' : ['hand1'],
1531 },
1532 'compiler.lexer' : {
1533 },
1534 },
1535 'root' : {
1536 'level' : 'WARNING',
1537 },
1538 }
1539
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001540 # config8a disables existing loggers
1541 config8a = {
1542 'version': 1,
1543 'disable_existing_loggers' : True,
1544 'formatters': {
1545 'form1' : {
1546 'format' : '%(levelname)s ++ %(message)s',
1547 },
1548 },
1549 'handlers' : {
1550 'hand1' : {
1551 'class' : 'logging.StreamHandler',
1552 'formatter' : 'form1',
1553 'level' : 'NOTSET',
1554 'stream' : 'ext://sys.stdout',
1555 },
1556 },
1557 'loggers' : {
1558 'compiler' : {
1559 'level' : 'DEBUG',
1560 'handlers' : ['hand1'],
1561 },
1562 'compiler.lexer' : {
1563 },
1564 },
1565 'root' : {
1566 'level' : 'WARNING',
1567 },
1568 }
1569
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001570 config9 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001571 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001572 'formatters': {
1573 'form1' : {
1574 'format' : '%(levelname)s ++ %(message)s',
1575 },
1576 },
1577 'handlers' : {
1578 'hand1' : {
1579 'class' : 'logging.StreamHandler',
1580 'formatter' : 'form1',
1581 'level' : 'WARNING',
1582 'stream' : 'ext://sys.stdout',
1583 },
1584 },
1585 'loggers' : {
1586 'compiler.parser' : {
1587 'level' : 'WARNING',
1588 'handlers' : ['hand1'],
1589 },
1590 },
1591 'root' : {
1592 'level' : 'NOTSET',
1593 },
1594 }
1595
1596 config9a = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001597 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001598 'incremental' : True,
1599 'handlers' : {
1600 'hand1' : {
1601 'level' : 'WARNING',
1602 },
1603 },
1604 'loggers' : {
1605 'compiler.parser' : {
1606 'level' : 'INFO',
1607 },
1608 },
1609 }
1610
1611 config9b = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001612 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001613 'incremental' : True,
1614 'handlers' : {
1615 'hand1' : {
1616 'level' : 'INFO',
1617 },
1618 },
1619 'loggers' : {
1620 'compiler.parser' : {
1621 'level' : 'INFO',
1622 },
1623 },
1624 }
1625
1626 #As config1 but with a filter added
1627 config10 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001628 'version': 1,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001629 'formatters': {
1630 'form1' : {
1631 'format' : '%(levelname)s ++ %(message)s',
1632 },
1633 },
1634 'filters' : {
1635 'filt1' : {
1636 'name' : 'compiler.parser',
1637 },
1638 },
1639 'handlers' : {
1640 'hand1' : {
1641 'class' : 'logging.StreamHandler',
1642 'formatter' : 'form1',
1643 'level' : 'NOTSET',
1644 'stream' : 'ext://sys.stdout',
1645 'filters' : ['filt1'],
1646 },
1647 },
1648 'loggers' : {
1649 'compiler.parser' : {
1650 'level' : 'DEBUG',
1651 'filters' : ['filt1'],
1652 },
1653 },
1654 'root' : {
1655 'level' : 'WARNING',
1656 'handlers' : ['hand1'],
1657 },
1658 }
1659
1660 #As config1 but using cfg:// references
1661 config11 = {
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001662 'version': 1,
1663 'true_formatters': {
1664 'form1' : {
1665 'format' : '%(levelname)s ++ %(message)s',
1666 },
1667 },
1668 'handler_configs': {
1669 'hand1' : {
1670 'class' : 'logging.StreamHandler',
1671 'formatter' : 'form1',
1672 'level' : 'NOTSET',
1673 'stream' : 'ext://sys.stdout',
1674 },
1675 },
1676 'formatters' : 'cfg://true_formatters',
1677 'handlers' : {
1678 'hand1' : 'cfg://handler_configs[hand1]',
1679 },
1680 'loggers' : {
1681 'compiler.parser' : {
1682 'level' : 'DEBUG',
1683 'handlers' : ['hand1'],
1684 },
1685 },
1686 'root' : {
1687 'level' : 'WARNING',
1688 },
1689 }
1690
1691 #As config11 but missing the version key
1692 config12 = {
1693 'true_formatters': {
1694 'form1' : {
1695 'format' : '%(levelname)s ++ %(message)s',
1696 },
1697 },
1698 'handler_configs': {
1699 'hand1' : {
1700 'class' : 'logging.StreamHandler',
1701 'formatter' : 'form1',
1702 'level' : 'NOTSET',
1703 'stream' : 'ext://sys.stdout',
1704 },
1705 },
1706 'formatters' : 'cfg://true_formatters',
1707 'handlers' : {
1708 'hand1' : 'cfg://handler_configs[hand1]',
1709 },
1710 'loggers' : {
1711 'compiler.parser' : {
1712 'level' : 'DEBUG',
1713 'handlers' : ['hand1'],
1714 },
1715 },
1716 'root' : {
1717 'level' : 'WARNING',
1718 },
1719 }
1720
1721 #As config11 but using an unsupported version
1722 config13 = {
1723 'version': 2,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001724 'true_formatters': {
1725 'form1' : {
1726 'format' : '%(levelname)s ++ %(message)s',
1727 },
1728 },
1729 'handler_configs': {
1730 'hand1' : {
1731 'class' : 'logging.StreamHandler',
1732 'formatter' : 'form1',
1733 'level' : 'NOTSET',
1734 'stream' : 'ext://sys.stdout',
1735 },
1736 },
1737 'formatters' : 'cfg://true_formatters',
1738 'handlers' : {
1739 'hand1' : 'cfg://handler_configs[hand1]',
1740 },
1741 'loggers' : {
1742 'compiler.parser' : {
1743 'level' : 'DEBUG',
1744 'handlers' : ['hand1'],
1745 },
1746 },
1747 'root' : {
1748 'level' : 'WARNING',
1749 },
1750 }
1751
1752 def apply_config(self, conf):
1753 logging.config.dictConfig(conf)
1754
1755 def test_config0_ok(self):
1756 # A simple config which overrides the default settings.
1757 with captured_stdout() as output:
1758 self.apply_config(self.config0)
1759 logger = logging.getLogger()
1760 # Won't output anything
1761 logger.info(self.next_message())
1762 # Outputs a message
1763 logger.error(self.next_message())
1764 self.assert_log_lines([
1765 ('ERROR', '2'),
1766 ], stream=output)
1767 # Original logger output is empty.
1768 self.assert_log_lines([])
1769
1770 def test_config1_ok(self, config=config1):
1771 # A config defining a sub-parser as well.
1772 with captured_stdout() as output:
1773 self.apply_config(config)
1774 logger = logging.getLogger("compiler.parser")
1775 # Both will output a message
1776 logger.info(self.next_message())
1777 logger.error(self.next_message())
1778 self.assert_log_lines([
1779 ('INFO', '1'),
1780 ('ERROR', '2'),
1781 ], stream=output)
1782 # Original logger output is empty.
1783 self.assert_log_lines([])
1784
1785 def test_config2_failure(self):
1786 # A simple config which overrides the default settings.
1787 self.assertRaises(Exception, self.apply_config, self.config2)
1788
1789 def test_config2a_failure(self):
1790 # A simple config which overrides the default settings.
1791 self.assertRaises(Exception, self.apply_config, self.config2a)
1792
1793 def test_config2b_failure(self):
1794 # A simple config which overrides the default settings.
1795 self.assertRaises(Exception, self.apply_config, self.config2b)
1796
1797 def test_config3_failure(self):
1798 # A simple config which overrides the default settings.
1799 self.assertRaises(Exception, self.apply_config, self.config3)
1800
1801 def test_config4_ok(self):
1802 # A config specifying a custom formatter class.
1803 with captured_stdout() as output:
1804 self.apply_config(self.config4)
1805 #logger = logging.getLogger()
1806 try:
1807 raise RuntimeError()
1808 except RuntimeError:
1809 logging.exception("just testing")
1810 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001811 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001812 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1813 # Original logger output is empty
1814 self.assert_log_lines([])
1815
1816 def test_config4a_ok(self):
1817 # A config specifying a custom formatter class.
1818 with captured_stdout() as output:
1819 self.apply_config(self.config4a)
1820 #logger = logging.getLogger()
1821 try:
1822 raise RuntimeError()
1823 except RuntimeError:
1824 logging.exception("just testing")
1825 sys.stdout.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +00001826 self.assertEqual(output.getvalue(),
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001827 "ERROR:root:just testing\nGot a [RuntimeError]\n")
1828 # Original logger output is empty
1829 self.assert_log_lines([])
1830
1831 def test_config5_ok(self):
1832 self.test_config1_ok(config=self.config5)
1833
1834 def test_config6_failure(self):
1835 self.assertRaises(Exception, self.apply_config, self.config6)
1836
1837 def test_config7_ok(self):
1838 with captured_stdout() as output:
1839 self.apply_config(self.config1)
1840 logger = logging.getLogger("compiler.parser")
1841 # Both will output a message
1842 logger.info(self.next_message())
1843 logger.error(self.next_message())
1844 self.assert_log_lines([
1845 ('INFO', '1'),
1846 ('ERROR', '2'),
1847 ], stream=output)
1848 # Original logger output is empty.
1849 self.assert_log_lines([])
1850 with captured_stdout() as output:
1851 self.apply_config(self.config7)
1852 logger = logging.getLogger("compiler.parser")
1853 self.assertTrue(logger.disabled)
1854 logger = logging.getLogger("compiler.lexer")
1855 # Both will output a message
1856 logger.info(self.next_message())
1857 logger.error(self.next_message())
1858 self.assert_log_lines([
1859 ('INFO', '3'),
1860 ('ERROR', '4'),
1861 ], stream=output)
1862 # Original logger output is empty.
1863 self.assert_log_lines([])
1864
1865 #Same as test_config_7_ok but don't disable old loggers.
1866 def test_config_8_ok(self):
1867 with captured_stdout() as output:
1868 self.apply_config(self.config1)
1869 logger = logging.getLogger("compiler.parser")
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001870 # All will output a message
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001871 logger.info(self.next_message())
1872 logger.error(self.next_message())
1873 self.assert_log_lines([
1874 ('INFO', '1'),
1875 ('ERROR', '2'),
1876 ], stream=output)
1877 # Original logger output is empty.
1878 self.assert_log_lines([])
1879 with captured_stdout() as output:
1880 self.apply_config(self.config8)
1881 logger = logging.getLogger("compiler.parser")
1882 self.assertFalse(logger.disabled)
1883 # Both will output a message
1884 logger.info(self.next_message())
1885 logger.error(self.next_message())
1886 logger = logging.getLogger("compiler.lexer")
1887 # Both will output a message
1888 logger.info(self.next_message())
1889 logger.error(self.next_message())
1890 self.assert_log_lines([
1891 ('INFO', '3'),
1892 ('ERROR', '4'),
1893 ('INFO', '5'),
1894 ('ERROR', '6'),
1895 ], stream=output)
1896 # Original logger output is empty.
1897 self.assert_log_lines([])
1898
Vinay Sajip9f9991c2011-03-07 18:02:57 +00001899 def test_config_8a_ok(self):
1900 with captured_stdout() as output:
1901 self.apply_config(self.config1a)
1902 logger = logging.getLogger("compiler.parser")
1903 # See issue #11424. compiler-hyphenated sorts
1904 # between compiler and compiler.xyz and this
1905 # was preventing compiler.xyz from being included
1906 # in the child loggers of compiler because of an
1907 # overzealous loop termination condition.
1908 hyphenated = logging.getLogger('compiler-hyphenated')
1909 # All will output a message
1910 logger.info(self.next_message())
1911 logger.error(self.next_message())
1912 hyphenated.critical(self.next_message())
1913 self.assert_log_lines([
1914 ('INFO', '1'),
1915 ('ERROR', '2'),
1916 ('CRITICAL', '3'),
1917 ], stream=output)
1918 # Original logger output is empty.
1919 self.assert_log_lines([])
1920 with captured_stdout() as output:
1921 self.apply_config(self.config8a)
1922 logger = logging.getLogger("compiler.parser")
1923 self.assertFalse(logger.disabled)
1924 # Both will output a message
1925 logger.info(self.next_message())
1926 logger.error(self.next_message())
1927 logger = logging.getLogger("compiler.lexer")
1928 # Both will output a message
1929 logger.info(self.next_message())
1930 logger.error(self.next_message())
1931 # Will not appear
1932 hyphenated.critical(self.next_message())
1933 self.assert_log_lines([
1934 ('INFO', '4'),
1935 ('ERROR', '5'),
1936 ('INFO', '6'),
1937 ('ERROR', '7'),
1938 ], stream=output)
1939 # Original logger output is empty.
1940 self.assert_log_lines([])
1941
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001942 def test_config_9_ok(self):
1943 with captured_stdout() as output:
1944 self.apply_config(self.config9)
1945 logger = logging.getLogger("compiler.parser")
1946 #Nothing will be output since both handler and logger are set to WARNING
1947 logger.info(self.next_message())
1948 self.assert_log_lines([], stream=output)
1949 self.apply_config(self.config9a)
1950 #Nothing will be output since both handler is still set to WARNING
1951 logger.info(self.next_message())
1952 self.assert_log_lines([], stream=output)
1953 self.apply_config(self.config9b)
1954 #Message should now be output
1955 logger.info(self.next_message())
1956 self.assert_log_lines([
1957 ('INFO', '3'),
1958 ], stream=output)
1959
1960 def test_config_10_ok(self):
1961 with captured_stdout() as output:
1962 self.apply_config(self.config10)
1963 logger = logging.getLogger("compiler.parser")
1964 logger.warning(self.next_message())
1965 logger = logging.getLogger('compiler')
1966 #Not output, because filtered
1967 logger.warning(self.next_message())
1968 logger = logging.getLogger('compiler.lexer')
1969 #Not output, because filtered
1970 logger.warning(self.next_message())
1971 logger = logging.getLogger("compiler.parser.codegen")
1972 #Output, as not filtered
1973 logger.error(self.next_message())
1974 self.assert_log_lines([
1975 ('WARNING', '1'),
1976 ('ERROR', '4'),
1977 ], stream=output)
1978
1979 def test_config11_ok(self):
1980 self.test_config1_ok(self.config11)
1981
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001982 def test_config12_failure(self):
1983 self.assertRaises(Exception, self.apply_config, self.config12)
1984
1985 def test_config13_failure(self):
1986 self.assertRaises(Exception, self.apply_config, self.config13)
1987
Victor Stinner45df8202010-04-28 22:31:17 +00001988 @unittest.skipUnless(threading, 'listen() needs threading to work')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001989 def setup_via_listener(self, text):
Benjamin Peterson9451a1c2010-03-13 22:30:34 +00001990 text = text.encode("utf-8")
Florent Xiclunadc692742010-08-15 20:16:27 +00001991 # Ask for a randomly assigned port (by using port 0)
1992 t = logging.config.listen(0)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001993 t.start()
1994 t.ready.wait()
Benjamin Petersona82addb2010-06-27 20:54:28 +00001995 # Now get the port allocated
1996 port = t.port
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001997 t.ready.clear()
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00001998 try:
1999 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2000 sock.settimeout(2.0)
2001 sock.connect(('localhost', port))
2002
2003 slen = struct.pack('>L', len(text))
2004 s = slen + text
2005 sentsofar = 0
2006 left = len(s)
2007 while left > 0:
2008 sent = sock.send(s[sentsofar:])
2009 sentsofar += sent
2010 left -= sent
2011 sock.close()
2012 finally:
2013 t.ready.wait(2.0)
2014 logging.config.stopListening()
2015 t.join(2.0)
2016
2017 def test_listen_config_10_ok(self):
2018 with captured_stdout() as output:
2019 self.setup_via_listener(json.dumps(self.config10))
2020 logger = logging.getLogger("compiler.parser")
2021 logger.warning(self.next_message())
2022 logger = logging.getLogger('compiler')
2023 #Not output, because filtered
2024 logger.warning(self.next_message())
2025 logger = logging.getLogger('compiler.lexer')
2026 #Not output, because filtered
2027 logger.warning(self.next_message())
2028 logger = logging.getLogger("compiler.parser.codegen")
2029 #Output, as not filtered
2030 logger.error(self.next_message())
2031 self.assert_log_lines([
2032 ('WARNING', '1'),
2033 ('ERROR', '4'),
2034 ], stream=output)
2035
2036 def test_listen_config_1_ok(self):
2037 with captured_stdout() as output:
2038 self.setup_via_listener(textwrap.dedent(ConfigFileTest.config1))
2039 logger = logging.getLogger("compiler.parser")
2040 # Both will output a message
2041 logger.info(self.next_message())
2042 logger.error(self.next_message())
2043 self.assert_log_lines([
2044 ('INFO', '1'),
2045 ('ERROR', '2'),
2046 ], stream=output)
2047 # Original logger output is empty.
2048 self.assert_log_lines([])
2049
Vinay Sajip373baef2011-04-26 20:05:24 +01002050 def test_baseconfig(self):
2051 d = {
2052 'atuple': (1, 2, 3),
2053 'alist': ['a', 'b', 'c'],
2054 'adict': {'d': 'e', 'f': 3 },
2055 'nest1': ('g', ('h', 'i'), 'j'),
2056 'nest2': ['k', ['l', 'm'], 'n'],
2057 'nest3': ['o', 'cfg://alist', 'p'],
2058 }
2059 bc = logging.config.BaseConfigurator(d)
2060 self.assertEqual(bc.convert('cfg://atuple[1]'), 2)
2061 self.assertEqual(bc.convert('cfg://alist[1]'), 'b')
2062 self.assertEqual(bc.convert('cfg://nest1[1][0]'), 'h')
2063 self.assertEqual(bc.convert('cfg://nest2[1][1]'), 'm')
2064 self.assertEqual(bc.convert('cfg://adict.d'), 'e')
2065 self.assertEqual(bc.convert('cfg://adict[f]'), 3)
2066 v = bc.convert('cfg://nest3')
2067 self.assertEqual(v.pop(1), ['a', 'b', 'c'])
2068 self.assertRaises(KeyError, bc.convert, 'cfg://nosuch')
2069 self.assertRaises(ValueError, bc.convert, 'cfg://!')
2070 self.assertRaises(KeyError, bc.convert, 'cfg://adict[2]')
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002071
2072class ManagerTest(BaseTest):
2073 def test_manager_loggerclass(self):
2074 logged = []
2075
2076 class MyLogger(logging.Logger):
2077 def _log(self, level, msg, args, exc_info=None, extra=None):
2078 logged.append(msg)
2079
2080 man = logging.Manager(None)
2081 self.assertRaises(TypeError, man.setLoggerClass, int)
2082 man.setLoggerClass(MyLogger)
2083 logger = man.getLogger('test')
2084 logger.warning('should appear in logged')
2085 logging.warning('should not appear in logged')
2086
2087 self.assertEqual(logged, ['should appear in logged'])
2088
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002089 def test_set_log_record_factory(self):
2090 man = logging.Manager(None)
2091 expected = object()
2092 man.setLogRecordFactory(expected)
2093 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002094
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002095class ChildLoggerTest(BaseTest):
2096 def test_child_loggers(self):
2097 r = logging.getLogger()
2098 l1 = logging.getLogger('abc')
2099 l2 = logging.getLogger('def.ghi')
2100 c1 = r.getChild('xyz')
2101 c2 = r.getChild('uvw.xyz')
2102 self.assertTrue(c1 is logging.getLogger('xyz'))
2103 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2104 c1 = l1.getChild('def')
2105 c2 = c1.getChild('ghi')
2106 c3 = l1.getChild('def.ghi')
2107 self.assertTrue(c1 is logging.getLogger('abc.def'))
2108 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2109 self.assertTrue(c2 is c3)
2110
2111
Vinay Sajip6fac8172010-10-19 20:44:14 +00002112class DerivedLogRecord(logging.LogRecord):
2113 pass
2114
Vinay Sajip61561522010-12-03 11:50:38 +00002115class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002116
2117 def setUp(self):
2118 class CheckingFilter(logging.Filter):
2119 def __init__(self, cls):
2120 self.cls = cls
2121
2122 def filter(self, record):
2123 t = type(record)
2124 if t is not self.cls:
2125 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2126 self.cls)
2127 raise TypeError(msg)
2128 return True
2129
2130 BaseTest.setUp(self)
2131 self.filter = CheckingFilter(DerivedLogRecord)
2132 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002133 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002134
2135 def tearDown(self):
2136 self.root_logger.removeFilter(self.filter)
2137 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002138 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002139
2140 def test_logrecord_class(self):
2141 self.assertRaises(TypeError, self.root_logger.warning,
2142 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002143 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002144 self.root_logger.error(self.next_message())
2145 self.assert_log_lines([
2146 ('root', 'ERROR', '2'),
2147 ])
2148
2149
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002150class QueueHandlerTest(BaseTest):
2151 # Do not bother with a logger name group.
2152 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2153
2154 def setUp(self):
2155 BaseTest.setUp(self)
2156 self.queue = queue.Queue(-1)
2157 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2158 self.que_logger = logging.getLogger('que')
2159 self.que_logger.propagate = False
2160 self.que_logger.setLevel(logging.WARNING)
2161 self.que_logger.addHandler(self.que_hdlr)
2162
2163 def tearDown(self):
2164 self.que_hdlr.close()
2165 BaseTest.tearDown(self)
2166
2167 def test_queue_handler(self):
2168 self.que_logger.debug(self.next_message())
2169 self.assertRaises(queue.Empty, self.queue.get_nowait)
2170 self.que_logger.info(self.next_message())
2171 self.assertRaises(queue.Empty, self.queue.get_nowait)
2172 msg = self.next_message()
2173 self.que_logger.warning(msg)
2174 data = self.queue.get_nowait()
2175 self.assertTrue(isinstance(data, logging.LogRecord))
2176 self.assertEqual(data.name, self.que_logger.name)
2177 self.assertEqual((data.msg, data.args), (msg, None))
2178
Vinay Sajipe723e962011-04-15 22:27:17 +01002179 def test_queue_listener(self):
2180 handler = TestHandler(Matcher())
2181 listener = logging.handlers.QueueListener(self.queue, handler)
2182 listener.start()
2183 try:
2184 self.que_logger.warning(self.next_message())
2185 self.que_logger.error(self.next_message())
2186 self.que_logger.critical(self.next_message())
2187 finally:
2188 listener.stop()
2189 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2190 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2191 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2192
2193
Vinay Sajipa39c5712010-10-25 13:57:39 +00002194class FormatterTest(unittest.TestCase):
2195 def setUp(self):
2196 self.common = {
2197 'name': 'formatter.test',
2198 'level': logging.DEBUG,
2199 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2200 'lineno': 42,
2201 'exc_info': None,
2202 'func': None,
2203 'msg': 'Message with %d %s',
2204 'args': (2, 'placeholders'),
2205 }
2206 self.variants = {
2207 }
2208
2209 def get_record(self, name=None):
2210 result = dict(self.common)
2211 if name is not None:
2212 result.update(self.variants[name])
2213 return logging.makeLogRecord(result)
2214
2215 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002216 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002217 r = self.get_record()
2218 f = logging.Formatter('${%(message)s}')
2219 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2220 f = logging.Formatter('%(random)s')
2221 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002222 self.assertFalse(f.usesTime())
2223 f = logging.Formatter('%(asctime)s')
2224 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002225 f = logging.Formatter('%(asctime)-15s')
2226 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002227 f = logging.Formatter('asctime')
2228 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002229
2230 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002231 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002232 r = self.get_record()
2233 f = logging.Formatter('$%{message}%$', style='{')
2234 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2235 f = logging.Formatter('{random}', style='{')
2236 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002237 self.assertFalse(f.usesTime())
2238 f = logging.Formatter('{asctime}', style='{')
2239 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002240 f = logging.Formatter('{asctime!s:15}', style='{')
2241 self.assertTrue(f.usesTime())
2242 f = logging.Formatter('{asctime:15}', style='{')
2243 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002244 f = logging.Formatter('asctime', style='{')
2245 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002246
2247 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002248 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002249 r = self.get_record()
2250 f = logging.Formatter('$message', style='$')
2251 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2252 f = logging.Formatter('$$%${message}%$$', style='$')
2253 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2254 f = logging.Formatter('${random}', style='$')
2255 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002256 self.assertFalse(f.usesTime())
2257 f = logging.Formatter('${asctime}', style='$')
2258 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002259 f = logging.Formatter('${asctime', style='$')
2260 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002261 f = logging.Formatter('$asctime', style='$')
2262 self.assertTrue(f.usesTime())
2263 f = logging.Formatter('asctime', style='$')
2264 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002265
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002266 def test_invalid_style(self):
2267 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2268
2269 def test_time(self):
2270 r = self.get_record()
2271 r.created = 735375780.0 # 21 April 1993 08:03:00
2272 r.msecs = 123
2273 f = logging.Formatter('%(asctime)s %(message)s')
2274 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2275 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002276 f.format(r)
2277 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2278
2279class TestBufferingFormatter(logging.BufferingFormatter):
2280 def formatHeader(self, records):
2281 return '[(%d)' % len(records)
2282
2283 def formatFooter(self, records):
2284 return '(%d)]' % len(records)
2285
2286class BufferingFormatterTest(unittest.TestCase):
2287 def setUp(self):
2288 self.records = [
2289 logging.makeLogRecord({'msg': 'one'}),
2290 logging.makeLogRecord({'msg': 'two'}),
2291 ]
2292
2293 def test_default(self):
2294 f = logging.BufferingFormatter()
2295 self.assertEqual('', f.format([]))
2296 self.assertEqual('onetwo', f.format(self.records))
2297
2298 def test_custom(self):
2299 f = TestBufferingFormatter()
2300 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2301 lf = logging.Formatter('<%(message)s>')
2302 f = TestBufferingFormatter(lf)
2303 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002304
2305class ExceptionTest(BaseTest):
2306 def test_formatting(self):
2307 r = self.root_logger
2308 h = RecordingHandler()
2309 r.addHandler(h)
2310 try:
2311 raise RuntimeError('deliberate mistake')
2312 except:
2313 logging.exception('failed', stack_info=True)
2314 r.removeHandler(h)
2315 h.close()
2316 r = h.records[0]
2317 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2318 'call last):\n'))
2319 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2320 'deliberate mistake'))
2321 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2322 'call last):\n'))
2323 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2324 'stack_info=True)'))
2325
2326
Vinay Sajip5a27d402010-12-10 11:42:57 +00002327class LastResortTest(BaseTest):
2328 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002329 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002330 root = self.root_logger
2331 root.removeHandler(self.root_hdlr)
2332 old_stderr = sys.stderr
2333 old_lastresort = logging.lastResort
2334 old_raise_exceptions = logging.raiseExceptions
2335 try:
2336 sys.stderr = sio = io.StringIO()
2337 root.warning('This is your final chance!')
2338 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2339 #No handlers and no last resort, so 'No handlers' message
2340 logging.lastResort = None
2341 sys.stderr = sio = io.StringIO()
2342 root.warning('This is your final chance!')
2343 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2344 # 'No handlers' message only printed once
2345 sys.stderr = sio = io.StringIO()
2346 root.warning('This is your final chance!')
2347 self.assertEqual(sio.getvalue(), '')
2348 root.manager.emittedNoHandlerWarning = False
2349 #If raiseExceptions is False, no message is printed
2350 logging.raiseExceptions = False
2351 sys.stderr = sio = io.StringIO()
2352 root.warning('This is your final chance!')
2353 self.assertEqual(sio.getvalue(), '')
2354 finally:
2355 sys.stderr = old_stderr
2356 root.addHandler(self.root_hdlr)
2357 logging.lastResort = old_lastresort
2358 logging.raiseExceptions = old_raise_exceptions
2359
2360
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002361class FakeHandler:
2362
2363 def __init__(self, identifier, called):
2364 for method in ('acquire', 'flush', 'close', 'release'):
2365 setattr(self, method, self.record_call(identifier, method, called))
2366
2367 def record_call(self, identifier, method_name, called):
2368 def inner():
2369 called.append('{} - {}'.format(identifier, method_name))
2370 return inner
2371
2372
2373class RecordingHandler(logging.NullHandler):
2374
2375 def __init__(self, *args, **kwargs):
2376 super(RecordingHandler, self).__init__(*args, **kwargs)
2377 self.records = []
2378
2379 def handle(self, record):
2380 """Keep track of all the emitted records."""
2381 self.records.append(record)
2382
2383
2384class ShutdownTest(BaseTest):
2385
Vinay Sajip5e66b162011-04-20 15:41:14 +01002386 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002387
2388 def setUp(self):
2389 super(ShutdownTest, self).setUp()
2390 self.called = []
2391
2392 raise_exceptions = logging.raiseExceptions
2393 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2394
2395 def raise_error(self, error):
2396 def inner():
2397 raise error()
2398 return inner
2399
2400 def test_no_failure(self):
2401 # create some fake handlers
2402 handler0 = FakeHandler(0, self.called)
2403 handler1 = FakeHandler(1, self.called)
2404 handler2 = FakeHandler(2, self.called)
2405
2406 # create live weakref to those handlers
2407 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2408
2409 logging.shutdown(handlerList=list(handlers))
2410
2411 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2412 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2413 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2414 self.assertEqual(expected, self.called)
2415
2416 def _test_with_failure_in_method(self, method, error):
2417 handler = FakeHandler(0, self.called)
2418 setattr(handler, method, self.raise_error(error))
2419 handlers = [logging.weakref.ref(handler)]
2420
2421 logging.shutdown(handlerList=list(handlers))
2422
2423 self.assertEqual('0 - release', self.called[-1])
2424
2425 def test_with_ioerror_in_acquire(self):
2426 self._test_with_failure_in_method('acquire', IOError)
2427
2428 def test_with_ioerror_in_flush(self):
2429 self._test_with_failure_in_method('flush', IOError)
2430
2431 def test_with_ioerror_in_close(self):
2432 self._test_with_failure_in_method('close', IOError)
2433
2434 def test_with_valueerror_in_acquire(self):
2435 self._test_with_failure_in_method('acquire', ValueError)
2436
2437 def test_with_valueerror_in_flush(self):
2438 self._test_with_failure_in_method('flush', ValueError)
2439
2440 def test_with_valueerror_in_close(self):
2441 self._test_with_failure_in_method('close', ValueError)
2442
2443 def test_with_other_error_in_acquire_without_raise(self):
2444 logging.raiseExceptions = False
2445 self._test_with_failure_in_method('acquire', IndexError)
2446
2447 def test_with_other_error_in_flush_without_raise(self):
2448 logging.raiseExceptions = False
2449 self._test_with_failure_in_method('flush', IndexError)
2450
2451 def test_with_other_error_in_close_without_raise(self):
2452 logging.raiseExceptions = False
2453 self._test_with_failure_in_method('close', IndexError)
2454
2455 def test_with_other_error_in_acquire_with_raise(self):
2456 logging.raiseExceptions = True
2457 self.assertRaises(IndexError, self._test_with_failure_in_method,
2458 'acquire', IndexError)
2459
2460 def test_with_other_error_in_flush_with_raise(self):
2461 logging.raiseExceptions = True
2462 self.assertRaises(IndexError, self._test_with_failure_in_method,
2463 'flush', IndexError)
2464
2465 def test_with_other_error_in_close_with_raise(self):
2466 logging.raiseExceptions = True
2467 self.assertRaises(IndexError, self._test_with_failure_in_method,
2468 'close', IndexError)
2469
2470
2471class ModuleLevelMiscTest(BaseTest):
2472
Vinay Sajip5e66b162011-04-20 15:41:14 +01002473 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002474
2475 def test_disable(self):
2476 old_disable = logging.root.manager.disable
2477 # confirm our assumptions are correct
2478 assert old_disable == 0
2479 self.addCleanup(lambda: logging.disable(old_disable))
2480
2481 logging.disable(83)
2482 self.assertEqual(logging.root.manager.disable, 83)
2483
2484 def _test_log(self, method, level=None):
2485 called = []
2486 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002487 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002488
2489 recording = RecordingHandler()
2490 logging.root.addHandler(recording)
2491
2492 log_method = getattr(logging, method)
2493 if level is not None:
2494 log_method(level, "test me: %r", recording)
2495 else:
2496 log_method("test me: %r", recording)
2497
2498 self.assertEqual(len(recording.records), 1)
2499 record = recording.records[0]
2500 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2501
2502 expected_level = level if level is not None else getattr(logging, method.upper())
2503 self.assertEqual(record.levelno, expected_level)
2504
2505 # basicConfig was not called!
2506 self.assertEqual(called, [])
2507
2508 def test_log(self):
2509 self._test_log('log', logging.ERROR)
2510
2511 def test_debug(self):
2512 self._test_log('debug')
2513
2514 def test_info(self):
2515 self._test_log('info')
2516
2517 def test_warning(self):
2518 self._test_log('warning')
2519
2520 def test_error(self):
2521 self._test_log('error')
2522
2523 def test_critical(self):
2524 self._test_log('critical')
2525
2526 def test_set_logger_class(self):
2527 self.assertRaises(TypeError, logging.setLoggerClass, object)
2528
2529 class MyLogger(logging.Logger):
2530 pass
2531
2532 logging.setLoggerClass(MyLogger)
2533 self.assertEqual(logging.getLoggerClass(), MyLogger)
2534
2535 logging.setLoggerClass(logging.Logger)
2536 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2537
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002538class LogRecordTest(BaseTest):
2539 def test_str_rep(self):
2540 r = logging.makeLogRecord({})
2541 s = str(r)
2542 self.assertTrue(s.startswith('<LogRecord: '))
2543 self.assertTrue(s.endswith('>'))
2544
2545 def test_dict_arg(self):
2546 h = RecordingHandler()
2547 r = logging.getLogger()
2548 r.addHandler(h)
2549 d = {'less' : 'more' }
2550 logging.warning('less is %(less)s', d)
2551 self.assertIs(h.records[0].args, d)
2552 self.assertEqual(h.records[0].message, 'less is more')
2553 r.removeHandler(h)
2554 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002555
2556class BasicConfigTest(unittest.TestCase):
2557
Vinay Sajip95bf5042011-04-20 11:50:56 +01002558 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002559
2560 def setUp(self):
2561 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01002562 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01002563 self.saved_handlers = logging._handlers.copy()
2564 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002565 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002566 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002567 logging.root.handlers = []
2568
2569 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01002570 for h in logging.root.handlers[:]:
2571 logging.root.removeHandler(h)
2572 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002573 super(BasicConfigTest, self).tearDown()
2574
Vinay Sajip3def7e02011-04-20 10:58:06 +01002575 def cleanup(self):
2576 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01002577 logging._handlers.clear()
2578 logging._handlers.update(self.saved_handlers)
2579 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002580 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002581
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002582 def test_no_kwargs(self):
2583 logging.basicConfig()
2584
2585 # handler defaults to a StreamHandler to sys.stderr
2586 self.assertEqual(len(logging.root.handlers), 1)
2587 handler = logging.root.handlers[0]
2588 self.assertIsInstance(handler, logging.StreamHandler)
2589 self.assertEqual(handler.stream, sys.stderr)
2590
2591 formatter = handler.formatter
2592 # format defaults to logging.BASIC_FORMAT
2593 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2594 # datefmt defaults to None
2595 self.assertIsNone(formatter.datefmt)
2596 # style defaults to %
2597 self.assertIsInstance(formatter._style, logging.PercentStyle)
2598
2599 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01002600 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002601
2602 def test_filename(self):
2603 logging.basicConfig(filename='test.log')
2604
2605 self.assertEqual(len(logging.root.handlers), 1)
2606 handler = logging.root.handlers[0]
2607 self.assertIsInstance(handler, logging.FileHandler)
2608
2609 expected = logging.FileHandler('test.log', 'a')
2610 self.addCleanup(expected.close)
2611 self.assertEqual(handler.stream.mode, expected.stream.mode)
2612 self.assertEqual(handler.stream.name, expected.stream.name)
2613
2614 def test_filemode(self):
2615 logging.basicConfig(filename='test.log', filemode='wb')
2616
2617 handler = logging.root.handlers[0]
2618 expected = logging.FileHandler('test.log', 'wb')
2619 self.addCleanup(expected.close)
2620 self.assertEqual(handler.stream.mode, expected.stream.mode)
2621
2622 def test_stream(self):
2623 stream = io.StringIO()
2624 self.addCleanup(stream.close)
2625 logging.basicConfig(stream=stream)
2626
2627 self.assertEqual(len(logging.root.handlers), 1)
2628 handler = logging.root.handlers[0]
2629 self.assertIsInstance(handler, logging.StreamHandler)
2630 self.assertEqual(handler.stream, stream)
2631
2632 def test_format(self):
2633 logging.basicConfig(format='foo')
2634
2635 formatter = logging.root.handlers[0].formatter
2636 self.assertEqual(formatter._style._fmt, 'foo')
2637
2638 def test_datefmt(self):
2639 logging.basicConfig(datefmt='bar')
2640
2641 formatter = logging.root.handlers[0].formatter
2642 self.assertEqual(formatter.datefmt, 'bar')
2643
2644 def test_style(self):
2645 logging.basicConfig(style='$')
2646
2647 formatter = logging.root.handlers[0].formatter
2648 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2649
2650 def test_level(self):
2651 old_level = logging.root.level
2652 self.addCleanup(lambda: logging.root.setLevel(old_level))
2653
2654 logging.basicConfig(level=57)
2655 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002656 # Test that second call has no effect
2657 logging.basicConfig(level=58)
2658 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002659
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002660 def test_incompatible(self):
2661 assertRaises = self.assertRaises
2662 handlers = [logging.StreamHandler()]
2663 stream = sys.stderr
2664 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2665 stream=stream)
2666 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2667 handlers=handlers)
2668 assertRaises(ValueError, logging.basicConfig, stream=stream,
2669 handlers=handlers)
2670
2671 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002672 handlers = [
2673 logging.StreamHandler(),
2674 logging.StreamHandler(sys.stdout),
2675 logging.StreamHandler(),
2676 ]
2677 f = logging.Formatter()
2678 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002679 logging.basicConfig(handlers=handlers)
2680 self.assertIs(handlers[0], logging.root.handlers[0])
2681 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002682 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002683 self.assertIsNotNone(handlers[0].formatter)
2684 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002685 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002686 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2687
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002688 def _test_log(self, method, level=None):
2689 # logging.root has no handlers so basicConfig should be called
2690 called = []
2691
2692 old_basic_config = logging.basicConfig
2693 def my_basic_config(*a, **kw):
2694 old_basic_config()
2695 old_level = logging.root.level
2696 logging.root.setLevel(100) # avoid having messages in stderr
2697 self.addCleanup(lambda: logging.root.setLevel(old_level))
2698 called.append((a, kw))
2699
2700 patch(self, logging, 'basicConfig', my_basic_config)
2701
2702 log_method = getattr(logging, method)
2703 if level is not None:
2704 log_method(level, "test me")
2705 else:
2706 log_method("test me")
2707
2708 # basicConfig was called with no arguments
2709 self.assertEqual(called, [((), {})])
2710
2711 def test_log(self):
2712 self._test_log('log', logging.WARNING)
2713
2714 def test_debug(self):
2715 self._test_log('debug')
2716
2717 def test_info(self):
2718 self._test_log('info')
2719
2720 def test_warning(self):
2721 self._test_log('warning')
2722
2723 def test_error(self):
2724 self._test_log('error')
2725
2726 def test_critical(self):
2727 self._test_log('critical')
2728
2729
2730class LoggerAdapterTest(unittest.TestCase):
2731
2732 def setUp(self):
2733 super(LoggerAdapterTest, self).setUp()
2734 old_handler_list = logging._handlerList[:]
2735
2736 self.recording = RecordingHandler()
2737 self.logger = logging.root
2738 self.logger.addHandler(self.recording)
2739 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2740 self.addCleanup(self.recording.close)
2741
2742 def cleanup():
2743 logging._handlerList[:] = old_handler_list
2744
2745 self.addCleanup(cleanup)
2746 self.addCleanup(logging.shutdown)
2747 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2748
2749 def test_exception(self):
2750 msg = 'testing exception: %r'
2751 exc = None
2752 try:
2753 assert False
2754 except AssertionError as e:
2755 exc = e
2756 self.adapter.exception(msg, self.recording)
2757
2758 self.assertEqual(len(self.recording.records), 1)
2759 record = self.recording.records[0]
2760 self.assertEqual(record.levelno, logging.ERROR)
2761 self.assertEqual(record.msg, msg)
2762 self.assertEqual(record.args, (self.recording,))
2763 self.assertEqual(record.exc_info,
2764 (exc.__class__, exc, exc.__traceback__))
2765
2766 def test_critical(self):
2767 msg = 'critical test! %r'
2768 self.adapter.critical(msg, self.recording)
2769
2770 self.assertEqual(len(self.recording.records), 1)
2771 record = self.recording.records[0]
2772 self.assertEqual(record.levelno, logging.CRITICAL)
2773 self.assertEqual(record.msg, msg)
2774 self.assertEqual(record.args, (self.recording,))
2775
2776 def test_is_enabled_for(self):
2777 old_disable = self.adapter.logger.manager.disable
2778 self.adapter.logger.manager.disable = 33
2779 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2780 'disable', old_disable))
2781 self.assertFalse(self.adapter.isEnabledFor(32))
2782
2783 def test_has_handlers(self):
2784 self.assertTrue(self.adapter.hasHandlers())
2785
2786 for handler in self.logger.handlers:
2787 self.logger.removeHandler(handler)
2788 assert not self.logger.hasHandlers()
2789
2790 self.assertFalse(self.adapter.hasHandlers())
2791
2792
2793class LoggerTest(BaseTest):
2794
2795 def setUp(self):
2796 super(LoggerTest, self).setUp()
2797 self.recording = RecordingHandler()
2798 self.logger = logging.Logger(name='blah')
2799 self.logger.addHandler(self.recording)
2800 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2801 self.addCleanup(self.recording.close)
2802 self.addCleanup(logging.shutdown)
2803
2804 def test_set_invalid_level(self):
2805 self.assertRaises(TypeError, self.logger.setLevel, object())
2806
2807 def test_exception(self):
2808 msg = 'testing exception: %r'
2809 exc = None
2810 try:
2811 assert False
2812 except AssertionError as e:
2813 exc = e
2814 self.logger.exception(msg, self.recording)
2815
2816 self.assertEqual(len(self.recording.records), 1)
2817 record = self.recording.records[0]
2818 self.assertEqual(record.levelno, logging.ERROR)
2819 self.assertEqual(record.msg, msg)
2820 self.assertEqual(record.args, (self.recording,))
2821 self.assertEqual(record.exc_info,
2822 (exc.__class__, exc, exc.__traceback__))
2823
2824 def test_log_invalid_level_with_raise(self):
2825 old_raise = logging.raiseExceptions
2826 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2827
2828 logging.raiseExceptions = True
2829 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2830
2831 def test_log_invalid_level_no_raise(self):
2832 old_raise = logging.raiseExceptions
2833 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2834
2835 logging.raiseExceptions = False
2836 self.logger.log('10', 'test message') # no exception happens
2837
2838 def test_find_caller_with_stack_info(self):
2839 called = []
2840 patch(self, logging.traceback, 'print_stack',
2841 lambda f, file: called.append(file.getvalue()))
2842
2843 self.logger.findCaller(stack_info=True)
2844
2845 self.assertEqual(len(called), 1)
2846 self.assertEqual('Stack (most recent call last):\n', called[0])
2847
2848 def test_make_record_with_extra_overwrite(self):
2849 name = 'my record'
2850 level = 13
2851 fn = lno = msg = args = exc_info = func = sinfo = None
2852 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2853 exc_info, func, sinfo)
2854
2855 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2856 extra = {key: 'some value'}
2857 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2858 fn, lno, msg, args, exc_info,
2859 extra=extra, sinfo=sinfo)
2860
2861 def test_make_record_with_extra_no_overwrite(self):
2862 name = 'my record'
2863 level = 13
2864 fn = lno = msg = args = exc_info = func = sinfo = None
2865 extra = {'valid_key': 'some value'}
2866 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2867 exc_info, extra=extra, sinfo=sinfo)
2868 self.assertIn('valid_key', result.__dict__)
2869
2870 def test_has_handlers(self):
2871 self.assertTrue(self.logger.hasHandlers())
2872
2873 for handler in self.logger.handlers:
2874 self.logger.removeHandler(handler)
2875 assert not self.logger.hasHandlers()
2876
2877 self.assertFalse(self.logger.hasHandlers())
2878
2879 def test_has_handlers_no_propagate(self):
2880 child_logger = logging.getLogger('blah.child')
2881 child_logger.propagate = False
2882 assert child_logger.handlers == []
2883
2884 self.assertFalse(child_logger.hasHandlers())
2885
2886 def test_is_enabled_for(self):
2887 old_disable = self.logger.manager.disable
2888 self.logger.manager.disable = 23
2889 self.addCleanup(lambda: setattr(self.logger.manager,
2890 'disable', old_disable))
2891 self.assertFalse(self.logger.isEnabledFor(22))
2892
2893
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002894class BaseFileTest(BaseTest):
2895 "Base class for handler tests that write log files"
2896
2897 def setUp(self):
2898 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002899 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2900 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002901 self.rmfiles = []
2902
2903 def tearDown(self):
2904 for fn in self.rmfiles:
2905 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002906 if os.path.exists(self.fn):
2907 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002908 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002909
2910 def assertLogFile(self, filename):
2911 "Assert a log file is there and register it for deletion"
2912 self.assertTrue(os.path.exists(filename),
2913 msg="Log file %r does not exist")
2914 self.rmfiles.append(filename)
2915
2916
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002917class FileHandlerTest(BaseFileTest):
2918 def test_delay(self):
2919 os.unlink(self.fn)
2920 fh = logging.FileHandler(self.fn, delay=True)
2921 self.assertIsNone(fh.stream)
2922 self.assertFalse(os.path.exists(self.fn))
2923 fh.handle(logging.makeLogRecord({}))
2924 self.assertIsNotNone(fh.stream)
2925 self.assertTrue(os.path.exists(self.fn))
2926 fh.close()
2927
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002928class RotatingFileHandlerTest(BaseFileTest):
2929 def next_rec(self):
2930 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2931 self.next_message(), None, None, None)
2932
2933 def test_should_not_rollover(self):
2934 # If maxbytes is zero rollover never occurs
2935 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2936 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002937 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002938
2939 def test_should_rollover(self):
2940 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2941 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002942 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002943
2944 def test_file_created(self):
2945 # checks that the file is created and assumes it was created
2946 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002947 rh = logging.handlers.RotatingFileHandler(self.fn)
2948 rh.emit(self.next_rec())
2949 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002950 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002951
2952 def test_rollover_filenames(self):
2953 rh = logging.handlers.RotatingFileHandler(
2954 self.fn, backupCount=2, maxBytes=1)
2955 rh.emit(self.next_rec())
2956 self.assertLogFile(self.fn)
2957 rh.emit(self.next_rec())
2958 self.assertLogFile(self.fn + ".1")
2959 rh.emit(self.next_rec())
2960 self.assertLogFile(self.fn + ".2")
2961 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002962 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002963
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002964class TimedRotatingFileHandlerTest(BaseFileTest):
2965 # test methods added below
2966 pass
2967
2968def secs(**kw):
2969 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2970
2971for when, exp in (('S', 1),
2972 ('M', 60),
2973 ('H', 60 * 60),
2974 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002975 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002976 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002977 ('W0', secs(days=4, hours=24)),
2978 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002979 def test_compute_rollover(self, when=when, exp=exp):
2980 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002981 self.fn, when=when, interval=1, backupCount=0, utc=True)
2982 currentTime = 0.0
2983 actual = rh.computeRollover(currentTime)
2984 if exp != actual:
2985 # Failures occur on some systems for MIDNIGHT and W0.
2986 # Print detailed calculation for MIDNIGHT so we can try to see
2987 # what's going on
2988 import time
2989 if when == 'MIDNIGHT':
2990 try:
2991 if rh.utc:
2992 t = time.gmtime(currentTime)
2993 else:
2994 t = time.localtime(currentTime)
2995 currentHour = t[3]
2996 currentMinute = t[4]
2997 currentSecond = t[5]
2998 # r is the number of seconds left between now and midnight
2999 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
3000 currentMinute) * 60 +
3001 currentSecond)
3002 result = currentTime + r
3003 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
3004 print('currentHour: %s' % currentHour, file=sys.stderr)
3005 print('currentMinute: %s' % currentMinute, file=sys.stderr)
3006 print('currentSecond: %s' % currentSecond, file=sys.stderr)
3007 print('r: %s' % r, file=sys.stderr)
3008 print('result: %s' % result, file=sys.stderr)
3009 except Exception:
3010 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
3011 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00003012 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00003013 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
3014
Christian Heimes180510d2008-03-03 19:15:45 +00003015# Set the locale to the platform-dependent default. I have no idea
3016# why the test does this, but in any case we save the current locale
3017# first and restore it at the end.
3018@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00003019def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00003020 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003021 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003022 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003023 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip985ef872011-04-26 19:34:04 +01003024 FormatterTest, BufferingFormatterTest, StreamHandlerTest,
Vinay Sajip61561522010-12-03 11:50:38 +00003025 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003026 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
3027 LoggerAdapterTest, LoggerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003028 FileHandlerTest, RotatingFileHandlerTest,
3029 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003030 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003031 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003032
Christian Heimes180510d2008-03-03 19:15:45 +00003033if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003034 test_main()