blob: cad0ad7c6ef4ebcf5cab6377b093ad1e3d5b0acc [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
2050
2051class ManagerTest(BaseTest):
2052 def test_manager_loggerclass(self):
2053 logged = []
2054
2055 class MyLogger(logging.Logger):
2056 def _log(self, level, msg, args, exc_info=None, extra=None):
2057 logged.append(msg)
2058
2059 man = logging.Manager(None)
2060 self.assertRaises(TypeError, man.setLoggerClass, int)
2061 man.setLoggerClass(MyLogger)
2062 logger = man.getLogger('test')
2063 logger.warning('should appear in logged')
2064 logging.warning('should not appear in logged')
2065
2066 self.assertEqual(logged, ['should appear in logged'])
2067
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002068 def test_set_log_record_factory(self):
2069 man = logging.Manager(None)
2070 expected = object()
2071 man.setLogRecordFactory(expected)
2072 self.assertEqual(man.logRecordFactory, expected)
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00002073
Benjamin Peterson22005fc2010-04-11 16:25:06 +00002074class ChildLoggerTest(BaseTest):
2075 def test_child_loggers(self):
2076 r = logging.getLogger()
2077 l1 = logging.getLogger('abc')
2078 l2 = logging.getLogger('def.ghi')
2079 c1 = r.getChild('xyz')
2080 c2 = r.getChild('uvw.xyz')
2081 self.assertTrue(c1 is logging.getLogger('xyz'))
2082 self.assertTrue(c2 is logging.getLogger('uvw.xyz'))
2083 c1 = l1.getChild('def')
2084 c2 = c1.getChild('ghi')
2085 c3 = l1.getChild('def.ghi')
2086 self.assertTrue(c1 is logging.getLogger('abc.def'))
2087 self.assertTrue(c2 is logging.getLogger('abc.def.ghi'))
2088 self.assertTrue(c2 is c3)
2089
2090
Vinay Sajip6fac8172010-10-19 20:44:14 +00002091class DerivedLogRecord(logging.LogRecord):
2092 pass
2093
Vinay Sajip61561522010-12-03 11:50:38 +00002094class LogRecordFactoryTest(BaseTest):
Vinay Sajip6fac8172010-10-19 20:44:14 +00002095
2096 def setUp(self):
2097 class CheckingFilter(logging.Filter):
2098 def __init__(self, cls):
2099 self.cls = cls
2100
2101 def filter(self, record):
2102 t = type(record)
2103 if t is not self.cls:
2104 msg = 'Unexpected LogRecord type %s, expected %s' % (t,
2105 self.cls)
2106 raise TypeError(msg)
2107 return True
2108
2109 BaseTest.setUp(self)
2110 self.filter = CheckingFilter(DerivedLogRecord)
2111 self.root_logger.addFilter(self.filter)
Vinay Sajip61561522010-12-03 11:50:38 +00002112 self.orig_factory = logging.getLogRecordFactory()
Vinay Sajip6fac8172010-10-19 20:44:14 +00002113
2114 def tearDown(self):
2115 self.root_logger.removeFilter(self.filter)
2116 BaseTest.tearDown(self)
Vinay Sajip61561522010-12-03 11:50:38 +00002117 logging.setLogRecordFactory(self.orig_factory)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002118
2119 def test_logrecord_class(self):
2120 self.assertRaises(TypeError, self.root_logger.warning,
2121 self.next_message())
Vinay Sajip61561522010-12-03 11:50:38 +00002122 logging.setLogRecordFactory(DerivedLogRecord)
Vinay Sajip6fac8172010-10-19 20:44:14 +00002123 self.root_logger.error(self.next_message())
2124 self.assert_log_lines([
2125 ('root', 'ERROR', '2'),
2126 ])
2127
2128
Vinay Sajip8552d1f2010-09-14 09:34:09 +00002129class QueueHandlerTest(BaseTest):
2130 # Do not bother with a logger name group.
2131 expected_log_pat = r"^[\w.]+ -> ([\w]+): ([\d]+)$"
2132
2133 def setUp(self):
2134 BaseTest.setUp(self)
2135 self.queue = queue.Queue(-1)
2136 self.que_hdlr = logging.handlers.QueueHandler(self.queue)
2137 self.que_logger = logging.getLogger('que')
2138 self.que_logger.propagate = False
2139 self.que_logger.setLevel(logging.WARNING)
2140 self.que_logger.addHandler(self.que_hdlr)
2141
2142 def tearDown(self):
2143 self.que_hdlr.close()
2144 BaseTest.tearDown(self)
2145
2146 def test_queue_handler(self):
2147 self.que_logger.debug(self.next_message())
2148 self.assertRaises(queue.Empty, self.queue.get_nowait)
2149 self.que_logger.info(self.next_message())
2150 self.assertRaises(queue.Empty, self.queue.get_nowait)
2151 msg = self.next_message()
2152 self.que_logger.warning(msg)
2153 data = self.queue.get_nowait()
2154 self.assertTrue(isinstance(data, logging.LogRecord))
2155 self.assertEqual(data.name, self.que_logger.name)
2156 self.assertEqual((data.msg, data.args), (msg, None))
2157
Vinay Sajipe723e962011-04-15 22:27:17 +01002158 def test_queue_listener(self):
2159 handler = TestHandler(Matcher())
2160 listener = logging.handlers.QueueListener(self.queue, handler)
2161 listener.start()
2162 try:
2163 self.que_logger.warning(self.next_message())
2164 self.que_logger.error(self.next_message())
2165 self.que_logger.critical(self.next_message())
2166 finally:
2167 listener.stop()
2168 self.assertTrue(handler.matches(levelno=logging.WARNING, message='1'))
2169 self.assertTrue(handler.matches(levelno=logging.ERROR, message='2'))
2170 self.assertTrue(handler.matches(levelno=logging.CRITICAL, message='3'))
2171
2172
Vinay Sajipa39c5712010-10-25 13:57:39 +00002173class FormatterTest(unittest.TestCase):
2174 def setUp(self):
2175 self.common = {
2176 'name': 'formatter.test',
2177 'level': logging.DEBUG,
2178 'pathname': os.path.join('path', 'to', 'dummy.ext'),
2179 'lineno': 42,
2180 'exc_info': None,
2181 'func': None,
2182 'msg': 'Message with %d %s',
2183 'args': (2, 'placeholders'),
2184 }
2185 self.variants = {
2186 }
2187
2188 def get_record(self, name=None):
2189 result = dict(self.common)
2190 if name is not None:
2191 result.update(self.variants[name])
2192 return logging.makeLogRecord(result)
2193
2194 def test_percent(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002195 # Test %-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002196 r = self.get_record()
2197 f = logging.Formatter('${%(message)s}')
2198 self.assertEqual(f.format(r), '${Message with 2 placeholders}')
2199 f = logging.Formatter('%(random)s')
2200 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002201 self.assertFalse(f.usesTime())
2202 f = logging.Formatter('%(asctime)s')
2203 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002204 f = logging.Formatter('%(asctime)-15s')
2205 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002206 f = logging.Formatter('asctime')
2207 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002208
2209 def test_braces(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002210 # Test {}-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002211 r = self.get_record()
2212 f = logging.Formatter('$%{message}%$', style='{')
2213 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2214 f = logging.Formatter('{random}', style='{')
2215 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002216 self.assertFalse(f.usesTime())
2217 f = logging.Formatter('{asctime}', style='{')
2218 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002219 f = logging.Formatter('{asctime!s:15}', style='{')
2220 self.assertTrue(f.usesTime())
2221 f = logging.Formatter('{asctime:15}', style='{')
2222 self.assertTrue(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002223 f = logging.Formatter('asctime', style='{')
2224 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002225
2226 def test_dollars(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002227 # Test $-formatting
Vinay Sajipa39c5712010-10-25 13:57:39 +00002228 r = self.get_record()
2229 f = logging.Formatter('$message', style='$')
2230 self.assertEqual(f.format(r), 'Message with 2 placeholders')
2231 f = logging.Formatter('$$%${message}%$$', style='$')
2232 self.assertEqual(f.format(r), '$%Message with 2 placeholders%$')
2233 f = logging.Formatter('${random}', style='$')
2234 self.assertRaises(KeyError, f.format, r)
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002235 self.assertFalse(f.usesTime())
2236 f = logging.Formatter('${asctime}', style='$')
2237 self.assertTrue(f.usesTime())
Vinay Sajip89807a52011-02-26 16:06:02 +00002238 f = logging.Formatter('${asctime', style='$')
2239 self.assertFalse(f.usesTime())
Vinay Sajip6a65c5d2010-10-26 13:16:11 +00002240 f = logging.Formatter('$asctime', style='$')
2241 self.assertTrue(f.usesTime())
2242 f = logging.Formatter('asctime', style='$')
2243 self.assertFalse(f.usesTime())
Vinay Sajipa39c5712010-10-25 13:57:39 +00002244
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002245 def test_invalid_style(self):
2246 self.assertRaises(ValueError, logging.Formatter, None, None, 'x')
2247
2248 def test_time(self):
2249 r = self.get_record()
2250 r.created = 735375780.0 # 21 April 1993 08:03:00
2251 r.msecs = 123
2252 f = logging.Formatter('%(asctime)s %(message)s')
2253 self.assertEqual(f.formatTime(r), '1993-04-21 08:03:00,123')
2254 self.assertEqual(f.formatTime(r, '%Y:%d'), '1993:21')
Vinay Sajip985ef872011-04-26 19:34:04 +01002255 f.format(r)
2256 self.assertEqual(r.asctime, '1993-04-21 08:03:00,123')
2257
2258class TestBufferingFormatter(logging.BufferingFormatter):
2259 def formatHeader(self, records):
2260 return '[(%d)' % len(records)
2261
2262 def formatFooter(self, records):
2263 return '(%d)]' % len(records)
2264
2265class BufferingFormatterTest(unittest.TestCase):
2266 def setUp(self):
2267 self.records = [
2268 logging.makeLogRecord({'msg': 'one'}),
2269 logging.makeLogRecord({'msg': 'two'}),
2270 ]
2271
2272 def test_default(self):
2273 f = logging.BufferingFormatter()
2274 self.assertEqual('', f.format([]))
2275 self.assertEqual('onetwo', f.format(self.records))
2276
2277 def test_custom(self):
2278 f = TestBufferingFormatter()
2279 self.assertEqual('[(2)onetwo(2)]', f.format(self.records))
2280 lf = logging.Formatter('<%(message)s>')
2281 f = TestBufferingFormatter(lf)
2282 self.assertEqual('[(2)<one><two>(2)]', f.format(self.records))
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002283
2284class ExceptionTest(BaseTest):
2285 def test_formatting(self):
2286 r = self.root_logger
2287 h = RecordingHandler()
2288 r.addHandler(h)
2289 try:
2290 raise RuntimeError('deliberate mistake')
2291 except:
2292 logging.exception('failed', stack_info=True)
2293 r.removeHandler(h)
2294 h.close()
2295 r = h.records[0]
2296 self.assertTrue(r.exc_text.startswith('Traceback (most recent '
2297 'call last):\n'))
2298 self.assertTrue(r.exc_text.endswith('\nRuntimeError: '
2299 'deliberate mistake'))
2300 self.assertTrue(r.stack_info.startswith('Stack (most recent '
2301 'call last):\n'))
2302 self.assertTrue(r.stack_info.endswith('logging.exception(\'failed\', '
2303 'stack_info=True)'))
2304
2305
Vinay Sajip5a27d402010-12-10 11:42:57 +00002306class LastResortTest(BaseTest):
2307 def test_last_resort(self):
Vinay Sajip60b4df12010-12-27 11:18:52 +00002308 # Test the last resort handler
Vinay Sajip5a27d402010-12-10 11:42:57 +00002309 root = self.root_logger
2310 root.removeHandler(self.root_hdlr)
2311 old_stderr = sys.stderr
2312 old_lastresort = logging.lastResort
2313 old_raise_exceptions = logging.raiseExceptions
2314 try:
2315 sys.stderr = sio = io.StringIO()
2316 root.warning('This is your final chance!')
2317 self.assertEqual(sio.getvalue(), 'This is your final chance!\n')
2318 #No handlers and no last resort, so 'No handlers' message
2319 logging.lastResort = None
2320 sys.stderr = sio = io.StringIO()
2321 root.warning('This is your final chance!')
2322 self.assertEqual(sio.getvalue(), 'No handlers could be found for logger "root"\n')
2323 # 'No handlers' message only printed once
2324 sys.stderr = sio = io.StringIO()
2325 root.warning('This is your final chance!')
2326 self.assertEqual(sio.getvalue(), '')
2327 root.manager.emittedNoHandlerWarning = False
2328 #If raiseExceptions is False, no message is printed
2329 logging.raiseExceptions = False
2330 sys.stderr = sio = io.StringIO()
2331 root.warning('This is your final chance!')
2332 self.assertEqual(sio.getvalue(), '')
2333 finally:
2334 sys.stderr = old_stderr
2335 root.addHandler(self.root_hdlr)
2336 logging.lastResort = old_lastresort
2337 logging.raiseExceptions = old_raise_exceptions
2338
2339
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002340class FakeHandler:
2341
2342 def __init__(self, identifier, called):
2343 for method in ('acquire', 'flush', 'close', 'release'):
2344 setattr(self, method, self.record_call(identifier, method, called))
2345
2346 def record_call(self, identifier, method_name, called):
2347 def inner():
2348 called.append('{} - {}'.format(identifier, method_name))
2349 return inner
2350
2351
2352class RecordingHandler(logging.NullHandler):
2353
2354 def __init__(self, *args, **kwargs):
2355 super(RecordingHandler, self).__init__(*args, **kwargs)
2356 self.records = []
2357
2358 def handle(self, record):
2359 """Keep track of all the emitted records."""
2360 self.records.append(record)
2361
2362
2363class ShutdownTest(BaseTest):
2364
Vinay Sajip5e66b162011-04-20 15:41:14 +01002365 """Test suite for the shutdown method."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002366
2367 def setUp(self):
2368 super(ShutdownTest, self).setUp()
2369 self.called = []
2370
2371 raise_exceptions = logging.raiseExceptions
2372 self.addCleanup(lambda: setattr(logging, 'raiseExceptions', raise_exceptions))
2373
2374 def raise_error(self, error):
2375 def inner():
2376 raise error()
2377 return inner
2378
2379 def test_no_failure(self):
2380 # create some fake handlers
2381 handler0 = FakeHandler(0, self.called)
2382 handler1 = FakeHandler(1, self.called)
2383 handler2 = FakeHandler(2, self.called)
2384
2385 # create live weakref to those handlers
2386 handlers = map(logging.weakref.ref, [handler0, handler1, handler2])
2387
2388 logging.shutdown(handlerList=list(handlers))
2389
2390 expected = ['2 - acquire', '2 - flush', '2 - close', '2 - release',
2391 '1 - acquire', '1 - flush', '1 - close', '1 - release',
2392 '0 - acquire', '0 - flush', '0 - close', '0 - release']
2393 self.assertEqual(expected, self.called)
2394
2395 def _test_with_failure_in_method(self, method, error):
2396 handler = FakeHandler(0, self.called)
2397 setattr(handler, method, self.raise_error(error))
2398 handlers = [logging.weakref.ref(handler)]
2399
2400 logging.shutdown(handlerList=list(handlers))
2401
2402 self.assertEqual('0 - release', self.called[-1])
2403
2404 def test_with_ioerror_in_acquire(self):
2405 self._test_with_failure_in_method('acquire', IOError)
2406
2407 def test_with_ioerror_in_flush(self):
2408 self._test_with_failure_in_method('flush', IOError)
2409
2410 def test_with_ioerror_in_close(self):
2411 self._test_with_failure_in_method('close', IOError)
2412
2413 def test_with_valueerror_in_acquire(self):
2414 self._test_with_failure_in_method('acquire', ValueError)
2415
2416 def test_with_valueerror_in_flush(self):
2417 self._test_with_failure_in_method('flush', ValueError)
2418
2419 def test_with_valueerror_in_close(self):
2420 self._test_with_failure_in_method('close', ValueError)
2421
2422 def test_with_other_error_in_acquire_without_raise(self):
2423 logging.raiseExceptions = False
2424 self._test_with_failure_in_method('acquire', IndexError)
2425
2426 def test_with_other_error_in_flush_without_raise(self):
2427 logging.raiseExceptions = False
2428 self._test_with_failure_in_method('flush', IndexError)
2429
2430 def test_with_other_error_in_close_without_raise(self):
2431 logging.raiseExceptions = False
2432 self._test_with_failure_in_method('close', IndexError)
2433
2434 def test_with_other_error_in_acquire_with_raise(self):
2435 logging.raiseExceptions = True
2436 self.assertRaises(IndexError, self._test_with_failure_in_method,
2437 'acquire', IndexError)
2438
2439 def test_with_other_error_in_flush_with_raise(self):
2440 logging.raiseExceptions = True
2441 self.assertRaises(IndexError, self._test_with_failure_in_method,
2442 'flush', IndexError)
2443
2444 def test_with_other_error_in_close_with_raise(self):
2445 logging.raiseExceptions = True
2446 self.assertRaises(IndexError, self._test_with_failure_in_method,
2447 'close', IndexError)
2448
2449
2450class ModuleLevelMiscTest(BaseTest):
2451
Vinay Sajip5e66b162011-04-20 15:41:14 +01002452 """Test suite for some module level methods."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002453
2454 def test_disable(self):
2455 old_disable = logging.root.manager.disable
2456 # confirm our assumptions are correct
2457 assert old_disable == 0
2458 self.addCleanup(lambda: logging.disable(old_disable))
2459
2460 logging.disable(83)
2461 self.assertEqual(logging.root.manager.disable, 83)
2462
2463 def _test_log(self, method, level=None):
2464 called = []
2465 patch(self, logging, 'basicConfig',
Vinay Sajip00bdbe12011-04-22 00:17:46 +01002466 lambda *a, **kw: called.append((a, kw)))
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002467
2468 recording = RecordingHandler()
2469 logging.root.addHandler(recording)
2470
2471 log_method = getattr(logging, method)
2472 if level is not None:
2473 log_method(level, "test me: %r", recording)
2474 else:
2475 log_method("test me: %r", recording)
2476
2477 self.assertEqual(len(recording.records), 1)
2478 record = recording.records[0]
2479 self.assertEqual(record.getMessage(), "test me: %r" % recording)
2480
2481 expected_level = level if level is not None else getattr(logging, method.upper())
2482 self.assertEqual(record.levelno, expected_level)
2483
2484 # basicConfig was not called!
2485 self.assertEqual(called, [])
2486
2487 def test_log(self):
2488 self._test_log('log', logging.ERROR)
2489
2490 def test_debug(self):
2491 self._test_log('debug')
2492
2493 def test_info(self):
2494 self._test_log('info')
2495
2496 def test_warning(self):
2497 self._test_log('warning')
2498
2499 def test_error(self):
2500 self._test_log('error')
2501
2502 def test_critical(self):
2503 self._test_log('critical')
2504
2505 def test_set_logger_class(self):
2506 self.assertRaises(TypeError, logging.setLoggerClass, object)
2507
2508 class MyLogger(logging.Logger):
2509 pass
2510
2511 logging.setLoggerClass(MyLogger)
2512 self.assertEqual(logging.getLoggerClass(), MyLogger)
2513
2514 logging.setLoggerClass(logging.Logger)
2515 self.assertEqual(logging.getLoggerClass(), logging.Logger)
2516
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002517class LogRecordTest(BaseTest):
2518 def test_str_rep(self):
2519 r = logging.makeLogRecord({})
2520 s = str(r)
2521 self.assertTrue(s.startswith('<LogRecord: '))
2522 self.assertTrue(s.endswith('>'))
2523
2524 def test_dict_arg(self):
2525 h = RecordingHandler()
2526 r = logging.getLogger()
2527 r.addHandler(h)
2528 d = {'less' : 'more' }
2529 logging.warning('less is %(less)s', d)
2530 self.assertIs(h.records[0].args, d)
2531 self.assertEqual(h.records[0].message, 'less is more')
2532 r.removeHandler(h)
2533 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002534
2535class BasicConfigTest(unittest.TestCase):
2536
Vinay Sajip95bf5042011-04-20 11:50:56 +01002537 """Test suite for logging.basicConfig."""
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002538
2539 def setUp(self):
2540 super(BasicConfigTest, self).setUp()
Vinay Sajip3def7e02011-04-20 10:58:06 +01002541 self.handlers = logging.root.handlers
Vinay Sajip95bf5042011-04-20 11:50:56 +01002542 self.saved_handlers = logging._handlers.copy()
2543 self.saved_handler_list = logging._handlerList[:]
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002544 self.original_logging_level = logging.root.level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002545 self.addCleanup(self.cleanup)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002546 logging.root.handlers = []
2547
2548 def tearDown(self):
Vinay Sajip3def7e02011-04-20 10:58:06 +01002549 for h in logging.root.handlers[:]:
2550 logging.root.removeHandler(h)
2551 h.close()
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002552 super(BasicConfigTest, self).tearDown()
2553
Vinay Sajip3def7e02011-04-20 10:58:06 +01002554 def cleanup(self):
2555 setattr(logging.root, 'handlers', self.handlers)
Vinay Sajip95bf5042011-04-20 11:50:56 +01002556 logging._handlers.clear()
2557 logging._handlers.update(self.saved_handlers)
2558 logging._handlerList[:] = self.saved_handler_list
Vinay Sajip1e42f9e2011-04-20 12:20:44 +01002559 logging.root.level = self.original_logging_level
Vinay Sajip3def7e02011-04-20 10:58:06 +01002560
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002561 def test_no_kwargs(self):
2562 logging.basicConfig()
2563
2564 # handler defaults to a StreamHandler to sys.stderr
2565 self.assertEqual(len(logging.root.handlers), 1)
2566 handler = logging.root.handlers[0]
2567 self.assertIsInstance(handler, logging.StreamHandler)
2568 self.assertEqual(handler.stream, sys.stderr)
2569
2570 formatter = handler.formatter
2571 # format defaults to logging.BASIC_FORMAT
2572 self.assertEqual(formatter._style._fmt, logging.BASIC_FORMAT)
2573 # datefmt defaults to None
2574 self.assertIsNone(formatter.datefmt)
2575 # style defaults to %
2576 self.assertIsInstance(formatter._style, logging.PercentStyle)
2577
2578 # level is not explicitely set
Vinay Sajip5b9eecf2011-04-20 12:50:42 +01002579 self.assertEqual(logging.root.level, self.original_logging_level)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002580
2581 def test_filename(self):
2582 logging.basicConfig(filename='test.log')
2583
2584 self.assertEqual(len(logging.root.handlers), 1)
2585 handler = logging.root.handlers[0]
2586 self.assertIsInstance(handler, logging.FileHandler)
2587
2588 expected = logging.FileHandler('test.log', 'a')
2589 self.addCleanup(expected.close)
2590 self.assertEqual(handler.stream.mode, expected.stream.mode)
2591 self.assertEqual(handler.stream.name, expected.stream.name)
2592
2593 def test_filemode(self):
2594 logging.basicConfig(filename='test.log', filemode='wb')
2595
2596 handler = logging.root.handlers[0]
2597 expected = logging.FileHandler('test.log', 'wb')
2598 self.addCleanup(expected.close)
2599 self.assertEqual(handler.stream.mode, expected.stream.mode)
2600
2601 def test_stream(self):
2602 stream = io.StringIO()
2603 self.addCleanup(stream.close)
2604 logging.basicConfig(stream=stream)
2605
2606 self.assertEqual(len(logging.root.handlers), 1)
2607 handler = logging.root.handlers[0]
2608 self.assertIsInstance(handler, logging.StreamHandler)
2609 self.assertEqual(handler.stream, stream)
2610
2611 def test_format(self):
2612 logging.basicConfig(format='foo')
2613
2614 formatter = logging.root.handlers[0].formatter
2615 self.assertEqual(formatter._style._fmt, 'foo')
2616
2617 def test_datefmt(self):
2618 logging.basicConfig(datefmt='bar')
2619
2620 formatter = logging.root.handlers[0].formatter
2621 self.assertEqual(formatter.datefmt, 'bar')
2622
2623 def test_style(self):
2624 logging.basicConfig(style='$')
2625
2626 formatter = logging.root.handlers[0].formatter
2627 self.assertIsInstance(formatter._style, logging.StringTemplateStyle)
2628
2629 def test_level(self):
2630 old_level = logging.root.level
2631 self.addCleanup(lambda: logging.root.setLevel(old_level))
2632
2633 logging.basicConfig(level=57)
2634 self.assertEqual(logging.root.level, 57)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002635 # Test that second call has no effect
2636 logging.basicConfig(level=58)
2637 self.assertEqual(logging.root.level, 57)
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002638
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002639 def test_incompatible(self):
2640 assertRaises = self.assertRaises
2641 handlers = [logging.StreamHandler()]
2642 stream = sys.stderr
2643 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2644 stream=stream)
2645 assertRaises(ValueError, logging.basicConfig, filename='test.log',
2646 handlers=handlers)
2647 assertRaises(ValueError, logging.basicConfig, stream=stream,
2648 handlers=handlers)
2649
2650 def test_handlers(self):
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002651 handlers = [
2652 logging.StreamHandler(),
2653 logging.StreamHandler(sys.stdout),
2654 logging.StreamHandler(),
2655 ]
2656 f = logging.Formatter()
2657 handlers[2].setFormatter(f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002658 logging.basicConfig(handlers=handlers)
2659 self.assertIs(handlers[0], logging.root.handlers[0])
2660 self.assertIs(handlers[1], logging.root.handlers[1])
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002661 self.assertIs(handlers[2], logging.root.handlers[2])
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002662 self.assertIsNotNone(handlers[0].formatter)
2663 self.assertIsNotNone(handlers[1].formatter)
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002664 self.assertIs(handlers[2].formatter, f)
Vinay Sajip4a0a31d2011-04-11 08:42:07 +01002665 self.assertIs(handlers[0].formatter, handlers[1].formatter)
2666
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01002667 def _test_log(self, method, level=None):
2668 # logging.root has no handlers so basicConfig should be called
2669 called = []
2670
2671 old_basic_config = logging.basicConfig
2672 def my_basic_config(*a, **kw):
2673 old_basic_config()
2674 old_level = logging.root.level
2675 logging.root.setLevel(100) # avoid having messages in stderr
2676 self.addCleanup(lambda: logging.root.setLevel(old_level))
2677 called.append((a, kw))
2678
2679 patch(self, logging, 'basicConfig', my_basic_config)
2680
2681 log_method = getattr(logging, method)
2682 if level is not None:
2683 log_method(level, "test me")
2684 else:
2685 log_method("test me")
2686
2687 # basicConfig was called with no arguments
2688 self.assertEqual(called, [((), {})])
2689
2690 def test_log(self):
2691 self._test_log('log', logging.WARNING)
2692
2693 def test_debug(self):
2694 self._test_log('debug')
2695
2696 def test_info(self):
2697 self._test_log('info')
2698
2699 def test_warning(self):
2700 self._test_log('warning')
2701
2702 def test_error(self):
2703 self._test_log('error')
2704
2705 def test_critical(self):
2706 self._test_log('critical')
2707
2708
2709class LoggerAdapterTest(unittest.TestCase):
2710
2711 def setUp(self):
2712 super(LoggerAdapterTest, self).setUp()
2713 old_handler_list = logging._handlerList[:]
2714
2715 self.recording = RecordingHandler()
2716 self.logger = logging.root
2717 self.logger.addHandler(self.recording)
2718 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2719 self.addCleanup(self.recording.close)
2720
2721 def cleanup():
2722 logging._handlerList[:] = old_handler_list
2723
2724 self.addCleanup(cleanup)
2725 self.addCleanup(logging.shutdown)
2726 self.adapter = logging.LoggerAdapter(logger=self.logger, extra=None)
2727
2728 def test_exception(self):
2729 msg = 'testing exception: %r'
2730 exc = None
2731 try:
2732 assert False
2733 except AssertionError as e:
2734 exc = e
2735 self.adapter.exception(msg, self.recording)
2736
2737 self.assertEqual(len(self.recording.records), 1)
2738 record = self.recording.records[0]
2739 self.assertEqual(record.levelno, logging.ERROR)
2740 self.assertEqual(record.msg, msg)
2741 self.assertEqual(record.args, (self.recording,))
2742 self.assertEqual(record.exc_info,
2743 (exc.__class__, exc, exc.__traceback__))
2744
2745 def test_critical(self):
2746 msg = 'critical test! %r'
2747 self.adapter.critical(msg, self.recording)
2748
2749 self.assertEqual(len(self.recording.records), 1)
2750 record = self.recording.records[0]
2751 self.assertEqual(record.levelno, logging.CRITICAL)
2752 self.assertEqual(record.msg, msg)
2753 self.assertEqual(record.args, (self.recording,))
2754
2755 def test_is_enabled_for(self):
2756 old_disable = self.adapter.logger.manager.disable
2757 self.adapter.logger.manager.disable = 33
2758 self.addCleanup(lambda: setattr(self.adapter.logger.manager,
2759 'disable', old_disable))
2760 self.assertFalse(self.adapter.isEnabledFor(32))
2761
2762 def test_has_handlers(self):
2763 self.assertTrue(self.adapter.hasHandlers())
2764
2765 for handler in self.logger.handlers:
2766 self.logger.removeHandler(handler)
2767 assert not self.logger.hasHandlers()
2768
2769 self.assertFalse(self.adapter.hasHandlers())
2770
2771
2772class LoggerTest(BaseTest):
2773
2774 def setUp(self):
2775 super(LoggerTest, self).setUp()
2776 self.recording = RecordingHandler()
2777 self.logger = logging.Logger(name='blah')
2778 self.logger.addHandler(self.recording)
2779 self.addCleanup(lambda: self.logger.removeHandler(self.recording))
2780 self.addCleanup(self.recording.close)
2781 self.addCleanup(logging.shutdown)
2782
2783 def test_set_invalid_level(self):
2784 self.assertRaises(TypeError, self.logger.setLevel, object())
2785
2786 def test_exception(self):
2787 msg = 'testing exception: %r'
2788 exc = None
2789 try:
2790 assert False
2791 except AssertionError as e:
2792 exc = e
2793 self.logger.exception(msg, self.recording)
2794
2795 self.assertEqual(len(self.recording.records), 1)
2796 record = self.recording.records[0]
2797 self.assertEqual(record.levelno, logging.ERROR)
2798 self.assertEqual(record.msg, msg)
2799 self.assertEqual(record.args, (self.recording,))
2800 self.assertEqual(record.exc_info,
2801 (exc.__class__, exc, exc.__traceback__))
2802
2803 def test_log_invalid_level_with_raise(self):
2804 old_raise = logging.raiseExceptions
2805 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2806
2807 logging.raiseExceptions = True
2808 self.assertRaises(TypeError, self.logger.log, '10', 'test message')
2809
2810 def test_log_invalid_level_no_raise(self):
2811 old_raise = logging.raiseExceptions
2812 self.addCleanup(lambda: setattr(logging, 'raiseExecptions', old_raise))
2813
2814 logging.raiseExceptions = False
2815 self.logger.log('10', 'test message') # no exception happens
2816
2817 def test_find_caller_with_stack_info(self):
2818 called = []
2819 patch(self, logging.traceback, 'print_stack',
2820 lambda f, file: called.append(file.getvalue()))
2821
2822 self.logger.findCaller(stack_info=True)
2823
2824 self.assertEqual(len(called), 1)
2825 self.assertEqual('Stack (most recent call last):\n', called[0])
2826
2827 def test_make_record_with_extra_overwrite(self):
2828 name = 'my record'
2829 level = 13
2830 fn = lno = msg = args = exc_info = func = sinfo = None
2831 rv = logging._logRecordFactory(name, level, fn, lno, msg, args,
2832 exc_info, func, sinfo)
2833
2834 for key in ('message', 'asctime') + tuple(rv.__dict__.keys()):
2835 extra = {key: 'some value'}
2836 self.assertRaises(KeyError, self.logger.makeRecord, name, level,
2837 fn, lno, msg, args, exc_info,
2838 extra=extra, sinfo=sinfo)
2839
2840 def test_make_record_with_extra_no_overwrite(self):
2841 name = 'my record'
2842 level = 13
2843 fn = lno = msg = args = exc_info = func = sinfo = None
2844 extra = {'valid_key': 'some value'}
2845 result = self.logger.makeRecord(name, level, fn, lno, msg, args,
2846 exc_info, extra=extra, sinfo=sinfo)
2847 self.assertIn('valid_key', result.__dict__)
2848
2849 def test_has_handlers(self):
2850 self.assertTrue(self.logger.hasHandlers())
2851
2852 for handler in self.logger.handlers:
2853 self.logger.removeHandler(handler)
2854 assert not self.logger.hasHandlers()
2855
2856 self.assertFalse(self.logger.hasHandlers())
2857
2858 def test_has_handlers_no_propagate(self):
2859 child_logger = logging.getLogger('blah.child')
2860 child_logger.propagate = False
2861 assert child_logger.handlers == []
2862
2863 self.assertFalse(child_logger.hasHandlers())
2864
2865 def test_is_enabled_for(self):
2866 old_disable = self.logger.manager.disable
2867 self.logger.manager.disable = 23
2868 self.addCleanup(lambda: setattr(self.logger.manager,
2869 'disable', old_disable))
2870 self.assertFalse(self.logger.isEnabledFor(22))
2871
2872
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002873class BaseFileTest(BaseTest):
2874 "Base class for handler tests that write log files"
2875
2876 def setUp(self):
2877 BaseTest.setUp(self)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002878 fd, self.fn = tempfile.mkstemp(".log", "test_logging-2-")
2879 os.close(fd)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002880 self.rmfiles = []
2881
2882 def tearDown(self):
2883 for fn in self.rmfiles:
2884 os.unlink(fn)
Vinay Sajip60b4df12010-12-27 11:18:52 +00002885 if os.path.exists(self.fn):
2886 os.unlink(self.fn)
Hirokazu Yamamoto2cdacd72010-09-18 03:54:32 +00002887 BaseTest.tearDown(self)
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002888
2889 def assertLogFile(self, filename):
2890 "Assert a log file is there and register it for deletion"
2891 self.assertTrue(os.path.exists(filename),
2892 msg="Log file %r does not exist")
2893 self.rmfiles.append(filename)
2894
2895
Vinay Sajip26fe4b72011-04-26 18:43:05 +01002896class FileHandlerTest(BaseFileTest):
2897 def test_delay(self):
2898 os.unlink(self.fn)
2899 fh = logging.FileHandler(self.fn, delay=True)
2900 self.assertIsNone(fh.stream)
2901 self.assertFalse(os.path.exists(self.fn))
2902 fh.handle(logging.makeLogRecord({}))
2903 self.assertIsNotNone(fh.stream)
2904 self.assertTrue(os.path.exists(self.fn))
2905 fh.close()
2906
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002907class RotatingFileHandlerTest(BaseFileTest):
2908 def next_rec(self):
2909 return logging.LogRecord('n', logging.DEBUG, 'p', 1,
2910 self.next_message(), None, None, None)
2911
2912 def test_should_not_rollover(self):
2913 # If maxbytes is zero rollover never occurs
2914 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=0)
2915 self.assertFalse(rh.shouldRollover(None))
Vinay Sajipb046b802010-10-30 09:50:18 +00002916 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002917
2918 def test_should_rollover(self):
2919 rh = logging.handlers.RotatingFileHandler(self.fn, maxBytes=1)
2920 self.assertTrue(rh.shouldRollover(self.next_rec()))
Vinay Sajipb046b802010-10-30 09:50:18 +00002921 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002922
2923 def test_file_created(self):
2924 # checks that the file is created and assumes it was created
2925 # by us
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002926 rh = logging.handlers.RotatingFileHandler(self.fn)
2927 rh.emit(self.next_rec())
2928 self.assertLogFile(self.fn)
Vinay Sajipb046b802010-10-30 09:50:18 +00002929 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002930
2931 def test_rollover_filenames(self):
2932 rh = logging.handlers.RotatingFileHandler(
2933 self.fn, backupCount=2, maxBytes=1)
2934 rh.emit(self.next_rec())
2935 self.assertLogFile(self.fn)
2936 rh.emit(self.next_rec())
2937 self.assertLogFile(self.fn + ".1")
2938 rh.emit(self.next_rec())
2939 self.assertLogFile(self.fn + ".2")
2940 self.assertFalse(os.path.exists(self.fn + ".3"))
Vinay Sajipb046b802010-10-30 09:50:18 +00002941 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002942
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002943class TimedRotatingFileHandlerTest(BaseFileTest):
2944 # test methods added below
2945 pass
2946
2947def secs(**kw):
2948 return datetime.timedelta(**kw) // datetime.timedelta(seconds=1)
2949
2950for when, exp in (('S', 1),
2951 ('M', 60),
2952 ('H', 60 * 60),
2953 ('D', 60 * 60 * 24),
Vinay Sajiped0473c2011-02-26 15:35:38 +00002954 ('MIDNIGHT', 60 * 60 * 24),
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002955 # current time (epoch start) is a Thursday, W0 means Monday
Vinay Sajiped0473c2011-02-26 15:35:38 +00002956 ('W0', secs(days=4, hours=24)),
2957 ):
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002958 def test_compute_rollover(self, when=when, exp=exp):
2959 rh = logging.handlers.TimedRotatingFileHandler(
Vinay Sajiped0473c2011-02-26 15:35:38 +00002960 self.fn, when=when, interval=1, backupCount=0, utc=True)
2961 currentTime = 0.0
2962 actual = rh.computeRollover(currentTime)
2963 if exp != actual:
2964 # Failures occur on some systems for MIDNIGHT and W0.
2965 # Print detailed calculation for MIDNIGHT so we can try to see
2966 # what's going on
2967 import time
2968 if when == 'MIDNIGHT':
2969 try:
2970 if rh.utc:
2971 t = time.gmtime(currentTime)
2972 else:
2973 t = time.localtime(currentTime)
2974 currentHour = t[3]
2975 currentMinute = t[4]
2976 currentSecond = t[5]
2977 # r is the number of seconds left between now and midnight
2978 r = logging.handlers._MIDNIGHT - ((currentHour * 60 +
2979 currentMinute) * 60 +
2980 currentSecond)
2981 result = currentTime + r
2982 print('t: %s (%s)' % (t, rh.utc), file=sys.stderr)
2983 print('currentHour: %s' % currentHour, file=sys.stderr)
2984 print('currentMinute: %s' % currentMinute, file=sys.stderr)
2985 print('currentSecond: %s' % currentSecond, file=sys.stderr)
2986 print('r: %s' % r, file=sys.stderr)
2987 print('result: %s' % result, file=sys.stderr)
2988 except Exception:
2989 print('exception in diagnostic code: %s' % sys.exc_info()[1], file=sys.stderr)
2990 self.assertEqual(exp, actual)
Vinay Sajipb046b802010-10-30 09:50:18 +00002991 rh.close()
Vinay Sajip19ec67a2010-09-17 18:57:36 +00002992 setattr(TimedRotatingFileHandlerTest, "test_compute_rollover_%s" % when, test_compute_rollover)
2993
Christian Heimes180510d2008-03-03 19:15:45 +00002994# Set the locale to the platform-dependent default. I have no idea
2995# why the test does this, but in any case we save the current locale
2996# first and restore it at the end.
2997@run_with_locale('LC_ALL', '')
Tim Peters36f7e932003-07-23 00:05:07 +00002998def test_main():
Christian Heimes180510d2008-03-03 19:15:45 +00002999 run_unittest(BuiltinLevelsTest, BasicFilterTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003000 CustomLevelsAndFiltersTest, HandlerTest, MemoryHandlerTest,
Vinay Sajipdb81c4c2010-02-25 23:13:06 +00003001 ConfigFileTest, SocketHandlerTest, MemoryTest,
Benjamin Peterson22005fc2010-04-11 16:25:06 +00003002 EncodingTest, WarningsTest, ConfigDictTest, ManagerTest,
Vinay Sajip985ef872011-04-26 19:34:04 +01003003 FormatterTest, BufferingFormatterTest, StreamHandlerTest,
Vinay Sajip61561522010-12-03 11:50:38 +00003004 LogRecordFactoryTest, ChildLoggerTest, QueueHandlerTest,
Vinay Sajipe6c1eb92011-03-29 17:20:34 +01003005 ShutdownTest, ModuleLevelMiscTest, BasicConfigTest,
3006 LoggerAdapterTest, LoggerTest,
Vinay Sajip26fe4b72011-04-26 18:43:05 +01003007 FileHandlerTest, RotatingFileHandlerTest,
3008 LastResortTest, LogRecordTest, ExceptionTest,
Vinay Sajip89807a52011-02-26 16:06:02 +00003009 TimedRotatingFileHandlerTest
Vinay Sajipbc85d842010-09-17 23:35:29 +00003010 )
Jeremy Hylton096d9862003-07-18 03:19:20 +00003011
Christian Heimes180510d2008-03-03 19:15:45 +00003012if __name__ == "__main__":
Neal Norwitzb4a2df02003-01-02 14:56:39 +00003013 test_main()